From 55e4eb0f70bb66ccf14e17100ea44f833d6873df Mon Sep 17 00:00:00 2001 From: Eugene Date: Tue, 25 Jul 2023 16:59:49 +0300 Subject: [PATCH] Added eslint & prettier config Formatted files in project --- .eslintrc.cjs | 26 ++ .prettierignore | 2 + .prettierrc.json | 1 + README.md | 32 +- index.html | 2 +- package.json | 9 +- pnpm-lock.yaml | 813 +++++++++++++++++++++++++++++++++++++ src/components/editor.ts | 122 +++--- src/components/header.ts | 20 +- src/components/scroller.ts | 380 +++++++++-------- src/components/sheet.ts | 112 +++-- src/components/table.ts | 34 +- src/components/toolbar.ts | 20 +- src/index.ts | 47 ++- src/main.ts | 576 +++++++++++++------------- src/modules/cache.ts | 90 ++-- src/modules/cell.ts | 216 +++++----- src/modules/column.ts | 20 +- src/modules/config.ts | 59 ++- src/modules/renderBox.ts | 49 ++- src/modules/row.ts | 22 +- src/modules/selection.ts | 18 +- src/modules/styles.ts | 11 +- src/modules/viewport.ts | 121 +++--- src/scss/_global.scss | 4 +- src/scss/_spreadsheet.scss | 42 +- src/scss/main.scss | 4 +- src/utils/createData.ts | 116 +++--- vite.config.ts | 68 ++-- 29 files changed, 1978 insertions(+), 1058 deletions(-) create mode 100644 .eslintrc.cjs create mode 100644 .prettierignore create mode 100644 .prettierrc.json diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..3878678 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,26 @@ +module.exports = { + env: { + browser: true, + es2021: true, + node: true, + }, + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], + overrides: [ + { + env: { + node: true, + }, + files: [".eslintrc.{js,cjs}"], + parserOptions: { + sourceType: "script", + }, + }, + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + plugins: ["@typescript-eslint"], + rules: {}, +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..780b455 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# Ignore artifacts: +dist \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/README.md b/README.md index 20c1a61..ce5621e 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,37 @@ # Modern Spreadsheet + - High performance spreadsheet based on CanvasAPI. - TypeScript supported ## Basic usage -```ts -import Spreadsheet from 'modern_spreadsheet' -import 'modern_spreadsheet/style.css' // <= this is required -const target = document.getElementById('spreadsheet') -const sheet = new Spreadsheet(target) +```ts +import Spreadsheet from "modern_spreadsheet"; +import "modern_spreadsheet/style.css"; // <= this is required + +const target = document.getElementById("spreadsheet"); +const sheet = new Spreadsheet(target); //... ``` ## Save and load data + ```ts -function saveData() { - const serialized = sheet.serializeData() - localStorage.setItem('sheet_data', JSON.stringify(serialized)) +function saveData() { + const serialized = sheet.serializeData(); + localStorage.setItem("sheet_data", JSON.stringify(serialized)); } -function loadData() { - const data = localStorage.getItem('sheet_data') - const json = JSON.parse(data) - if(!json) return; - sheet.loadData(json) +function loadData() { + const data = localStorage.getItem("sheet_data"); + const json = JSON.parse(data); + if (!json) return; + sheet.loadData(json); } ``` ## Roadmap + - Custom event functions (ex.: onSelectionChange, onCellEdit...). Full list of supported events will available on this page - Rows number and columns heading render - Rows and columns resizing @@ -37,4 +41,4 @@ function loadData() { - Selected cell depends cells highlight - Async formulas support - Mutlisheets (?) -- +- diff --git a/index.html b/index.html index 2a0e6ef..6227333 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + diff --git a/package.json b/package.json index e40c8a3..320916a 100644 --- a/package.json +++ b/package.json @@ -36,12 +36,19 @@ "build:watch": "tsc && vite build --watch", "preview": "vite preview", "predeploy": "npm run dist", - "deploy": "gh-pages -d dist" + "deploy": "gh-pages -d dist", + "lint": "eslint src --ext .ts", + "lint:fix": "eslint src --ext .ts --fix" }, "devDependencies": { "@rollup/plugin-typescript": "^11.1.2", "@types/node": "^20.4.4", + "@typescript-eslint/eslint-plugin": "^6.2.0", + "@typescript-eslint/parser": "^6.2.0", + "eslint": "^8.45.0", + "eslint-config-prettier": "^8.8.0", "gh-pages": "^5.0.0", + "prettier": "3.0.0", "rollup-plugin-typescript-paths": "^1.4.0", "sass": "^1.63.6", "tslib": "^2.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d16d53f..c00d484 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,24 @@ devDependencies: '@types/node': specifier: ^20.4.4 version: 20.4.4 + '@typescript-eslint/eslint-plugin': + specifier: ^6.2.0 + version: 6.2.0(@typescript-eslint/parser@6.2.0)(eslint@8.45.0)(typescript@5.0.2) + '@typescript-eslint/parser': + specifier: ^6.2.0 + version: 6.2.0(eslint@8.45.0)(typescript@5.0.2) + eslint: + specifier: ^8.45.0 + version: 8.45.0 + eslint-config-prettier: + specifier: ^8.8.0 + version: 8.8.0(eslint@8.45.0) gh-pages: specifier: ^5.0.0 version: 5.0.0 + prettier: + specifier: 3.0.0 + version: 3.0.0 rollup-plugin-typescript-paths: specifier: ^1.4.0 version: 1.4.0(typescript@5.0.2) @@ -32,6 +47,11 @@ devDependencies: packages: + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + /@esbuild/android-arm64@0.18.14: resolution: {integrity: sha512-rZ2v+Luba5/3D6l8kofWgTnqE+qsC/L5MleKIKFyllHTKHrNBMqeRCnZI1BtRx8B24xMYxeU32iIddRQqMsOsg==} engines: {node: '>=12'} @@ -230,6 +250,84 @@ packages: dev: true optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.45.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.45.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /@eslint-community/regexpp@4.6.1: + resolution: {integrity: sha512-O7x6dMstWLn2ktjcoiNLDkAGG2EjveHL+Vvc+n0fXumkJYAcSqcVYKtwDU+hDZ0uDUsnUagSYaZrOLAYE8un1A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.0: + resolution: {integrity: sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.20.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.44.0: + resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.10: + resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + /@rollup/plugin-typescript@11.1.2(tslib@2.6.0)(typescript@5.0.2): resolution: {integrity: sha512-0ghSOCMcA7fl1JM+0gYRf+Q/HWyg+zg7/gDSc+fRLmlJWcW5K1I+CLRzaRhXf4Y3DRyPnnDo4M2ktw+a6JcDEg==} engines: {node: '>=14.0.0'} @@ -267,10 +365,185 @@ packages: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} dev: true + /@types/json-schema@7.0.12: + resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} + dev: true + /@types/node@20.4.4: resolution: {integrity: sha512-CukZhumInROvLq3+b5gLev+vgpsIqC2D0deQr/yS1WnxvmYLlJXZpaQrQiseMY+6xusl79E04UjWoqyr+t1/Ew==} dev: true + /@types/semver@7.5.0: + resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} + dev: true + + /@typescript-eslint/eslint-plugin@6.2.0(@typescript-eslint/parser@6.2.0)(eslint@8.45.0)(typescript@5.0.2): + resolution: {integrity: sha512-rClGrMuyS/3j0ETa1Ui7s6GkLhfZGKZL3ZrChLeAiACBE/tRc1wq8SNZESUuluxhLj9FkUefRs2l6bCIArWBiQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.6.1 + '@typescript-eslint/parser': 6.2.0(eslint@8.45.0)(typescript@5.0.2) + '@typescript-eslint/scope-manager': 6.2.0 + '@typescript-eslint/type-utils': 6.2.0(eslint@8.45.0)(typescript@5.0.2) + '@typescript-eslint/utils': 6.2.0(eslint@8.45.0)(typescript@5.0.2) + '@typescript-eslint/visitor-keys': 6.2.0 + debug: 4.3.4 + eslint: 8.45.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + natural-compare-lite: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.1(typescript@5.0.2) + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.2.0(eslint@8.45.0)(typescript@5.0.2): + resolution: {integrity: sha512-igVYOqtiK/UsvKAmmloQAruAdUHihsOCvplJpplPZ+3h4aDkC/UKZZNKgB6h93ayuYLuEymU3h8nF1xMRbh37g==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.2.0 + '@typescript-eslint/types': 6.2.0 + '@typescript-eslint/typescript-estree': 6.2.0(typescript@5.0.2) + '@typescript-eslint/visitor-keys': 6.2.0 + debug: 4.3.4 + eslint: 8.45.0 + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.2.0: + resolution: {integrity: sha512-1ZMNVgm5nnHURU8ZSJ3snsHzpFeNK84rdZjluEVBGNu7jDymfqceB3kdIZ6A4xCfEFFhRIB6rF8q/JIqJd2R0Q==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.2.0 + '@typescript-eslint/visitor-keys': 6.2.0 + dev: true + + /@typescript-eslint/type-utils@6.2.0(eslint@8.45.0)(typescript@5.0.2): + resolution: {integrity: sha512-DnGZuNU2JN3AYwddYIqrVkYW0uUQdv0AY+kz2M25euVNlujcN2u+rJgfJsBFlUEzBB6OQkUqSZPyuTLf2bP5mw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.2.0(typescript@5.0.2) + '@typescript-eslint/utils': 6.2.0(eslint@8.45.0)(typescript@5.0.2) + debug: 4.3.4 + eslint: 8.45.0 + ts-api-utils: 1.0.1(typescript@5.0.2) + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.2.0: + resolution: {integrity: sha512-1nRRaDlp/XYJQLvkQJG5F3uBTno5SHPT7XVcJ5n1/k2WfNI28nJsvLakxwZRNY5spuatEKO7d5nZWsQpkqXwBA==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.2.0(typescript@5.0.2): + resolution: {integrity: sha512-Mts6+3HQMSM+LZCglsc2yMIny37IhUgp1Qe8yJUYVyO6rHP7/vN0vajKu3JvHCBIy8TSiKddJ/Zwu80jhnGj1w==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.2.0 + '@typescript-eslint/visitor-keys': 6.2.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.1(typescript@5.0.2) + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.2.0(eslint@8.45.0)(typescript@5.0.2): + resolution: {integrity: sha512-RCFrC1lXiX1qEZN8LmLrxYRhOkElEsPKTVSNout8DMzf8PeWoQG7Rxz2SadpJa3VSh5oYKGwt7j7X/VRg+Y3OQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) + '@types/json-schema': 7.0.12 + '@types/semver': 7.5.0 + '@typescript-eslint/scope-manager': 6.2.0 + '@typescript-eslint/types': 6.2.0 + '@typescript-eslint/typescript-estree': 6.2.0(typescript@5.0.2) + eslint: 8.45.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.2.0: + resolution: {integrity: sha512-QbaYUQVKKo9bgCzpjz45llCfwakyoxHetIy8CAvYCtd16Zu1KrpzNHofwF8kGkpPOxZB2o6kz+0nqH8ZkIzuoQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.2.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /acorn-jsx@5.3.2(acorn@8.10.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -279,6 +552,10 @@ packages: picomatch: 2.3.1 dev: true + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + /array-union@1.0.2: resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} engines: {node: '>=0.10.0'} @@ -286,6 +563,11 @@ packages: array-uniq: 1.0.3 dev: true + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + /array-uniq@1.0.3: resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} engines: {node: '>=0.10.0'} @@ -318,6 +600,19 @@ packages: fill-range: 7.0.1 dev: true + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -333,6 +628,17 @@ packages: fsevents: 2.3.2 dev: true + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true @@ -345,6 +651,45 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + /email-addresses@5.0.0: resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} dev: true @@ -384,10 +729,152 @@ packages: engines: {node: '>=0.8.0'} dev: true + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-prettier@8.8.0(eslint@8.45.0): + resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.45.0 + dev: true + + /eslint-scope@7.2.1: + resolution: {integrity: sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.1: + resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.45.0: + resolution: {integrity: sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) + '@eslint-community/regexpp': 4.6.1 + '@eslint/eslintrc': 2.1.0 + '@eslint/js': 8.44.0 + '@humanwhocodes/config-array': 0.11.10 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.1 + eslint-visitor-keys: 3.4.1 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.20.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) + eslint-visitor-keys: 3.4.1 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} dev: true + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + /filename-reserved-regex@2.0.0: resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==} engines: {node: '>=4'} @@ -426,6 +913,26 @@ packages: path-exists: 4.0.0 dev: true + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + /fs-extra@8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} @@ -472,6 +979,13 @@ packages: is-glob: 4.0.3 dev: true + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -483,6 +997,25 @@ packages: path-is-absolute: 1.0.1 dev: true + /globals@13.20.0: + resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + /globby@6.1.0: resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==} engines: {node: '>=0.10.0'} @@ -498,6 +1031,15 @@ packages: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -505,10 +1047,28 @@ packages: function-bind: 1.1.1 dev: true + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + /immutable@4.3.1: resolution: {integrity: sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==} dev: true + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: @@ -550,12 +1110,44 @@ packages: engines: {node: '>=0.12.0'} dev: true + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: graceful-fs: 4.2.11 dev: true + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -563,6 +1155,24 @@ packages: p-locate: 4.1.0 dev: true + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -570,18 +1180,43 @@ packages: semver: 6.3.1 dev: true + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: true + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + /nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -598,6 +1233,18 @@ packages: wrappy: 1.0.2 dev: true + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + /p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -605,6 +1252,13 @@ packages: p-try: 2.2.0 dev: true + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -612,11 +1266,25 @@ packages: p-limit: 2.3.0 dev: true + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} dev: true + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -627,10 +1295,20 @@ packages: engines: {node: '>=0.10.0'} dev: true + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -673,6 +1351,26 @@ packages: source-map-js: 1.0.2 dev: true + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier@3.0.0: + resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -680,6 +1378,11 @@ packages: picomatch: 2.3.1 dev: true + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + /resolve@1.22.2: resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} hasBin: true @@ -689,6 +1392,18 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + /rollup-plugin-typescript-paths@1.4.0(typescript@5.0.2): resolution: {integrity: sha512-6EgeLRjTVmymftEyCuYu91XzY5XMB5lR0YrJkeT0D7OG2RGSdbNL+C/hfPIdc/sjMa9Sl5NLsxIr6C/+/5EUpA==} peerDependencies: @@ -705,6 +1420,12 @@ packages: fsevents: 2.3.2 dev: true + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + /sass@1.63.6: resolution: {integrity: sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==} engines: {node: '>=14.0.0'} @@ -720,11 +1441,48 @@ packages: hasBin: true dev: true + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} dev: true + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + /strip-outer@1.0.1: resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} engines: {node: '>=0.10.0'} @@ -732,11 +1490,22 @@ packages: escape-string-regexp: 1.0.5 dev: true + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -751,10 +1520,31 @@ packages: escape-string-regexp: 1.0.5 dev: true + /ts-api-utils@1.0.1(typescript@5.0.2): + resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.0.2 + dev: true + /tslib@2.6.0: resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} dev: true + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + /typescript@5.0.2: resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==} engines: {node: '>=12.20'} @@ -766,6 +1556,12 @@ packages: engines: {node: '>= 4.0.0'} dev: true + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + /vite@4.4.0(@types/node@20.4.4)(sass@1.63.6): resolution: {integrity: sha512-Wf+DCEjuM8aGavEYiF77hnbxEZ+0+/jC9nABR46sh5Xi+GYeSvkeEFRiVuI3x+tPjxgZeS91h1jTAQTPFgePpA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -803,6 +1599,23 @@ packages: fsevents: 2.3.2 dev: true + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/src/components/editor.ts b/src/components/editor.ts index e0a5bbf..e765b3d 100644 --- a/src/components/editor.ts +++ b/src/components/editor.ts @@ -3,68 +3,66 @@ import { Position } from "../modules/cell"; import { RenderBox } from "../modules/renderBox"; export class Editor { - element: HTMLInputElement - root: Spreadsheet - constructor(root: Spreadsheet) { - this.root = root - const element = document.createElement('input') - element.classList.add(CSS_PREFIX + 'editor') - this.element = element - this.hide() + element: HTMLInputElement; + root: Spreadsheet; + constructor(root: Spreadsheet) { + this.root = root; + const element = document.createElement("input"); + element.classList.add(CSS_PREFIX + "editor"); + this.element = element; + this.hide(); + } + + hide() { + this.element.style.display = "none"; + this.element.classList.add("hide"); + this.element.blur(); + window.removeEventListener("click", this.handleClickOutside); + this.element.removeEventListener("keydown", this.handleKeydown); + + this.root.focusTable(); + } + + show(position: Position, initialString?: string) { + const { height, width, x, y } = new RenderBox(this.root.config, position); + const cell = this.root.getCell(position); + this.element.classList.remove("hide"); + + this.element.style.top = y - this.root.viewport.top + "px"; + this.element.style.left = x - this.root.viewport.left + "px"; + this.element.style.width = width + "px"; + this.element.style.height = height + "px"; + this.element.style.display = "block"; + + window.addEventListener("click", this.handleClickOutside); + this.element.addEventListener("keydown", this.handleKeydown); + this.element.value = initialString ? initialString : cell.value; + this.element.focus(); + if (!initialString) this.element.select(); + } + + handleKeydown = (event: KeyboardEvent) => { + const { key } = event; + + switch (key) { + case "Escape": { + this.hide(); + break; + } + case "Enter": { + this.root.changeCellValues(this.root.selection.selectedCell!, { + value: this.element.value, + displayValue: this.element.value, + }); + this.hide(); + } } + }; - hide() { - this.element.style.display = 'none' - this.element.classList.add('hide') - this.element.blur() - window.removeEventListener('click', this.handleClickOutside) - this.element.removeEventListener('keydown', this.handleKeydown) - - this.root.focusTable() + handleClickOutside = (event: MouseEvent) => { + const target = event.target as HTMLElement; + if (!this.element.contains(target)) { + this.hide(); } - - show(position: Position, initialString?: string) { - const { height, width, x, y } = new RenderBox(this.root.config, position); - const cell = this.root.getCell(position) - this.element.classList.remove('hide') - - this.element.style.top = (y - this.root.viewport.top) + 'px' - this.element.style.left = (x - this.root.viewport.left) + 'px' - this.element.style.width = width + 'px' - this.element.style.height = height + 'px' - this.element.style.display = 'block' - - window.addEventListener('click', this.handleClickOutside) - this.element.addEventListener('keydown', this.handleKeydown) - this.element.value = initialString ? initialString : cell.value - this.element.focus() - if (!initialString) this.element.select() - - - } - - handleKeydown = (event: KeyboardEvent) => { - const { key } = event - - switch (key) { - case 'Escape': { - this.hide(); - break; - } - case 'Enter': { - this.root.changeCellValues(this.root.selection.selectedCell!, { - value: this.element.value, - displayValue: this.element.value - }) - this.hide(); - } - } - } - - handleClickOutside = (event: MouseEvent) => { - const target = event.target as HTMLElement - if (!this.element.contains(target)) { - this.hide() - } - } -} \ No newline at end of file + }; +} diff --git a/src/components/header.ts b/src/components/header.ts index fb4f19b..71afc33 100644 --- a/src/components/header.ts +++ b/src/components/header.ts @@ -1,12 +1,12 @@ -import Spreadsheet from "../main" +import Spreadsheet from "../main"; export class Header { - element: HTMLHeadElement - root: Spreadsheet - constructor(root: Spreadsheet) { - this.root = root - const headerElement = document.createElement('header') - headerElement.classList.add() - this.element = headerElement - } -} \ No newline at end of file + element: HTMLHeadElement; + root: Spreadsheet; + constructor(root: Spreadsheet) { + this.root = root; + const headerElement = document.createElement("header"); + headerElement.classList.add(); + this.element = headerElement; + } +} diff --git a/src/components/scroller.ts b/src/components/scroller.ts index 60aa2ab..446c4be 100644 --- a/src/components/scroller.ts +++ b/src/components/scroller.ts @@ -1,218 +1,242 @@ -import Spreadsheet, { CSS_PREFIX } from "../main" +import Spreadsheet, { CSS_PREFIX } from "../main"; export interface ViewportRect { - top: number - left: number - right: number - bottom: number + top: number; + left: number; + right: number; + bottom: number; } export class Scroller { - element: HTMLDivElement - private verticalScroller: HTMLDivElement - private horizontalScroller: HTMLDivElement - private root: Spreadsheet + element: HTMLDivElement; + private verticalScroller: HTMLDivElement; + private horizontalScroller: HTMLDivElement; + private root: Spreadsheet; - private isSelecting = false + private isSelecting = false; - constructor(root: Spreadsheet) { - this.root = root - const { horizontalScroller, scroller, verticalScroller } = this.buildComponent() - this.element = scroller - this.verticalScroller = verticalScroller - this.horizontalScroller = horizontalScroller + constructor(root: Spreadsheet) { + this.root = root; + const { horizontalScroller, scroller, verticalScroller } = + this.buildComponent(); + this.element = scroller; + this.verticalScroller = verticalScroller; + this.horizontalScroller = horizontalScroller; - this.element.style.height = this.root.config.view.height + 'px' - this.element.style.width = this.root.config.view.width + 'px' - this.element.tabIndex = -1 + this.element.style.height = this.root.config.view.height + "px"; + this.element.style.width = this.root.config.view.width + "px"; + this.element.tabIndex = -1; - this.updateScrollerSize() //* Init size set + this.updateScrollerSize(); //* Init size set - this.element.addEventListener('scroll', this.handleScroll) + this.element.addEventListener("scroll", this.handleScroll); - this.element.addEventListener('mousedown', this.handleClick) - this.element.addEventListener('mousemove', this.handleMouseMove) - this.element.addEventListener('mouseup', this.handleMouseUp) - this.element.addEventListener('dblclick', this.handleDoubleClick) + this.element.addEventListener("mousedown", this.handleClick); + this.element.addEventListener("mousemove", this.handleMouseMove); + this.element.addEventListener("mouseup", this.handleMouseUp); + this.element.addEventListener("dblclick", this.handleDoubleClick); - this.element.addEventListener('keydown', this.handleKeydown) + this.element.addEventListener("keydown", this.handleKeydown); + } + + private handleMouseMove = (event: MouseEvent) => { + if (!this.isSelecting) return; + const { offsetX, offsetY } = event; + const lastSelectedCell = this.root.getCellByCoords(offsetX, offsetY); + if (this.root.selection.selectedRange) { + this.root.selection.selectedRange.to = lastSelectedCell; + } + this.root.renderSheet(); + }; + + private handleMouseUp = () => { + this.isSelecting = false; + + if (this.root.selection.selectedRange) { + if ( + this.root.selection.selectedRange.from.row === + this.root.selection.selectedRange.to.row && + this.root.selection.selectedRange.from.column === + this.root.selection.selectedRange.to.column + ) { + this.root.selection.selectedRange = null; + } } - private handleMouseMove = (event: MouseEvent) => { - if (!this.isSelecting) return; - const { offsetX, offsetY } = event - const lastSelectedCell = this.root.getCellByCoords(offsetX, offsetY) - if (this.root.selection.selectedRange) { - this.root.selection.selectedRange.to = lastSelectedCell + this.root.renderSheet(); + }; + + private handleDoubleClick = (event: MouseEvent) => { + event.preventDefault(); + const position = this.root.getCellByCoords(event.offsetX, event.offsetY); + this.root.showEditor(position); + }; + + private handleKeydown = (event: KeyboardEvent) => { + console.log(event); + //* Navigation + if ( + ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key) + ) { + event.preventDefault(); + this.root.selection.selectedRange = null; + switch (event.key) { + case "ArrowLeft": { + if ( + this.root.selection.selectedCell && + this.root.selection.selectedCell.column > 0 + ) { + console.log("tick"); + this.root.selection.selectedCell.column -= 1; + this.root.renderSheet(); + } + break; } - this.root.renderSheet() - } - - private handleMouseUp = () => { - this.isSelecting = false - - if (this.root.selection.selectedRange) { - if ( - (this.root.selection.selectedRange.from.row === this.root.selection.selectedRange.to.row) && - (this.root.selection.selectedRange.from.column === this.root.selection.selectedRange.to.column) - ) { - this.root.selection.selectedRange = null - } + case "ArrowRight": { + if ( + this.root.selection.selectedCell && + this.root.selection.selectedCell.column < + this.root.config.columns.length - 1 + ) { + this.root.selection.selectedCell.column += 1; + this.root.renderSheet(); + } + break; } - - this.root.renderSheet() + case "ArrowUp": { + if ( + this.root.selection.selectedCell && + this.root.selection.selectedCell.row > 0 + ) { + this.root.selection.selectedCell.row -= 1; + this.root.renderSheet(); + } + break; + } + case "ArrowDown": { + if ( + this.root.selection.selectedCell && + this.root.selection.selectedCell.row < + this.root.config.rows.length - 1 + ) { + this.root.selection.selectedCell.row += 1; + this.root.renderSheet(); + } + break; + } + } } + const keysRegex = /^([a-z]|[а-я])$/; + if (!event.metaKey && !event.ctrlKey) { + //* Prevent handle shortcutrs + const isPressedLetterKey = keysRegex.test(event.key.toLowerCase()); - private handleDoubleClick = (event: MouseEvent) => { + if (event.key === "F2" || isPressedLetterKey) { + //* English and Russian keyboard. Or F2 button event.preventDefault(); - const position = this.root.getCellByCoords(event.offsetX, event.offsetY) - this.root.showEditor(position) + if (!this.root.selection.selectedCell) return; + + this.root.showEditor( + this.root.selection.selectedCell, + isPressedLetterKey ? event.key : undefined, + ); + } } - private handleKeydown = (event: KeyboardEvent) => { - console.log(event) - //* Navigation - if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key)) { - event.preventDefault() - this.root.selection.selectedRange = null - switch (event.key) { - case 'ArrowLeft': { - if (this.root.selection.selectedCell && this.root.selection.selectedCell.column > 0) { - console.log('tick') - this.root.selection.selectedCell.column -= 1 - this.root.renderSheet() - } - break; - } - case 'ArrowRight': { - if (this.root.selection.selectedCell && this.root.selection.selectedCell.column < this.root.config.columns.length - 1) { - this.root.selection.selectedCell.column += 1 - this.root.renderSheet() - } - break; - } - case 'ArrowUp': { - if (this.root.selection.selectedCell && this.root.selection.selectedCell.row > 0) { - this.root.selection.selectedCell.row -= 1 - this.root.renderSheet() - } - break; - } - case 'ArrowDown': { - if (this.root.selection.selectedCell && this.root.selection.selectedCell.row < this.root.config.rows.length - 1) { - this.root.selection.selectedCell.row += 1 - this.root.renderSheet() - } - break; - } - } - } - const keysRegex = /^([a-z]|[а-я])$/ - if (!event.metaKey && !event.ctrlKey) { //* Prevent handle shortcutrs - const isPressedLetterKey = keysRegex.test(event.key.toLowerCase()) - - if (event.key === 'F2' || isPressedLetterKey) { //* English and Russian keyboard. Or F2 button - event.preventDefault() - if (!this.root.selection.selectedCell) return; - - this.root.showEditor(this.root.selection.selectedCell, isPressedLetterKey ? event.key : undefined) - } - } - - if (event.key === 'Delete') { - event.preventDefault() - this.root.deleteSelectedCellsValues() - this.root.renderSheet() - } + if (event.key === "Delete") { + event.preventDefault(); + this.root.deleteSelectedCellsValues(); + this.root.renderSheet(); } + }; - private handleClick = (event: MouseEvent) => { - if (event.button !== 0) return; // Left mouse button - const { offsetX, offsetY } = event - const clickedCell = this.root.getCellByCoords(offsetX, offsetY) - this.isSelecting = true - this.root.selection.selectedRange = { - from: clickedCell, - to: clickedCell - } - this.root.selection.selectedCell = clickedCell + private handleClick = (event: MouseEvent) => { + if (event.button !== 0) return; // Left mouse button + const { offsetX, offsetY } = event; + const clickedCell = this.root.getCellByCoords(offsetX, offsetY); + this.isSelecting = true; + this.root.selection.selectedRange = { + from: clickedCell, + to: clickedCell, + }; + this.root.selection.selectedCell = clickedCell; - this.root.renderSheet() - } + this.root.renderSheet(); + }; - private handleScroll = () => { - const rect = this.getViewportBoundlingRect() - this.root.viewport.updateValues(rect) + private handleScroll = () => { + const rect = this.getViewportBoundlingRect(); + this.root.viewport.updateValues(rect); - this.root.renderSheet() - } + this.root.renderSheet(); + }; - public getViewportBoundlingRect(): ViewportRect { - const { scrollTop, scrollLeft } = this.element - const { height, width } = this.element.getBoundingClientRect() - const bottom = scrollTop + height - const right = scrollLeft + width + public getViewportBoundlingRect(): ViewportRect { + const { scrollTop, scrollLeft } = this.element; + const { height, width } = this.element.getBoundingClientRect(); + const bottom = scrollTop + height; + const right = scrollLeft + width; - return { - top: scrollTop, - left: scrollLeft, - bottom, - right - } - } + return { + top: scrollTop, + left: scrollLeft, + bottom, + right, + }; + } - private buildComponent() { - const scroller = document.createElement('div') - const verticalScroller = document.createElement('div') - const horizontalScroller = document.createElement('div') - const groupScrollers = document.createElement('div') - const stack = document.createElement('div') + private buildComponent() { + const scroller = document.createElement("div"); + const verticalScroller = document.createElement("div"); + const horizontalScroller = document.createElement("div"); + const groupScrollers = document.createElement("div"); + const stack = document.createElement("div"); - verticalScroller.style.width = '0px' - verticalScroller.style.pointerEvents = 'none' + verticalScroller.style.width = "0px"; + verticalScroller.style.pointerEvents = "none"; - horizontalScroller.style.pointerEvents = 'none' + horizontalScroller.style.pointerEvents = "none"; - groupScrollers.style.display = 'flex' + groupScrollers.style.display = "flex"; - stack.appendChild(verticalScroller) - stack.appendChild(horizontalScroller) - groupScrollers.appendChild(stack) - this.verticalScroller = verticalScroller - this.horizontalScroller = horizontalScroller - scroller.appendChild(groupScrollers) - scroller.classList.add(CSS_PREFIX + 'scroller') + stack.appendChild(verticalScroller); + stack.appendChild(horizontalScroller); + groupScrollers.appendChild(stack); + this.verticalScroller = verticalScroller; + this.horizontalScroller = horizontalScroller; + scroller.appendChild(groupScrollers); + scroller.classList.add(CSS_PREFIX + "scroller"); - return { scroller, verticalScroller, horizontalScroller } - } + return { scroller, verticalScroller, horizontalScroller }; + } - private getActualHeight() { - return this.root.config.rows.reduce((acc, curr) => { - acc += curr.height - return acc - }, 0) - } + private getActualHeight() { + return this.root.config.rows.reduce((acc, curr) => { + acc += curr.height; + return acc; + }, 0); + } - private getActualWidth() { - return this.root.config.columns.reduce((acc, curr) => { - acc += curr.width - return acc - }, 0) - } + private getActualWidth() { + return this.root.config.columns.reduce((acc, curr) => { + acc += curr.width; + return acc; + }, 0); + } - updateScrollerSize() { - const totalHeight = this.getActualHeight() - const totalWidth = this.getActualWidth() + updateScrollerSize() { + const totalHeight = this.getActualHeight(); + const totalWidth = this.getActualWidth(); - this.setScrollerHeight(totalHeight) - this.setScrollerWidth(totalWidth) - } + this.setScrollerHeight(totalHeight); + this.setScrollerWidth(totalWidth); + } - private setScrollerHeight(height: number) { - this.verticalScroller.style.height = height + 'px' - } + private setScrollerHeight(height: number) { + this.verticalScroller.style.height = height + "px"; + } - private setScrollerWidth(width: number) { - this.horizontalScroller.style.width = width + 'px' - } -} \ No newline at end of file + private setScrollerWidth(width: number) { + this.horizontalScroller.style.width = width + "px"; + } +} diff --git a/src/components/sheet.ts b/src/components/sheet.ts index d64f524..a8280b4 100644 --- a/src/components/sheet.ts +++ b/src/components/sheet.ts @@ -1,71 +1,69 @@ -import Spreadsheet, { CSS_PREFIX } from "../main" -import { Position } from "../modules/cell" +import Spreadsheet, { CSS_PREFIX } from "../main"; +import { Position } from "../modules/cell"; /** * Display (CANVAS) element where cells render */ export class Sheet { - element: HTMLCanvasElement - ctx: CanvasRenderingContext2D - root: Spreadsheet - constructor(root: Spreadsheet) { - this.root = root - const canvas = document.createElement('canvas') - canvas.classList.add(CSS_PREFIX + 'sheet') + element: HTMLCanvasElement; + ctx: CanvasRenderingContext2D; + root: Spreadsheet; + constructor(root: Spreadsheet) { + this.root = root; + const canvas = document.createElement("canvas"); + canvas.classList.add(CSS_PREFIX + "sheet"); - //* Set up canvas sizes based on provided root config - canvas.height = this.root.config.view.height - canvas.width = this.root.config.view.width - canvas.style.width = this.root.config.view.width + 'px' - canvas.style.height = this.root.config.view.height + 'px' + //* Set up canvas sizes based on provided root config + canvas.height = this.root.config.view.height; + canvas.width = this.root.config.view.width; + canvas.style.width = this.root.config.view.width + "px"; + canvas.style.height = this.root.config.view.height + "px"; - this.element = canvas + this.element = canvas; - const ctx = this.element.getContext('2d') - if (!ctx) throw new Error('Enable hardware acceleration') - this.ctx = ctx + const ctx = this.element.getContext("2d"); + if (!ctx) throw new Error("Enable hardware acceleration"); + this.ctx = ctx; + } + getCellByCoords(x: number, y: number): Position { + let row = 0; + let height = 0; + while (height <= y) { + height += this.root.config.rows[row].height; + if (height >= y) break; + row++; } - getCellByCoords(x: number, y: number): Position { - let row = 0; - let height = 0 - while (height <= y) { - height += this.root.config.rows[row].height - if (height >= y) break; - row++; - } - - let col = 0; - let width = 0; - while (width <= x) { - width += this.root.config.columns[col].width - if (width >= x) break; - col++; - } - - return new Position(row, col) + let col = 0; + let width = 0; + while (width <= x) { + width += this.root.config.columns[col].width; + if (width >= x) break; + col++; } - renderCell(position: Position) { - const { column, row } = position - this.root.data[row][column].render(this.root) + return new Position(row, col); + } + + renderCell(position: Position) { + const { column, row } = position; + this.root.data[row][column].render(this.root); + } + + renderSheet() { + const firstRowIdx = this.root.viewport.firstRow; + const lastColIdx = this.root.viewport.lastCol + 3; + const lastRowIdx = this.root.viewport.lastRow + 3; + const firstColIdx = this.root.viewport.firstCol; + + for (let row = firstRowIdx; row <= lastRowIdx; row++) { + for (let col = firstColIdx; col <= lastColIdx; col++) { + if (!this.root.config.columns[col] || !this.root.config.rows[row]) + break; //* Prevent read undefined + + this.renderCell({ column: col, row }); + } } - - renderSheet() { - const firstRowIdx = this.root.viewport.firstRow - const lastColIdx = this.root.viewport.lastCol + 3 - const lastRowIdx = this.root.viewport.lastRow + 3 - const firstColIdx = this.root.viewport.firstCol - - for (let row = firstRowIdx; row <= lastRowIdx; row++) { - for (let col = firstColIdx; col <= lastColIdx; col++) { - if (!this.root.config.columns[col] || !this.root.config.rows[row]) break; //* Prevent read undefined - - this.renderCell({ column: col, row }) - } - } - - } - -} \ No newline at end of file + } +} diff --git a/src/components/table.ts b/src/components/table.ts index e4cf6d2..25d084e 100644 --- a/src/components/table.ts +++ b/src/components/table.ts @@ -1,22 +1,22 @@ -import Spreadsheet, { CSS_PREFIX } from "../main" -import { ViewProperties } from "../modules/config" +import Spreadsheet, { CSS_PREFIX } from "../main"; +import { ViewProperties } from "../modules/config"; /** Base (root) component */ export class Table { - element: HTMLDivElement - root: Spreadsheet - constructor(root: Spreadsheet) { - this.root = root - const container = document.createElement('div') - container.classList.add(CSS_PREFIX + 'spreadsheet_container') - this.element = container + element: HTMLDivElement; + root: Spreadsheet; + constructor(root: Spreadsheet) { + this.root = root; + const container = document.createElement("div"); + container.classList.add(CSS_PREFIX + "spreadsheet_container"); + this.element = container; - this.changeElementSizes(this.root.viewProps) - } + this.changeElementSizes(this.root.viewProps); + } - changeElementSizes(sizes: ViewProperties) { - const { height, width } = sizes - this.element.style.width = width + 'px' - this.element.style.height = height + 'px' - } -} \ No newline at end of file + changeElementSizes(sizes: ViewProperties) { + const { height, width } = sizes; + this.element.style.width = width + "px"; + this.element.style.height = height + "px"; + } +} diff --git a/src/components/toolbar.ts b/src/components/toolbar.ts index 8dfdef0..fef13a9 100644 --- a/src/components/toolbar.ts +++ b/src/components/toolbar.ts @@ -1,12 +1,12 @@ -import Spreadsheet, { CSS_PREFIX } from "../main" +import Spreadsheet, { CSS_PREFIX } from "../main"; export class Toolbar { - element: HTMLDivElement - root: Spreadsheet - constructor(root: Spreadsheet) { - this.root = root - const toolbarElement = document.createElement('div') - toolbarElement.classList.add(CSS_PREFIX + 'toolbar') - this.element = toolbarElement - } -} \ No newline at end of file + element: HTMLDivElement; + root: Spreadsheet; + constructor(root: Spreadsheet) { + this.root = root; + const toolbarElement = document.createElement("div"); + toolbarElement.classList.add(CSS_PREFIX + "toolbar"); + this.element = toolbarElement; + } +} diff --git a/src/index.ts b/src/index.ts index 72c5ca9..e95ea71 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,34 +1,37 @@ -import Spreadsheet from './main' +import Spreadsheet from "./main"; -const saveButton = document.querySelector('#save_button') -const loadButton = document.querySelector('#load_button') +const saveButton = document.querySelector("#save_button"); +const loadButton = document.querySelector("#load_button"); -if(!saveButton || !loadButton) throw new Error("LOST") +if (!saveButton || !loadButton) throw new Error("LOST"); -const sheet = new Spreadsheet('#spreadsheet') -const sheet2 = new Spreadsheet('#spreadsheet_2') +const sheet = new Spreadsheet("#spreadsheet"); +const sheet2 = new Spreadsheet("#spreadsheet_2"); -console.log(sheet2) +console.log(sheet2); function saveDataToLS() { - const serializableData = sheet.serializeData() - localStorage.setItem('sheet', JSON.stringify(serializableData)) + const serializableData = sheet.serializeData(); + localStorage.setItem("sheet", JSON.stringify(serializableData)); } function loadDataFromLS() { - const data = localStorage.getItem('sheet') - if(!data) return - const json = JSON.parse(data) - sheet.loadData(json) + const data = localStorage.getItem("sheet"); + if (!data) return; + const json = JSON.parse(data); + sheet.loadData(json); } -saveButton.addEventListener('click', saveDataToLS) -loadButton.addEventListener('click', loadDataFromLS) -sheet.changeCellStyles({column: 1, row: 1}, { - background: 'black', - borderColor: 'white', - fontColor: 'white', +saveButton.addEventListener("click", saveDataToLS); +loadButton.addEventListener("click", loadDataFromLS); +sheet.changeCellStyles( + { column: 1, row: 1 }, + { + background: "black", + borderColor: "white", + fontColor: "white", fontSize: 20, - selectedBackground: 'green', - selectedFontColor: 'black' -}) \ No newline at end of file + selectedBackground: "green", + selectedFontColor: "black", + }, +); diff --git a/src/main.ts b/src/main.ts index 16b34e1..a30f59f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,12 +4,18 @@ import { Scroller } from "./components/scroller"; import { Sheet } from "./components/sheet"; import { Table } from "./components/table"; import { Toolbar } from "./components/toolbar"; -import { Cell, CellConstructorProps, CellStyles, Position, SerializableCell } from "./modules/cell"; +import { + Cell, + CellConstructorProps, + CellStyles, + Position, + SerializableCell, +} from "./modules/cell"; import { Config, ViewProperties } from "./modules/config"; import { RangeSelectionType, Selection } from "./modules/selection"; import { Styles } from "./modules/styles"; import { Viewport } from "./modules/viewport"; -import './scss/main.scss' +import "./scss/main.scss"; import { createSampleData } from "./utils/createData"; import { Cache, CachedColumn, CachedRow } from "./modules/cache"; import { Row } from "./modules/row"; @@ -28,300 +34,322 @@ import { Column } from "./modules/column"; */ interface SpreadsheetConstructorProperties { - config?: Omit // Not optional. - view?: ViewProperties + config?: Omit; // Not optional. + view?: ViewProperties; } -export const CSS_PREFIX = "modern_sc_" +export const CSS_PREFIX = "modern_sc_"; export default class Spreadsheet { - private table: Table - private scroller: Scroller - private toolbar: Toolbar - private header: Header - private sheet: Sheet - private editor: Editor - public styles: Styles - public config: Config - public data: Cell[][] - public viewport: Viewport - public selection: Selection - public cache: Cache + private table: Table; + private scroller: Scroller; + private toolbar: Toolbar; + private header: Header; + private sheet: Sheet; + private editor: Editor; + public styles: Styles; + public config: Config; + public data: Cell[][]; + public viewport: Viewport; + public selection: Selection; + public cache: Cache; - constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties) { - const data = createSampleData(40, 40) - const config = this.makeConfigFromData(data, props?.view ?? { height: 600, width: 800 }) - if (props?.view) { - config.view = props.view - } - - this.config = new Config(config) - this.sheet = new Sheet(this) - - this.table = new Table(this) - this.scroller = new Scroller(this) - this.toolbar = new Toolbar(this) - this.header = new Header(this) - this.editor = new Editor(this) - this.cache = this.getInitialCache() - this.viewport = new Viewport(this, this.scroller.getViewportBoundlingRect()) - this.selection = new Selection() - - - this.data = data - this.styles = new Styles() - this.buildComponent() - this.appendTableToTarget(target) - this.renderSheet() + constructor( + target: string | HTMLElement, + props?: SpreadsheetConstructorProperties, + ) { + const data = createSampleData(40, 40); + const config = this.makeConfigFromData( + data, + props?.view ?? { height: 600, width: 800 }, + ); + if (props?.view) { + config.view = props.view; } - private getInitialCache(): Cache { - const cachedCols: CachedColumn[] = [] - let currentWidth = 0 - for (let i = 0; i <= this.config.columns.length - 1; i++) { - const col = this.config.columns[i] - currentWidth += col.width - const cacheCol = new CachedColumn({ - xPos: currentWidth, - colIdx: i - }) - cachedCols.push(cacheCol) - } + this.config = new Config(config); + this.sheet = new Sheet(this); - const cachedRows: CachedRow[] = [] - let currentHeight = 0 - for (let i = 0; i <= this.config.rows.length - 1; i++) { - const row = this.config.rows[i] - currentHeight += row.height - const cacheRow = new CachedRow({ - yPos: currentHeight, - rowIdx: i - }) - cachedRows.push(cacheRow) - } + this.table = new Table(this); + this.scroller = new Scroller(this); + this.toolbar = new Toolbar(this); + this.header = new Header(this); + this.editor = new Editor(this); + this.cache = this.getInitialCache(); + this.viewport = new Viewport( + this, + this.scroller.getViewportBoundlingRect(), + ); + this.selection = new Selection(); + this.data = data; + this.styles = new Styles(); + this.buildComponent(); + this.appendTableToTarget(target); + this.renderSheet(); + } - const cache = new Cache({ - columns: cachedCols, - rows: cachedRows - }) - - console.log("CACHE: ", cache) - console.log("CONFIG: ", this.config) - return cache + private getInitialCache(): Cache { + const cachedCols: CachedColumn[] = []; + let currentWidth = 0; + for (let i = 0; i <= this.config.columns.length - 1; i++) { + const col = this.config.columns[i]; + currentWidth += col.width; + const cacheCol = new CachedColumn({ + xPos: currentWidth, + colIdx: i, + }); + cachedCols.push(cacheCol); } - private buildComponent(): void { - - const content = document.createElement('div') //* Abstract - content.appendChild(this.header.element) - content.appendChild(this.sheet.element) - - content.classList.add(CSS_PREFIX + 'content') - - this.table.element.appendChild(this.toolbar.element) - this.table.element.appendChild(content) - this.table.element.appendChild(this.scroller.element) - this.table.element.append(this.editor.element) + const cachedRows: CachedRow[] = []; + let currentHeight = 0; + for (let i = 0; i <= this.config.rows.length - 1; i++) { + const row = this.config.rows[i]; + currentHeight += row.height; + const cacheRow = new CachedRow({ + yPos: currentHeight, + rowIdx: i, + }); + cachedRows.push(cacheRow); } - /**Destroy spreadsheet DOM element. - * - * May be usefull when need to rerender component. - */ - public destroy() { - this.table.element.remove() + const cache = new Cache({ + columns: cachedCols, + rows: cachedRows, + }); + + console.log("CACHE: ", cache); + console.log("CONFIG: ", this.config); + return cache; + } + + private buildComponent(): void { + const content = document.createElement("div"); //* Abstract + content.appendChild(this.header.element); + content.appendChild(this.sheet.element); + + content.classList.add(CSS_PREFIX + "content"); + + this.table.element.appendChild(this.toolbar.element); + this.table.element.appendChild(content); + this.table.element.appendChild(this.scroller.element); + this.table.element.append(this.editor.element); + } + + /**Destroy spreadsheet DOM element. + * + * May be usefull when need to rerender component. + */ + public destroy() { + this.table.element.remove(); + } + + private appendTableToTarget(target: string | HTMLElement) { + if (typeof target === "string") { + const element = document.querySelector(target); + if (!element) + throw new Error( + `Element with selector ${target} is not finded in DOM.\n Make sure it exists.`, + ); + element?.appendChild(this.table.element); + } + if (target instanceof HTMLElement) { + target.append(this.table.element); + } + } + + /** Canvas rendering context 2D. + * + * Abble to draw on canvas with default CanvasAPI methods + */ + get ctx() { + return this.sheet.ctx; + } + + get viewProps() { + return this.config.view; + } + + /** Focusing on interactive part of spreadsheet */ + focusTable() { + this.scroller.element.focus(); + } + + getCellByCoords(x: number, y: number) { + return this.sheet.getCellByCoords(x, y); + } + + getCell(position: Position): Cell { + const { column, row } = position; + return this.data[row][column]; + } + + changeCellValues( + position: Position, + values: Partial>, + ) { + const { column, row } = position; + + this.data[row][column].changeValues(values); + this.renderCell(row, column); + } + + changeCellStyles(position: Position, styles: CellStyles) { + const { column, row } = position; + this.data[row][column].changeStyles(styles); + this.renderCell(row, column); + } + + applyActionToRange( + range: RangeSelectionType, + callback: (cell: Cell) => void, + ): void { + const fromRow = Math.min(range.from.row, range.to.row); + const toRow = Math.max(range.from.row, range.to.row); + + const fromCol = Math.min(range.from.column, range.to.column); + const toCol = Math.max(range.from.column, range.to.column); + + for (let row = fromRow; row <= toRow; row++) { + for (let col = fromCol; col <= toCol; col++) { + const cell = this.data[row][col]; + callback(cell); + } + } + } + + deleteSelectedCellsValues() { + if (this.selection.selectedRange !== null) { + this.applyActionToRange(this.selection.selectedRange, (cell) => { + this.changeCellValues(cell.position, { + displayValue: "", + resultValue: "", + value: "", + }); + }); + } else { + if (!this.selection.selectedCell) return; + this.changeCellValues(this.selection.selectedCell, { + displayValue: "", + resultValue: "", + value: "", + }); + } + } + + showEditor(position: Position, initialString?: string) { + this.editor.show(position, initialString); + } + + renderSheet() { + this.sheet.renderSheet(); + } + + renderCell(row: number, col: number) { + this.data[row][col].render(this); + } + + public loadData(data: Cell[][] | SerializableCell[][]): Spreadsheet { + const rowsLength = data.length; + const colsLength = data[0] ? this.data[0].length : 0; + this.data = []; + + const formattedData: Cell[][] = []; + + for (let row = 0; row < rowsLength; row++) { + const innerRow: Cell[] = []; + for (let col = 0; col < colsLength; col++) { + const cell = data[row][col]; + innerRow.push( + new Cell({ + displayValue: cell.displayValue, + position: cell.position, + resultValue: cell.resultValue, + value: cell.value, + style: cell.style, + }), + ); + } + formattedData.push(innerRow); } - private appendTableToTarget(target: string | HTMLElement) { - if (typeof target === 'string') { - const element = document.querySelector(target) - if (!element) throw new Error(`Element with selector ${target} is not finded in DOM.\n Make sure it exists.`) - element?.appendChild(this.table.element) - } - if (target instanceof HTMLElement) { - target.append(this.table.element) - } + this.data = formattedData; + + this.selection.selectedCell = null; + this.selection.selectedRange = null; + this.config = this.makeConfigFromData(formattedData, this.config.view); + this.cache = this.getInitialCache(); + this.scroller.updateScrollerSize(); + this.viewport = new Viewport( + this, + this.scroller.getViewportBoundlingRect(), + ); + this.renderSheet(); + + return this; + } + + private makeConfigFromData(data: Cell[][], view: ViewProperties): Config { + const lastRowIdx = data.length - 1; + const lastColIdx = data[0] ? data[0].length : 0; + + const rows: Row[] = []; + for (let row = 0; row < lastRowIdx; row++) { + rows.push( + new Row({ + height: 40, + title: String(row), + }), + ); } - /** Canvas rendering context 2D. - * - * Abble to draw on canvas with default CanvasAPI methods - */ - get ctx() { - return this.sheet.ctx + const columns: Column[] = []; + + for (let col = 0; col < lastColIdx; col++) { + columns.push( + new Column({ + width: 150, + title: String(col), + }), + ); } - get viewProps() { - return this.config.view + const config = new Config({ + view, + rows, + columns, + }); + + return config; + } + + public serializeData(): SerializableCell[][] { + const rowsLength = this.data.length; + const colsLength = this.data[0] ? this.data[0].length : 0; + + const cellsArray: SerializableCell[][] = []; + + for (let row = 0; row < rowsLength; row++) { + const innerRow: SerializableCell[] = []; + for (let col = 0; col < colsLength; col++) { + innerRow.push(this.data[row][col].getSerializableCell()); + } + cellsArray.push(innerRow); } - /** Focusing on interactive part of spreadsheet */ - focusTable() { - this.scroller.element.focus() - } - - getCellByCoords(x: number, y: number) { - return this.sheet.getCellByCoords(x, y) - } - - getCell(position: Position): Cell { - const { column, row } = position - return this.data[row][column] - } - - changeCellValues(position: Position, values: Partial>) { - const { column, row } = position - - this.data[row][column].changeValues(values) - this.renderCell(row, column) - } - - changeCellStyles(position: Position, styles: CellStyles) { - const { column, row } = position - this.data[row][column].changeStyles(styles) - this.renderCell(row, column) - } - - applyActionToRange(range: RangeSelectionType, callback: (cell: Cell) => any): void { - const fromRow = Math.min(range.from.row, range.to.row) - const toRow = Math.max(range.from.row, range.to.row) - - const fromCol = Math.min(range.from.column, range.to.column) - const toCol = Math.max(range.from.column, range.to.column) - - for (let row = fromRow; row <= toRow; row++) { - for (let col = fromCol; col <= toCol; col++) { - const cell = this.data[row][col] - callback(cell) - } - } - } - - deleteSelectedCellsValues() { - if (this.selection.selectedRange !== null) { - - this.applyActionToRange(this.selection.selectedRange, cell => { - this.changeCellValues(cell.position, { - displayValue: '', - resultValue: '', - value: '' - }) - }) - - } else { - if (!this.selection.selectedCell) return; - this.changeCellValues(this.selection.selectedCell, { - displayValue: '', - resultValue: '', - value: '' - }) - } - } - - showEditor(position: Position, initialString?: string) { - this.editor.show(position, initialString) - } - - renderSheet() { - this.sheet.renderSheet() - } - - renderCell(row: number, col: number) { - this.data[row][col].render(this) - } - - public loadData(data: Cell[][] | SerializableCell[][]): Spreadsheet { - const rowsLength = data.length - const colsLength = data[0] ? this.data[0].length : 0 - this.data = [] - - const formattedData: Cell[][] = [] - - for (let row = 0; row < rowsLength; row++) { - const innerRow: Cell[] = [] - for (let col = 0; col < colsLength; col++) { - const cell = data[row][col] - innerRow.push(new Cell({ - displayValue: cell.displayValue, - position: cell.position, - resultValue: cell.resultValue, - value: cell.value, - style: cell.style - })) - } - formattedData.push(innerRow) - } - - this.data = formattedData - - this.selection.selectedCell = null - this.selection.selectedRange = null - this.config = this.makeConfigFromData(formattedData, this.config.view) - this.cache = this.getInitialCache() - this.scroller.updateScrollerSize() - this.viewport = new Viewport(this, this.scroller.getViewportBoundlingRect()) - this.renderSheet() - - return this - } - - private makeConfigFromData(data: Cell[][], view: ViewProperties): Config { - const lastRowIdx = data.length - 1 - const lastColIdx = data[0] ? data[0].length : 0 - - const rows: Row[] = [] - for (let row = 0; row < lastRowIdx; row++) { - rows.push(new Row({ - height: 40, - title: String(row) - })) - } - - const columns: Column[] = [] - - for (let col = 0; col < lastColIdx; col++) { - columns.push(new Column({ - width: 150, - title: String(col) - })) - } - - const config = new Config({ - view, - rows, - columns - }) - - return config - } - - public serializeData(): SerializableCell[][] { - const rowsLength = this.data.length - const colsLength = this.data[0] ? this.data[0].length : 0 - - const cellsArray: SerializableCell[][] = [] - - for (let row = 0; row < rowsLength; row++) { - const innerRow: SerializableCell[] = [] - for (let col = 0; col < colsLength; col++) { - innerRow.push(this.data[row][col].getSerializableCell()) - } - cellsArray.push(innerRow) - } - - return cellsArray - } + return cellsArray; + } } -export * from './modules/cache' -export * from './modules/cell' -export * from './modules/column' -export * from './modules/config' -export * from './modules/renderBox' -export * from './modules/row' -export * from './modules/selection' -export * from './modules/styles' -export * from './modules/viewport' +export * from "./modules/cache"; +export * from "./modules/cell"; +export * from "./modules/column"; +export * from "./modules/config"; +export * from "./modules/renderBox"; +export * from "./modules/row"; +export * from "./modules/selection"; +export * from "./modules/styles"; +export * from "./modules/viewport"; -export * from './utils/createData' \ No newline at end of file +export * from "./utils/createData"; diff --git a/src/modules/cache.ts b/src/modules/cache.ts index 5de0330..9120f0a 100644 --- a/src/modules/cache.ts +++ b/src/modules/cache.ts @@ -1,67 +1,67 @@ export interface CachedColumnProperties { - xPos: number - colIdx: number + xPos: number; + colIdx: number; } export class CachedColumn { - xPos: number - colIdx: number + xPos: number; + colIdx: number; - constructor(props: CachedColumnProperties) { - this.xPos = props.xPos - this.colIdx = props.colIdx - } + constructor(props: CachedColumnProperties) { + this.xPos = props.xPos; + this.colIdx = props.colIdx; + } } export interface CachedRowProperties { - yPos: number - rowIdx: number + yPos: number; + rowIdx: number; } export class CachedRow { - yPos: number - rowIdx: number + yPos: number; + rowIdx: number; - constructor(props: CachedRowProperties) { - this.yPos = props.yPos - this.rowIdx = props.rowIdx - } + constructor(props: CachedRowProperties) { + this.yPos = props.yPos; + this.rowIdx = props.rowIdx; + } } export interface CacheConstructorProps { - columns: CachedColumn[] - rows: CachedRow[] + columns: CachedColumn[]; + rows: CachedRow[]; } export class Cache { - public columns: CachedColumn[] - public rows: CachedRow[] - constructor(initial: CacheConstructorProps) { - this.columns = initial.columns - this.rows = initial.rows - } + public columns: CachedColumn[]; + public rows: CachedRow[]; + constructor(initial: CacheConstructorProps) { + this.columns = initial.columns; + this.rows = initial.rows; + } - public getRowByYCoord(y: number): number { - let rowIdx = 0; - for (let i = 0; i < this.rows.length; i++) { - if (y <= this.rows[i].yPos) { //* Intersection detect - rowIdx = i; - break; - } - } - return rowIdx; + public getRowByYCoord(y: number): number { + let rowIdx = 0; + for (let i = 0; i < this.rows.length; i++) { + if (y <= this.rows[i].yPos) { + //* Intersection detect + rowIdx = i; + break; + } } - + return rowIdx; + } - public getColumnByXCoord(x: number): number { - let colIdx = 0; - for (let i = 0; i < this.columns.length; i++) { - if (x <= this.columns[i].xPos) { //* Intersection detect - colIdx = i; - break; - } - } - return colIdx; + public getColumnByXCoord(x: number): number { + let colIdx = 0; + for (let i = 0; i < this.columns.length; i++) { + if (x <= this.columns[i].xPos) { + //* Intersection detect + colIdx = i; + break; + } } - -} \ No newline at end of file + return colIdx; + } +} diff --git a/src/modules/cell.ts b/src/modules/cell.ts index dac0b3b..c61cb4e 100644 --- a/src/modules/cell.ts +++ b/src/modules/cell.ts @@ -1,134 +1,148 @@ -import Spreadsheet from "../main" -import { RenderBox } from "./renderBox" +import Spreadsheet from "../main"; +import { RenderBox } from "./renderBox"; export type CellConstructorProps = { - value: string - displayValue: string - resultValue: string - position: Position - style: CellStyles | null -} + value: string; + displayValue: string; + resultValue: string; + position: Position; + style: CellStyles | null; +}; interface CellStylesConstructorProps { - fontSize: number - fontColor: string - background: string - borderColor: string + fontSize: number; + fontColor: string; + background: string; + borderColor: string; - selectedBackground: string - selectedFontColor: string + selectedBackground: string; + selectedFontColor: string; } export class CellStyles { - fontSize: number = 16 - fontColor: string = 'black' - background: string = 'white' - borderColor: string = 'black' + fontSize: number = 16; + fontColor: string = "black"; + background: string = "white"; + borderColor: string = "black"; - selectedBackground = '#4287f5' - selectedFontColor = '#ffffff' + selectedBackground = "#4287f5"; + selectedFontColor = "#ffffff"; - constructor(props?: CellStylesConstructorProps) { - if (props) { - Object.assign(this, props) // Override default styles - } + constructor(props?: CellStylesConstructorProps) { + if (props) { + Object.assign(this, props); // Override default styles } + } } export class Position { - row: number - column: number - constructor(row: number, column: number) { - this.row = row - this.column = column - } + row: number; + column: number; + constructor(row: number, column: number) { + this.row = row; + this.column = column; + } } export class SerializableCell { - value: string - displayValue: string - resultValue: string - position: Position - style: CellStyles | null - constructor(props: SerializableCell | SerializableCell) { - this.value = props.value - this.displayValue = props.displayValue - this.resultValue = props.resultValue - this.position = props.position - this.style = props.style - } + value: string; + displayValue: string; + resultValue: string; + position: Position; + style: CellStyles | null; + constructor(props: SerializableCell | SerializableCell) { + this.value = props.value; + this.displayValue = props.displayValue; + this.resultValue = props.resultValue; + this.position = props.position; + this.style = props.style; + } } export class Cell { - /** True value (data) */ - value: string - /** Value to render */ - displayValue: string - /** This refers to the values ​​​​that were obtained by calculations, for example, after calculating the formula */ - resultValue: string - position: Position - style: CellStyles | null = null + /** True value (data) */ + value: string; + /** Value to render */ + displayValue: string; + /** This refers to the values that were obtained by calculations, for example, after calculating the formula */ + resultValue: string; + position: Position; + style: CellStyles | null = null; - constructor(props: CellConstructorProps) { - this.value = props.value - this.displayValue = props.displayValue - this.resultValue = props.resultValue - this.position = props.position - this.style = props.style - } + constructor(props: CellConstructorProps) { + this.value = props.value; + this.displayValue = props.displayValue; + this.resultValue = props.resultValue; + this.position = props.position; + this.style = props.style; + } - public getSerializableCell(): SerializableCell { - const cell:SerializableCell = new SerializableCell({ - displayValue: this.displayValue, - position: this.position, - resultValue: this.resultValue, - style: this.style, - value: this.value - }) - return cell - } + public getSerializableCell(): SerializableCell { + const cell: SerializableCell = new SerializableCell({ + displayValue: this.displayValue, + position: this.position, + resultValue: this.resultValue, + style: this.style, + value: this.value, + }); + return cell; + } - changeStyles(styles: CellStyles) { - this.style = styles - } + changeStyles(styles: CellStyles) { + this.style = styles; + } - changeValues(values: Partial>) { - Object.assign(this, values) - } + changeValues(values: Partial>) { + Object.assign(this, values); + } - private isCellInRange(root: Spreadsheet): boolean { - const { column, row } = this.position - const { selectedRange } = root.selection + private isCellInRange(root: Spreadsheet): boolean { + const { column, row } = this.position; + const { selectedRange } = root.selection; - if (!selectedRange) return false; + 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) + 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 - } + return isCellInCol && isCellInRow; + } - render(root: Spreadsheet) { - let { height, width, x, y } = new RenderBox(root.config, this.position) - const { ctx } = root + render(root: Spreadsheet) { + const renderBox = new RenderBox(root.config, this.position); + let {x, y} = renderBox + const {height, width} = renderBox + const { ctx } = root; - const isCellSelected = (root.selection.selectedCell?.row === this.position.row && root.selection.selectedCell.column === this.position.column) - const isCellInRange = this.isCellInRange(root) - y -= root.viewport.top - x -= root.viewport.left + const isCellSelected = + root.selection.selectedCell?.row === this.position.row && + root.selection.selectedCell.column === this.position.column; + const isCellInRange = this.isCellInRange(root); + y -= root.viewport.top; + 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.fillStyle = isCellSelected || isCellInRange ? styles.selectedBackground : styles.background - ctx.strokeStyle = 'black' - ctx.fillRect(x, y, width - 1, height - 1) - ctx.strokeRect(x, y, width, height) + ctx.clearRect(x, y, width, height); + ctx.fillStyle = + isCellSelected || isCellInRange + ? styles.selectedBackground + : styles.background; + ctx.strokeStyle = "black"; + ctx.fillRect(x, y, width - 1, height - 1); + ctx.strokeRect(x, y, width, height); - ctx.fillStyle = isCellSelected || isCellInRange ? styles.selectedFontColor : styles.fontColor - ctx.textAlign = 'left' - ctx.font = `${styles.fontSize}px Arial` - ctx.textBaseline = 'middle' - ctx.fillText(this.displayValue, x + 2, y + height / 2) - } -} \ No newline at end of file + ctx.fillStyle = + isCellSelected || isCellInRange + ? styles.selectedFontColor + : styles.fontColor; + ctx.textAlign = "left"; + ctx.font = `${styles.fontSize}px Arial`; + ctx.textBaseline = "middle"; + ctx.fillText(this.displayValue, x + 2, y + height / 2); + } +} diff --git a/src/modules/column.ts b/src/modules/column.ts index b187d41..ac3d632 100644 --- a/src/modules/column.ts +++ b/src/modules/column.ts @@ -1,14 +1,14 @@ export type ColumnConstructorProperties = { - width: number - title: string -} + width: number; + title: string; +}; export class Column { - width: number - title: string + width: number; + title: string; - constructor(props: ColumnConstructorProperties) { - this.width = props.width - this.title = props.title - } -} \ No newline at end of file + constructor(props: ColumnConstructorProperties) { + this.width = props.width; + this.title = props.title; + } +} diff --git a/src/modules/config.ts b/src/modules/config.ts index b92d6fd..83f55b1 100644 --- a/src/modules/config.ts +++ b/src/modules/config.ts @@ -1,39 +1,38 @@ -import { Column } from "./column" -import { Row } from "./row" +import { Column } from "./column"; +import { Row } from "./row"; export interface ViewProperties { - width: number - height: number + width: number; + height: number; } export type ConfigProperties = { - /** Please, end it with '_' symbol. - * - * *Example:* - * - * 'test_' - * 'google_' */ - rows: Row[] - columns: Column[] - view: ViewProperties -} - + /** Please, end it with '_' symbol. + * + * *Example:* + * + * 'test_' + * 'google_' */ + rows: Row[]; + columns: Column[]; + view: ViewProperties; +}; export type SheetConfigConstructorProps = { - rows: Row[] - columns: Column[] -} + rows: Row[]; + columns: Column[]; +}; export class Config { - rows: Row[] - columns: Column[] - view: ViewProperties = { - width: 800, - height: 600, - } - constructor(props: ConfigProperties) { - this.columns = props.columns - this.rows = props.rows - this.view = props.view - } -} \ No newline at end of file + rows: Row[]; + columns: Column[]; + view: ViewProperties = { + width: 800, + height: 600, + }; + constructor(props: ConfigProperties) { + this.columns = props.columns; + this.rows = props.rows; + this.view = props.view; + } +} diff --git a/src/modules/renderBox.ts b/src/modules/renderBox.ts index 489873d..9c644ef 100644 --- a/src/modules/renderBox.ts +++ b/src/modules/renderBox.ts @@ -2,33 +2,32 @@ import { Position } from "./cell"; import { Config } from "./config"; export class RenderBox { - x: number - y: number - width: number - height: number - constructor(config: Config, cellPosition: Position) { + x: number; + y: number; + width: number; + height: number; + constructor(config: Config, cellPosition: Position) { + this.x = this.getXCoord(cellPosition.column, config); + this.y = this.getYCoord(cellPosition.row, config); + this.width = config.columns[cellPosition.column].width; + this.height = config.rows[cellPosition.row].height; + } - this.x = this.getXCoord(cellPosition.column, config) - this.y = this.getYCoord(cellPosition.row, config) - this.width = config.columns[cellPosition.column].width - this.height = config.rows[cellPosition.row].height + private getXCoord(column: number, config: Config): number { + let x = 0; + + for (let i = 0; i < column; i++) { + x += config.columns[i].width; } - private getXCoord(column: number, config: Config): number { - let x = 0; + return x; + } - for (let i = 0; i < column; i++) { - x += config.columns[i].width - } - - return x + private getYCoord(row: number, config: Config): number { + let y = 0; + for (let i = 0; i < row; i++) { + y += config.rows[i].height; } - - private getYCoord(row: number, config: Config): number { - let y = 0 - for (let i = 0; i < row; i++) { - y += config.rows[i].height - } - return y - } -} \ No newline at end of file + return y; + } +} diff --git a/src/modules/row.ts b/src/modules/row.ts index 9a7337a..ca8f69c 100644 --- a/src/modules/row.ts +++ b/src/modules/row.ts @@ -1,13 +1,13 @@ -export type RowConstructorProps = { - height: number - title: string -} +export type RowConstructorProps = { + height: number; + title: string; +}; export class Row { - height: number - title: string - constructor(props: RowConstructorProps) { - this.height = props.height - this.title = props.title - } -} \ No newline at end of file + height: number; + title: string; + constructor(props: RowConstructorProps) { + this.height = props.height; + this.title = props.title; + } +} diff --git a/src/modules/selection.ts b/src/modules/selection.ts index 14a1db2..e228603 100644 --- a/src/modules/selection.ts +++ b/src/modules/selection.ts @@ -1,14 +1,14 @@ export type BaseSelectionType = { - row: number - column: number -} + row: number; + column: number; +}; export type RangeSelectionType = { - from: BaseSelectionType - to: BaseSelectionType -} + from: BaseSelectionType; + to: BaseSelectionType; +}; export class Selection { - selectedCell: BaseSelectionType | null = null - selectedRange: RangeSelectionType | null = null -} \ No newline at end of file + selectedCell: BaseSelectionType | null = null; + selectedRange: RangeSelectionType | null = null; +} diff --git a/src/modules/styles.ts b/src/modules/styles.ts index 3207abf..e4fd5f5 100644 --- a/src/modules/styles.ts +++ b/src/modules/styles.ts @@ -1,9 +1,8 @@ import { CellStyles } from "./cell"; - export class Styles { - cells: CellStyles - constructor() { - this.cells = new CellStyles() - } -} \ No newline at end of file + cells: CellStyles; + constructor() { + this.cells = new CellStyles(); + } +} diff --git a/src/modules/viewport.ts b/src/modules/viewport.ts index ee9f4b9..0f4ceae 100644 --- a/src/modules/viewport.ts +++ b/src/modules/viewport.ts @@ -1,79 +1,78 @@ -import Spreadsheet from "../main" +import Spreadsheet from "../main"; export type ViewportConstructorProps = { - top: number - left: number - right: number - bottom: number -} + top: number; + left: number; + right: number; + bottom: number; +}; export class Viewport { - root: Spreadsheet + root: Spreadsheet; - top: number - left: number - right: number - bottom: number + top: number; + left: number; + right: number; + bottom: number; - firstRow: number - lastRow: number - firstCol: number - lastCol: number + firstRow: number; + lastRow: number; + firstCol: number; + lastCol: number; - constructor(root: Spreadsheet, props: ViewportConstructorProps) { - this.root = root + constructor(root: Spreadsheet, props: ViewportConstructorProps) { + this.root = root; - this.top = props.top - this.left = props.left - this.right = props.right - this.bottom = props.bottom + this.top = props.top; + this.left = props.left; + this.right = props.right; + this.bottom = props.bottom; - this.firstRow = this.getFirstRow() - this.lastCol = this.getFirstRow() //!Temp - this.firstCol = this.getFirstRow() //!Temp - this.lastRow = this.getLastRow() + this.firstRow = this.getFirstRow(); + this.lastCol = this.getFirstRow(); //!Temp + this.firstCol = this.getFirstRow(); //!Temp + this.lastRow = this.getLastRow(); - this.updateValues({ - top: 0, - left: 0, - right: this.root.viewProps.width, - bottom: this.root.viewProps.height - }) - } + this.updateValues({ + top: 0, + left: 0, + right: this.root.viewProps.width, + bottom: this.root.viewProps.height, + }); + } - updateValues(props: ViewportConstructorProps) { - this.top = props.top - this.left = props.left - this.right = props.right - this.bottom = props.bottom + updateValues(props: ViewportConstructorProps) { + this.top = props.top; + this.left = props.left; + this.right = props.right; + this.bottom = props.bottom; - this.firstRow = this.getFirstRow() - this.lastRow = this.getLastRow() - this.firstCol = this.getFirstCol() - this.lastCol = this.getLastCol() - } + this.firstRow = this.getFirstRow(); + this.lastRow = this.getLastRow(); + this.firstCol = this.getFirstCol(); + this.lastCol = this.getLastCol(); + } - /** Get index of first row in viewport */ - private getFirstRow(): number { - let rowIdx = this.root.cache.getRowByYCoord(this.top) - return rowIdx - } + /** Get index of first row in viewport */ + private getFirstRow(): number { + const rowIdx = this.root.cache.getRowByYCoord(this.top); + return rowIdx; + } - private getLastRow(): number { - let rowIdx = this.root.cache.getRowByYCoord(this.bottom) - return rowIdx - } + private getLastRow(): number { + const rowIdx = this.root.cache.getRowByYCoord(this.bottom); + return rowIdx; + } - private getFirstCol(): number { - let colIdx = this.root.cache.getColumnByXCoord(this.left) + private getFirstCol(): number { + const colIdx = this.root.cache.getColumnByXCoord(this.left); - return colIdx - } + return colIdx; + } - private getLastCol(): number { - let colIdx = this.root.cache.getColumnByXCoord(this.right) + private getLastCol(): number { + const colIdx = this.root.cache.getColumnByXCoord(this.right); - return colIdx - } - -} \ No newline at end of file + return colIdx; + } +} diff --git a/src/scss/_global.scss b/src/scss/_global.scss index e8007e4..d392ed7 100644 --- a/src/scss/_global.scss +++ b/src/scss/_global.scss @@ -1,4 +1,4 @@ body { - padding: 0px; - margin: 0px; + padding: 0px; + margin: 0px; } diff --git a/src/scss/_spreadsheet.scss b/src/scss/_spreadsheet.scss index 0717913..d7fb8fc 100644 --- a/src/scss/_spreadsheet.scss +++ b/src/scss/_spreadsheet.scss @@ -1,38 +1,38 @@ $css_prefix: "modern_sc_"; .#{$css_prefix}content { - position: absolute; - top: 0; - left: 0; + position: absolute; + top: 0; + left: 0; } .#{$css_prefix}spreadsheet_container { - position: relative; - isolation: isolate; - border: 2px solid black; + position: relative; + isolation: isolate; + border: 2px solid black; } -.#{$css_prefix}sheet{ - display: block; - contain: strict; +.#{$css_prefix}sheet { + display: block; + contain: strict; } .#{$css_prefix}scroller { - overflow: scroll; - box-sizing: border-box; - transform: translateZ(0); - &:focus { - outline: none; - } + overflow: scroll; + box-sizing: border-box; + transform: translateZ(0); + &:focus { + outline: none; + } } .#{$css_prefix}editor { - position: absolute; - box-sizing: border-box; - font-size: 16px; - font-family: Arial, Helvetica, sans-serif; + position: absolute; + box-sizing: border-box; + font-size: 16px; + font-family: Arial, Helvetica, sans-serif; } .#{$css_prefix}hide { - visibility: hidden; -} \ No newline at end of file + visibility: hidden; +} diff --git a/src/scss/main.scss b/src/scss/main.scss index c080071..358bb91 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -1,2 +1,2 @@ -@import 'global.scss'; -@import 'spreadsheet.scss'; \ No newline at end of file +@import "global.scss"; +@import "spreadsheet.scss"; diff --git a/src/utils/createData.ts b/src/utils/createData.ts index 1c87ca6..f3ce71b 100644 --- a/src/utils/createData.ts +++ b/src/utils/createData.ts @@ -3,72 +3,78 @@ import { Column } from "../modules/column"; import { Config } from "../modules/config"; import { Row } from "../modules/row"; -export function createSampleData(rows: number, columns: number, fillCellsByCoords: boolean = false): Cell[][] { - const data: Cell[][] = [] +export function createSampleData( + rows: number, + columns: number, + fillCellsByCoords: boolean = false, +): Cell[][] { + const data: Cell[][] = []; - for (let row = 0; row <= rows; row++) { - const innerRow: Cell[] = [] - for (let col = 0; col <= columns; col++) { - const value = fillCellsByCoords ? `${row}:${col}` : '' + for (let row = 0; row <= rows; row++) { + const innerRow: Cell[] = []; + for (let col = 0; col <= columns; col++) { + const value = fillCellsByCoords ? `${row}:${col}` : ""; - const cell = new Cell({ - displayValue: value, - resultValue: value, - value, - position: { - column: col, - row: row - }, - style: null - }) + const cell = new Cell({ + displayValue: value, + resultValue: value, + value, + position: { + column: col, + row: row, + }, + style: null, + }); - innerRow.push(cell) - } - data.push(innerRow) + innerRow.push(cell); } - return data + data.push(innerRow); + } + return data; } export function createSampleConfig(rows: number, columns: number): Config { + const rowsArr: Row[] = []; + for (let i = 0; i <= rows; i++) { + const rowItem = new Row({ + height: 40, + title: String(i), + }); + rowsArr.push(rowItem); + } - const rowsArr: Row[] = [] - for (let i = 0; i <= rows; i++) { - const rowItem = new Row({ - height: 40, - title: String(i) - }) - rowsArr.push(rowItem) - } + const colsArr: Column[] = []; + for (let i = 0; i <= columns; i++) { + const colItem = new Column({ + title: String(i), + width: 150, + }); + colsArr.push(colItem); + } - const colsArr: Column[] = [] - for (let i = 0; i <= columns; i++) { - const colItem = new Column({ - title: String(i), - width: 150 - }) - colsArr.push(colItem) - } + const config = new Config({ + columns: colsArr, + rows: rowsArr, + view: { + height: 600, + width: 800, + }, + }); - const config = new Config({ - columns: colsArr, - rows: rowsArr, - view: { - height: 600, - width: 800 - } - }) - - return config + return config; } export type SpreadsheetConfigAndDataReturnType = { - config: Config, - data: Cell[][] + config: Config; + data: Cell[][]; +}; + +export function makeSpreadsheetConfigAndData( + rows: number, + columns: number, +): SpreadsheetConfigAndDataReturnType { + const data = createSampleData(rows, columns); + const config = createSampleConfig(rows, columns); + + return { data, config }; } - -export function makeSpreadsheetConfigAndData(rows: number, columns: number): SpreadsheetConfigAndDataReturnType { - const data = createSampleData(rows, columns) - const config = createSampleConfig(rows, columns) - - return { data, config } -} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 0e5ca0d..166aa78 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,39 +1,39 @@ -import { defineConfig } from 'vite' -import path from 'path' +import { defineConfig } from "vite"; +import path from "path"; import typescript from "@rollup/plugin-typescript"; import { typescriptPaths } from "rollup-plugin-typescript-paths"; -import { fileURLToPath } from 'node:url'; +import { fileURLToPath } from "node:url"; export default defineConfig({ - base: '/modern_spreadsheet/', - plugins: [], - resolve: {}, - server: { - port: 3000, - open: true, + base: "/modern_spreadsheet/", + plugins: [], + resolve: {}, + server: { + port: 3000, + open: true, + }, + build: { + manifest: true, + minify: true, + reportCompressedSize: true, + sourcemap: true, + lib: { + entry: path.resolve(__dirname, "src/main.ts"), + fileName: "main", + formats: ["es", "cjs"], }, - build: { - manifest: true, - minify: true, - reportCompressedSize: true, - sourcemap: true, - lib: { - entry: path.resolve(__dirname, "src/main.ts"), - fileName: "main", - formats: ["es", "cjs"], - }, - rollupOptions: { - external: ["./src/index.ts"], - plugins: [ - typescriptPaths({ - preserveExtensions: true - }), - typescript({ - sourceMap: false, - declaration: true, - outDir: 'dist' - }) - ] - }, - } -}) \ No newline at end of file + rollupOptions: { + external: ["./src/index.ts"], + plugins: [ + typescriptPaths({ + preserveExtensions: true, + }), + typescript({ + sourceMap: false, + declaration: true, + outDir: "dist", + }), + ], + }, + }, +});