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