diff --git a/.yarn/cache/@lexical-clipboard-npm-0.7.5-0d475b4b93-a3fb01ecd9.zip b/.yarn/cache/@lexical-clipboard-npm-0.7.6-255284171a-2cc08a28f9.zip similarity index 93% rename from .yarn/cache/@lexical-clipboard-npm-0.7.5-0d475b4b93-a3fb01ecd9.zip rename to .yarn/cache/@lexical-clipboard-npm-0.7.6-255284171a-2cc08a28f9.zip index c6c9dd6aa..e9970ae49 100644 Binary files a/.yarn/cache/@lexical-clipboard-npm-0.7.5-0d475b4b93-a3fb01ecd9.zip and b/.yarn/cache/@lexical-clipboard-npm-0.7.6-255284171a-2cc08a28f9.zip differ diff --git a/.yarn/cache/@lexical-code-npm-0.7.5-259fe40fd2-14522373ae.zip b/.yarn/cache/@lexical-code-npm-0.7.6-8d90919312-67c688ae05.zip similarity index 92% rename from .yarn/cache/@lexical-code-npm-0.7.5-259fe40fd2-14522373ae.zip rename to .yarn/cache/@lexical-code-npm-0.7.6-8d90919312-67c688ae05.zip index 8b59049aa..1ea79b6b8 100644 Binary files a/.yarn/cache/@lexical-code-npm-0.7.5-259fe40fd2-14522373ae.zip and b/.yarn/cache/@lexical-code-npm-0.7.6-8d90919312-67c688ae05.zip differ diff --git a/.yarn/cache/@lexical-dragon-npm-0.7.5-83f3b2cd75-690e051f44.zip b/.yarn/cache/@lexical-dragon-npm-0.7.6-7319a27c5f-d565cc502f.zip similarity index 81% rename from .yarn/cache/@lexical-dragon-npm-0.7.5-83f3b2cd75-690e051f44.zip rename to .yarn/cache/@lexical-dragon-npm-0.7.6-7319a27c5f-d565cc502f.zip index 7c384636e..602e457cb 100644 Binary files a/.yarn/cache/@lexical-dragon-npm-0.7.5-83f3b2cd75-690e051f44.zip and b/.yarn/cache/@lexical-dragon-npm-0.7.6-7319a27c5f-d565cc502f.zip differ diff --git a/.yarn/cache/@lexical-hashtag-npm-0.7.5-c870be93b0-783fd2d6c0.zip b/.yarn/cache/@lexical-hashtag-npm-0.7.6-cb2dd3c9f8-352251fc41.zip similarity index 87% rename from .yarn/cache/@lexical-hashtag-npm-0.7.5-c870be93b0-783fd2d6c0.zip rename to .yarn/cache/@lexical-hashtag-npm-0.7.6-cb2dd3c9f8-352251fc41.zip index 026f13964..9cbc551da 100644 Binary files a/.yarn/cache/@lexical-hashtag-npm-0.7.5-c870be93b0-783fd2d6c0.zip and b/.yarn/cache/@lexical-hashtag-npm-0.7.6-cb2dd3c9f8-352251fc41.zip differ diff --git a/.yarn/cache/@lexical-history-npm-0.7.5-87faae9135-34569fc29d.zip b/.yarn/cache/@lexical-history-npm-0.7.6-b152d0a748-7461b6ace7.zip similarity index 92% rename from .yarn/cache/@lexical-history-npm-0.7.5-87faae9135-34569fc29d.zip rename to .yarn/cache/@lexical-history-npm-0.7.6-b152d0a748-7461b6ace7.zip index b481c11c2..41c5dcdc4 100644 Binary files a/.yarn/cache/@lexical-history-npm-0.7.5-87faae9135-34569fc29d.zip and b/.yarn/cache/@lexical-history-npm-0.7.6-b152d0a748-7461b6ace7.zip differ diff --git a/.yarn/cache/@lexical-html-npm-0.7.5-254521d200-bf88318aac.zip b/.yarn/cache/@lexical-html-npm-0.7.6-401fe69ed6-a53778ab12.zip similarity index 90% rename from .yarn/cache/@lexical-html-npm-0.7.5-254521d200-bf88318aac.zip rename to .yarn/cache/@lexical-html-npm-0.7.6-401fe69ed6-a53778ab12.zip index 1f4a934fc..c25c21a48 100644 Binary files a/.yarn/cache/@lexical-html-npm-0.7.5-254521d200-bf88318aac.zip and b/.yarn/cache/@lexical-html-npm-0.7.6-401fe69ed6-a53778ab12.zip differ diff --git a/.yarn/cache/@lexical-link-npm-0.7.5-5f90a49a95-153f88575a.zip b/.yarn/cache/@lexical-link-npm-0.7.6-60abf23443-ecf487bfd9.zip similarity index 92% rename from .yarn/cache/@lexical-link-npm-0.7.5-5f90a49a95-153f88575a.zip rename to .yarn/cache/@lexical-link-npm-0.7.6-60abf23443-ecf487bfd9.zip index be7f847be..fcb887b80 100644 Binary files a/.yarn/cache/@lexical-link-npm-0.7.5-5f90a49a95-153f88575a.zip and b/.yarn/cache/@lexical-link-npm-0.7.6-60abf23443-ecf487bfd9.zip differ diff --git a/.yarn/cache/@lexical-list-npm-0.7.5-731f1ee6f8-f44602977a.zip b/.yarn/cache/@lexical-list-npm-0.7.6-f677249d18-74b02536f7.zip similarity index 95% rename from .yarn/cache/@lexical-list-npm-0.7.5-731f1ee6f8-f44602977a.zip rename to .yarn/cache/@lexical-list-npm-0.7.6-f677249d18-74b02536f7.zip index 9e409c4e9..8a6d76854 100644 Binary files a/.yarn/cache/@lexical-list-npm-0.7.5-731f1ee6f8-f44602977a.zip and b/.yarn/cache/@lexical-list-npm-0.7.6-f677249d18-74b02536f7.zip differ diff --git a/.yarn/cache/@lexical-mark-npm-0.7.5-099512c9d8-3e8641a71c.zip b/.yarn/cache/@lexical-mark-npm-0.7.6-f096cb3ed4-fe361ce192.zip similarity index 85% rename from .yarn/cache/@lexical-mark-npm-0.7.5-099512c9d8-3e8641a71c.zip rename to .yarn/cache/@lexical-mark-npm-0.7.6-f096cb3ed4-fe361ce192.zip index 51857e885..2b127a1c7 100644 Binary files a/.yarn/cache/@lexical-mark-npm-0.7.5-099512c9d8-3e8641a71c.zip and b/.yarn/cache/@lexical-mark-npm-0.7.6-f096cb3ed4-fe361ce192.zip differ diff --git a/.yarn/cache/@lexical-markdown-npm-0.7.5-ef30748329-ac27bd5309.zip b/.yarn/cache/@lexical-markdown-npm-0.7.6-ac9069351c-1d73027e84.zip similarity index 97% rename from .yarn/cache/@lexical-markdown-npm-0.7.5-ef30748329-ac27bd5309.zip rename to .yarn/cache/@lexical-markdown-npm-0.7.6-ac9069351c-1d73027e84.zip index ea67db94a..8fc55ccfa 100644 Binary files a/.yarn/cache/@lexical-markdown-npm-0.7.5-ef30748329-ac27bd5309.zip and b/.yarn/cache/@lexical-markdown-npm-0.7.6-ac9069351c-1d73027e84.zip differ diff --git a/.yarn/cache/@lexical-offset-npm-0.7.5-8882f69b1d-f5b713e551.zip b/.yarn/cache/@lexical-offset-npm-0.7.6-0170285adb-249a2684de.zip similarity index 92% rename from .yarn/cache/@lexical-offset-npm-0.7.5-8882f69b1d-f5b713e551.zip rename to .yarn/cache/@lexical-offset-npm-0.7.6-0170285adb-249a2684de.zip index ea72092b3..a0dcc3361 100644 Binary files a/.yarn/cache/@lexical-offset-npm-0.7.5-8882f69b1d-f5b713e551.zip and b/.yarn/cache/@lexical-offset-npm-0.7.6-0170285adb-249a2684de.zip differ diff --git a/.yarn/cache/@lexical-overflow-npm-0.7.5-2fe419046b-a4694a96b6.zip b/.yarn/cache/@lexical-overflow-npm-0.7.6-05e5eac593-77755f1ed9.zip similarity index 83% rename from .yarn/cache/@lexical-overflow-npm-0.7.5-2fe419046b-a4694a96b6.zip rename to .yarn/cache/@lexical-overflow-npm-0.7.6-05e5eac593-77755f1ed9.zip index 1419d9cde..cad4d6702 100644 Binary files a/.yarn/cache/@lexical-overflow-npm-0.7.5-2fe419046b-a4694a96b6.zip and b/.yarn/cache/@lexical-overflow-npm-0.7.6-05e5eac593-77755f1ed9.zip differ diff --git a/.yarn/cache/@lexical-plain-text-npm-0.7.5-16effec12d-506d87b7f1.zip b/.yarn/cache/@lexical-plain-text-npm-0.7.6-9f6b884784-f9d3cd04be.zip similarity index 89% rename from .yarn/cache/@lexical-plain-text-npm-0.7.5-16effec12d-506d87b7f1.zip rename to .yarn/cache/@lexical-plain-text-npm-0.7.6-9f6b884784-f9d3cd04be.zip index 57372cbfe..98d8c1084 100644 Binary files a/.yarn/cache/@lexical-plain-text-npm-0.7.5-16effec12d-506d87b7f1.zip and b/.yarn/cache/@lexical-plain-text-npm-0.7.6-9f6b884784-f9d3cd04be.zip differ diff --git a/.yarn/cache/@lexical-react-npm-0.7.5-849c90abbd-0df2bd2c3d.zip b/.yarn/cache/@lexical-react-npm-0.7.6-f61ab83ba8-f98a47b09e.zip similarity index 90% rename from .yarn/cache/@lexical-react-npm-0.7.5-849c90abbd-0df2bd2c3d.zip rename to .yarn/cache/@lexical-react-npm-0.7.6-f61ab83ba8-f98a47b09e.zip index 8f9c38613..2a6202073 100644 Binary files a/.yarn/cache/@lexical-react-npm-0.7.5-849c90abbd-0df2bd2c3d.zip and b/.yarn/cache/@lexical-react-npm-0.7.6-f61ab83ba8-f98a47b09e.zip differ diff --git a/.yarn/cache/@lexical-rich-text-npm-0.7.5-9c3b4d905f-8b58eae116.zip b/.yarn/cache/@lexical-rich-text-npm-0.7.6-fea6f76fd7-53ddbd4e2a.zip similarity index 55% rename from .yarn/cache/@lexical-rich-text-npm-0.7.5-9c3b4d905f-8b58eae116.zip rename to .yarn/cache/@lexical-rich-text-npm-0.7.6-fea6f76fd7-53ddbd4e2a.zip index e8f0f811c..cface488e 100644 Binary files a/.yarn/cache/@lexical-rich-text-npm-0.7.5-9c3b4d905f-8b58eae116.zip and b/.yarn/cache/@lexical-rich-text-npm-0.7.6-fea6f76fd7-53ddbd4e2a.zip differ diff --git a/.yarn/cache/@lexical-selection-npm-0.7.5-f94c2e706d-57907d740d.zip b/.yarn/cache/@lexical-selection-npm-0.7.6-ca85f6e7e8-522d6ea559.zip similarity index 97% rename from .yarn/cache/@lexical-selection-npm-0.7.5-f94c2e706d-57907d740d.zip rename to .yarn/cache/@lexical-selection-npm-0.7.6-ca85f6e7e8-522d6ea559.zip index f3557c0cc..788a4782f 100644 Binary files a/.yarn/cache/@lexical-selection-npm-0.7.5-f94c2e706d-57907d740d.zip and b/.yarn/cache/@lexical-selection-npm-0.7.6-ca85f6e7e8-522d6ea559.zip differ diff --git a/.yarn/cache/@lexical-table-npm-0.7.5-93069cd3a8-6d0b3177d4.zip b/.yarn/cache/@lexical-table-npm-0.7.6-640c4a673d-2c6e93516b.zip similarity index 98% rename from .yarn/cache/@lexical-table-npm-0.7.5-93069cd3a8-6d0b3177d4.zip rename to .yarn/cache/@lexical-table-npm-0.7.6-640c4a673d-2c6e93516b.zip index 2c21882d6..213f97a2a 100644 Binary files a/.yarn/cache/@lexical-table-npm-0.7.5-93069cd3a8-6d0b3177d4.zip and b/.yarn/cache/@lexical-table-npm-0.7.6-640c4a673d-2c6e93516b.zip differ diff --git a/.yarn/cache/@lexical-text-npm-0.7.5-7f8ed6c23d-445d9dd3cc.zip b/.yarn/cache/@lexical-text-npm-0.7.6-8bf483ebf2-f8d645dfbd.zip similarity index 86% rename from .yarn/cache/@lexical-text-npm-0.7.5-7f8ed6c23d-445d9dd3cc.zip rename to .yarn/cache/@lexical-text-npm-0.7.6-8bf483ebf2-f8d645dfbd.zip index 80902a13d..d1ad10cb9 100644 Binary files a/.yarn/cache/@lexical-text-npm-0.7.5-7f8ed6c23d-445d9dd3cc.zip and b/.yarn/cache/@lexical-text-npm-0.7.6-8bf483ebf2-f8d645dfbd.zip differ diff --git a/.yarn/cache/@lexical-utils-npm-0.7.5-f46814baef-9f46fe5641.zip b/.yarn/cache/@lexical-utils-npm-0.7.6-5f6106fcc4-7562549583.zip similarity index 96% rename from .yarn/cache/@lexical-utils-npm-0.7.5-f46814baef-9f46fe5641.zip rename to .yarn/cache/@lexical-utils-npm-0.7.6-5f6106fcc4-7562549583.zip index f73490b99..65ad1b359 100644 Binary files a/.yarn/cache/@lexical-utils-npm-0.7.5-f46814baef-9f46fe5641.zip and b/.yarn/cache/@lexical-utils-npm-0.7.6-5f6106fcc4-7562549583.zip differ diff --git a/.yarn/cache/@lexical-yjs-npm-0.7.5-2a6f22137f-af25e26130.zip b/.yarn/cache/@lexical-yjs-npm-0.7.6-618ae81f79-9337f066ad.zip similarity index 98% rename from .yarn/cache/@lexical-yjs-npm-0.7.5-2a6f22137f-af25e26130.zip rename to .yarn/cache/@lexical-yjs-npm-0.7.6-618ae81f79-9337f066ad.zip index 5a50d33d0..c2287d53d 100644 Binary files a/.yarn/cache/@lexical-yjs-npm-0.7.5-2a6f22137f-af25e26130.zip and b/.yarn/cache/@lexical-yjs-npm-0.7.6-618ae81f79-9337f066ad.zip differ diff --git a/.yarn/cache/eslint-plugin-react-npm-7.32.0-60a40d5ae1-b81ce2623b.zip b/.yarn/cache/eslint-plugin-react-npm-7.32.0-60a40d5ae1-b81ce2623b.zip new file mode 100644 index 000000000..729ade426 Binary files /dev/null and b/.yarn/cache/eslint-plugin-react-npm-7.32.0-60a40d5ae1-b81ce2623b.zip differ diff --git a/.yarn/cache/lexical-npm-0.7.5-83e871f9b5-fa6955a6c9.zip b/.yarn/cache/lexical-npm-0.7.5-83e871f9b5-fa6955a6c9.zip deleted file mode 100644 index a6f7782e1..000000000 Binary files a/.yarn/cache/lexical-npm-0.7.5-83e871f9b5-fa6955a6c9.zip and /dev/null differ diff --git a/.yarn/cache/lexical-npm-0.7.6-a76c7e85e6-594423da85.zip b/.yarn/cache/lexical-npm-0.7.6-a76c7e85e6-594423da85.zip new file mode 100644 index 000000000..c24da0e4a Binary files /dev/null and b/.yarn/cache/lexical-npm-0.7.6-a76c7e85e6-594423da85.zip differ diff --git a/.yarn/cache/prettier-plugin-tailwindcss-npm-0.2.0-1d6c5eaa79-427cd16e5c.zip b/.yarn/cache/prettier-plugin-tailwindcss-npm-0.2.1-848c29a572-5a04b26f50.zip similarity index 91% rename from .yarn/cache/prettier-plugin-tailwindcss-npm-0.2.0-1d6c5eaa79-427cd16e5c.zip rename to .yarn/cache/prettier-plugin-tailwindcss-npm-0.2.1-848c29a572-5a04b26f50.zip index 0dad6a92a..97fc31a1c 100644 Binary files a/.yarn/cache/prettier-plugin-tailwindcss-npm-0.2.0-1d6c5eaa79-427cd16e5c.zip and b/.yarn/cache/prettier-plugin-tailwindcss-npm-0.2.1-848c29a572-5a04b26f50.zip differ diff --git a/packages/blocks-editor/.eslintrc.js b/packages/blocks-editor/.eslintrc.js index 592685e7e..ab45dd8b6 100644 --- a/packages/blocks-editor/.eslintrc.js +++ b/packages/blocks-editor/.eslintrc.js @@ -1,254 +1,18 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -'use strict'; - -const restrictedGlobals = require('confusing-browser-globals'); - -const OFF = 0; -const ERROR = 2; - module.exports = { root: true, - // Prettier must be last so it can override other configs (https://github.com/prettier/eslint-config-prettier#installation) - extends: [ - 'fbjs', - 'plugin:react-hooks/recommended', - 'plugin:lexical/all', - 'prettier', - ], - + extends: ['../../common.eslintrc.js', 'plugin:react-hooks/recommended'], + parserOptions: { + project: './tsconfig.json', + tsconfigRootDir: __dirname, + }, + ignorePatterns: ['**/*.spec.ts', '__mocks__'], + plugins: ['@typescript-eslint', 'react', 'react-hooks', 'prettier'], + env: { + browser: true, + }, globals: { + __WEB_VERSION__: true, JSX: true, __DEV__: true, }, - - overrides: [ - { - // We apply these settings to the source files that get compiled. - // They can use all features including JSX (but shouldn't use `var`). - files: [ - 'packages/*/src/**/*.js', - 'packages/*/__tests__/**/*.?(m)js', - 'packages/*/src/**/*.jsx', - ], - parser: 'babel-eslint', - parserOptions: { - allowImportExportEverywhere: true, - sourceType: 'module', - }, - rules: { - 'no-var': ERROR, - 'prefer-const': ERROR, - strict: OFF, - }, - }, - { - // node scripts should be console logging so don't lint against that - files: ['scripts/**/*.js'], - rules: { - 'no-console': OFF, - }, - }, - { - env: { - browser: true, - }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/eslint-recommended', - 'plugin:@typescript-eslint/recommended', - ], - files: ['**/*.ts', '**/*.tsx'], - parser: '@typescript-eslint/parser', - parserOptions: { - sourceType: 'module', - }, - plugins: ['react', '@typescript-eslint', 'header'], - rules: { - '@typescript-eslint/ban-ts-comment': OFF, - '@typescript-eslint/no-this-alias': OFF, - '@typescript-eslint/no-unused-vars': [ERROR, {args: 'none'}], - 'header/header': [2, 'scripts/www/headerTemplate.js'], - }, - }, - { - // don't lint headers in entrypoint files so we can add TypeDoc module comments - files: ['packages/**/src/index.ts'], - rules: { - 'header/header': OFF, - }, - }, - { - files: [ - 'packages/**/src/__tests__/**', - 'packages/lexical-playground/**', - 'packages/lexical-devtools/**', - ], - rules: { - 'lexical/no-optional-chaining': OFF, - }, - }, - ], - - parser: 'babel-eslint', - - parserOptions: { - ecmaFeatures: { - experimentalObjectRestSpread: true, - }, - ecmaVersion: 8, - sourceType: 'script', - }, - - plugins: [ - 'sort-keys-fix', - 'simple-import-sort', - 'header', - - // import helps to configure simple-import-sort - 'import', - 'jest', - 'no-function-declare-after-return', - 'react', - 'no-only-tests', - 'lexical', - ], - - // Stop ESLint from looking for a configuration file in parent folders - root: true, - // We're stricter than the default config, mostly. We'll override a few rules - // and then enable some React specific ones. - rules: { - 'accessor-pairs': OFF, - - 'brace-style': [ERROR, '1tbs'], - 'consistent-return': OFF, - 'dot-location': [ERROR, 'property'], - // We use console['error']() as a signal to not transform it: - 'dot-notation': [ERROR, {allowPattern: '^(error|warn)$'}], - - 'eol-last': ERROR, - eqeqeq: [ERROR, 'allow-null'], - // Prettier forces semicolons in a few places - 'flowtype/object-type-delimiter': OFF, - - 'flowtype/sort-keys': ERROR, - - 'header/header': [2, 'scripts/www/headerTemplate.js'], - - // (This helps configure simple-import-sort) Make sure all imports are at the top of the file - 'import/first': ERROR, - - // (This helps configure simple-import-sort) Make sure there's a newline after the imports - 'import/newline-after-import': ERROR, - - // (This helps configure simple-import-sort) Merge imports of the same file - 'import/no-duplicates': ERROR, - - indent: OFF, - - 'jsx-quotes': [ERROR, 'prefer-double'], - - 'keyword-spacing': [ERROR, {after: true, before: true}], - - // Enforced by Prettier - // TODO: Prettier doesn't handle long strings or long comments. Not a big - // deal. But I turned it off because loading the plugin causes some obscure - // syntax error and it didn't seem worth investigating. - 'max-len': OFF, - - 'no-bitwise': OFF, - - 'no-console': ERROR, - - 'no-debugger': ERROR, - - // Prevent function declarations after return statements - 'no-function-declare-after-return/no-function-declare-after-return': ERROR, - - 'no-inner-declarations': [ERROR, 'functions'], - - 'no-multi-spaces': ERROR, - - 'no-only-tests/no-only-tests': ERROR, - - 'no-restricted-globals': [ERROR].concat(restrictedGlobals), - - 'no-restricted-syntax': [ERROR, 'WithStatement'], - - 'no-shadow': ERROR, - - 'no-unused-expressions': ERROR, - - 'no-unused-vars': [ERROR, {args: 'none'}], - - 'no-use-before-define': OFF, - - // Flow fails with with non-string literal keys - 'no-useless-computed-key': OFF, - - 'no-useless-concat': OFF, - - // We apply these settings to files that should run on Node. - // They can't use JSX or ES6 modules, and must be in strict mode. - // They can, however, use other ES6 features. - // (Note these rules are overridden later for source files.) - 'no-var': ERROR, - - quotes: [ERROR, 'single', {allowTemplateLiterals: true, avoidEscape: true}], - - // React & JSX - // Our transforms set this automatically - 'react/jsx-boolean-value': [ERROR, 'always'], - - 'react/jsx-no-undef': ERROR, - - // We don't care to do this - 'react/jsx-sort-prop-types': OFF, - - 'react/jsx-tag-spacing': ERROR, - - 'react/jsx-uses-react': ERROR, - - // We don't care to do this - 'react/jsx-wrap-multilines': [ - ERROR, - {assignment: false, declaration: false}, - ], - - 'react/no-is-mounted': OFF, - - // This isn't useful in our test code - 'react/react-in-jsx-scope': ERROR, - - 'react/self-closing-comp': ERROR, - - // This sorts re-exports (`export * from 'foo';`), but not other types of exports. - 'simple-import-sort/exports': ERROR, - - 'simple-import-sort/imports': [ - ERROR, - { - // The default grouping, but with type imports first as a separate group. - // See: https://github.com/lydell/eslint-plugin-simple-import-sort/blob/d9a116f71302c5dcfc1581fc7ded8d77392f1924/examples/.eslintrc.js#L122-L133 - groups: [['^.*\\u0000$'], ['^\\u0000'], ['^@?\\w'], ['^'], ['^\\.']], - }, - ], - - 'sort-keys-fix/sort-keys-fix': ERROR, - - 'space-before-blocks': ERROR, - - 'space-before-function-paren': OFF, - - strict: ERROR, - - 'valid-typeof': [ERROR, {requireStringLiterals: true}], - }, }; diff --git a/packages/blocks-editor/.prettierrc.js b/packages/blocks-editor/.prettierrc.js index 892289423..19e7bc5b0 100644 --- a/packages/blocks-editor/.prettierrc.js +++ b/packages/blocks-editor/.prettierrc.js @@ -1,11 +1,7 @@ -'use strict'; - module.exports = { - bracketSpacing: false, singleQuote: true, - bracketSameLine: true, - printWidth: 80, trailingComma: 'all', - htmlWhitespaceSensitivity: 'ignore', - attributeGroups: ['$DEFAULT', '^data-'], + printWidth: 120, + semi: false, + plugins: [require('prettier-plugin-tailwindcss')], }; diff --git a/packages/blocks-editor/package.json b/packages/blocks-editor/package.json index 4cc1c694b..a1820b281 100644 --- a/packages/blocks-editor/package.json +++ b/packages/blocks-editor/package.json @@ -4,20 +4,25 @@ "private": true, "main": "./src/index.ts", "scripts": { - "tsc": "tsc -p tsconfig.json" + "tsc": "tsc -p tsconfig.json", + "format": "prettier --write src/", + "lint:fix": "eslint src/ --fix" }, "dependencies": { - "@lexical/react": "0.7.5", + "@lexical/react": "0.7.6", "@standardnotes/icons": "workspace:*", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", - "lexical": "0.7.5", + "lexical": "0.7.6", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "eslint": "*", + "eslint-plugin-react": "*", + "eslint-plugin-react-hooks": "*", "prettier": "*", + "prettier-plugin-tailwindcss": "*", "typescript": "*" } } diff --git a/packages/blocks-editor/src/Editor/BlocksEditor.tsx b/packages/blocks-editor/src/Editor/BlocksEditor.tsx index b3fe8c542..750f75fff 100644 --- a/packages/blocks-editor/src/Editor/BlocksEditor.tsx +++ b/packages/blocks-editor/src/Editor/BlocksEditor.tsx @@ -1,41 +1,41 @@ -import {FunctionComponent, useCallback, useState} from 'react'; -import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin'; -import {ContentEditable} from '@lexical/react/LexicalContentEditable'; -import {OnChangePlugin} from '@lexical/react/LexicalOnChangePlugin'; -import {CheckListPlugin} from '@lexical/react/LexicalCheckListPlugin'; -import {ClearEditorPlugin} from '@lexical/react/LexicalClearEditorPlugin'; -import {MarkdownShortcutPlugin} from '@lexical/react/LexicalMarkdownShortcutPlugin'; -import {TablePlugin} from '@lexical/react/LexicalTablePlugin'; -import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary'; -import {HashtagPlugin} from '@lexical/react/LexicalHashtagPlugin'; -import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin'; -import {LinkPlugin} from '@lexical/react/LexicalLinkPlugin'; -import {ListPlugin} from '@lexical/react/LexicalListPlugin'; -import {$getRoot, EditorState, LexicalEditor} from 'lexical'; -import HorizontalRulePlugin from '../Lexical/Plugins/HorizontalRulePlugin'; -import TwitterPlugin from '../Lexical/Plugins/TwitterPlugin'; -import YouTubePlugin from '../Lexical/Plugins/YouTubePlugin'; -import AutoEmbedPlugin from '../Lexical/Plugins/AutoEmbedPlugin'; -import CollapsiblePlugin from '../Lexical/Plugins/CollapsiblePlugin'; -import DraggableBlockPlugin from '../Lexical/Plugins/DraggableBlockPlugin'; -import CodeHighlightPlugin from '../Lexical/Plugins/CodeHighlightPlugin'; -import FloatingTextFormatToolbarPlugin from '../Lexical/Plugins/FloatingTextFormatToolbarPlugin'; -import FloatingLinkEditorPlugin from '../Lexical/Plugins/FloatingLinkEditorPlugin'; -import {TabIndentationPlugin} from '../Lexical/Plugins/TabIndentationPlugin'; -import {truncateString} from './Utils'; -import {SuperEditorContentId} from './Constants'; -import {classNames} from '@standardnotes/utils'; -import {MarkdownTransformers} from './MarkdownTransformers'; +import { FunctionComponent, useCallback, useState } from 'react' +import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin' +import { ContentEditable } from '@lexical/react/LexicalContentEditable' +import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin' +import { CheckListPlugin } from '@lexical/react/LexicalCheckListPlugin' +import { ClearEditorPlugin } from '@lexical/react/LexicalClearEditorPlugin' +import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin' +import { TablePlugin } from '@lexical/react/LexicalTablePlugin' +import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary' +import { HashtagPlugin } from '@lexical/react/LexicalHashtagPlugin' +import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin' +import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin' +import { ListPlugin } from '@lexical/react/LexicalListPlugin' +import { $getRoot, EditorState, LexicalEditor } from 'lexical' +import HorizontalRulePlugin from '../Lexical/Plugins/HorizontalRulePlugin' +import TwitterPlugin from '../Lexical/Plugins/TwitterPlugin' +import YouTubePlugin from '../Lexical/Plugins/YouTubePlugin' +import AutoEmbedPlugin from '../Lexical/Plugins/AutoEmbedPlugin' +import CollapsiblePlugin from '../Lexical/Plugins/CollapsiblePlugin' +import DraggableBlockPlugin from '../Lexical/Plugins/DraggableBlockPlugin' +import CodeHighlightPlugin from '../Lexical/Plugins/CodeHighlightPlugin' +import FloatingTextFormatToolbarPlugin from '../Lexical/Plugins/FloatingTextFormatToolbarPlugin' +import FloatingLinkEditorPlugin from '../Lexical/Plugins/FloatingLinkEditorPlugin' +import { TabIndentationPlugin } from '../Lexical/Plugins/TabIndentationPlugin' +import { truncateString } from './Utils' +import { SuperEditorContentId } from './Constants' +import { classNames } from '@standardnotes/utils' +import { MarkdownTransformers } from './MarkdownTransformers' type BlocksEditorProps = { - onChange?: (value: string, preview: string) => void; - className?: string; - children?: React.ReactNode; - previewLength?: number; - spellcheck?: boolean; - ignoreFirstChange?: boolean; - readonly?: boolean; -}; + onChange?: (value: string, preview: string) => void + className?: string + children?: React.ReactNode + previewLength?: number + spellcheck?: boolean + ignoreFirstChange?: boolean + readonly?: boolean +} export const BlocksEditor: FunctionComponent = ({ onChange, @@ -46,49 +46,50 @@ export const BlocksEditor: FunctionComponent = ({ ignoreFirstChange = false, readonly, }) => { - const [didIgnoreFirstChange, setDidIgnoreFirstChange] = useState(false); + const [didIgnoreFirstChange, setDidIgnoreFirstChange] = useState(false) const handleChange = useCallback( (editorState: EditorState, _editor: LexicalEditor) => { if (ignoreFirstChange && !didIgnoreFirstChange) { - setDidIgnoreFirstChange(true); - return; + setDidIgnoreFirstChange(true) + return } editorState.read(() => { - const childrenNodes = $getRoot().getAllTextNodes().slice(0, 2); - let previewText = ''; + const childrenNodes = $getRoot().getAllTextNodes().slice(0, 2) + let previewText = '' childrenNodes.forEach((node, index) => { - previewText += node.getTextContent(); + previewText += node.getTextContent() if (index !== childrenNodes.length - 1) { - previewText += '\n'; + previewText += '\n' } - }); + }) if (previewLength) { - previewText = truncateString(previewText, previewLength); + previewText = truncateString(previewText, previewLength) } try { - const stringifiedEditorState = JSON.stringify(editorState.toJSON()); - onChange?.(stringifiedEditorState, previewText); + const stringifiedEditorState = JSON.stringify(editorState.toJSON()) + onChange?.(stringifiedEditorState, previewText) } catch (error) { window.alert( `An invalid change was made inside the Super editor. Your change was not saved. Please report this error to the team: ${error}`, - ); + ) } - }); + }) }, + // Ignoring 'ignoreFirstChange' and 'previewLength' + // eslint-disable-next-line react-hooks/exhaustive-deps [onChange, didIgnoreFirstChange], - ); + ) - const [floatingAnchorElem, setFloatingAnchorElem] = - useState(null); + const [floatingAnchorElem, setFloatingAnchorElem] = useState(null) const onRef = (_floatingAnchorElem: HTMLDivElement) => { if (_floatingAnchorElem !== null) { - setFloatingAnchorElem(_floatingAnchorElem); + setFloatingAnchorElem(_floatingAnchorElem) } - }; + } return ( <> @@ -99,10 +100,7 @@ export const BlocksEditor: FunctionComponent = ({
@@ -135,5 +133,5 @@ export const BlocksEditor: FunctionComponent = ({ )} - ); -}; + ) +} diff --git a/packages/blocks-editor/src/Editor/BlocksEditorComposer.tsx b/packages/blocks-editor/src/Editor/BlocksEditorComposer.tsx index 764c108c9..22b75426a 100644 --- a/packages/blocks-editor/src/Editor/BlocksEditorComposer.tsx +++ b/packages/blocks-editor/src/Editor/BlocksEditorComposer.tsx @@ -1,19 +1,22 @@ -import {FunctionComponent} from 'react'; -import {LexicalComposer} from '@lexical/react/LexicalComposer'; -import BlocksEditorTheme from '../Lexical/Theme/Theme'; -import {BlockEditorNodes} from '../Lexical/Nodes/AllNodes'; -import {Klass, LexicalNode} from 'lexical'; +import { FunctionComponent } from 'react' +import { LexicalComposer } from '@lexical/react/LexicalComposer' +import BlocksEditorTheme from '../Lexical/Theme/Theme' +import { BlockEditorNodes } from '../Lexical/Nodes/AllNodes' +import { Klass, LexicalNode } from 'lexical' type BlocksEditorComposerProps = { - initialValue: string | undefined; - children: React.ReactNode; - nodes?: Array>; - readonly?: boolean; -}; + initialValue: string | undefined + children: React.ReactNode + nodes?: Array> + readonly?: boolean +} -export const BlocksEditorComposer: FunctionComponent< - BlocksEditorComposerProps -> = ({initialValue, children, readonly, nodes = []}) => { +export const BlocksEditorComposer: FunctionComponent = ({ + initialValue, + children, + readonly, + nodes = [], +}) => { return ( console.error(error), - editorState: - initialValue && initialValue.length > 0 ? initialValue : undefined, + editorState: initialValue && initialValue.length > 0 ? initialValue : undefined, nodes: [...nodes, ...BlockEditorNodes], - }}> + }} + > <>{children} - ); -}; + ) +} diff --git a/packages/blocks-editor/src/Editor/Constants.ts b/packages/blocks-editor/src/Editor/Constants.ts index 8fc5ec40a..785a88a50 100644 --- a/packages/blocks-editor/src/Editor/Constants.ts +++ b/packages/blocks-editor/src/Editor/Constants.ts @@ -1 +1 @@ -export const SuperEditorContentId = 'super-editor-content'; +export const SuperEditorContentId = 'super-editor-content' diff --git a/packages/blocks-editor/src/Editor/MarkdownTransformers.ts b/packages/blocks-editor/src/Editor/MarkdownTransformers.ts index 96c76c383..50eadc1de 100644 --- a/packages/blocks-editor/src/Editor/MarkdownTransformers.ts +++ b/packages/blocks-editor/src/Editor/MarkdownTransformers.ts @@ -1,13 +1,8 @@ -import { - CHECK_LIST, - ELEMENT_TRANSFORMERS, - TEXT_FORMAT_TRANSFORMERS, - TEXT_MATCH_TRANSFORMERS, -} from '@lexical/markdown'; +import { CHECK_LIST, ELEMENT_TRANSFORMERS, TEXT_FORMAT_TRANSFORMERS, TEXT_MATCH_TRANSFORMERS } from '@lexical/markdown' export const MarkdownTransformers = [ CHECK_LIST, ...ELEMENT_TRANSFORMERS, ...TEXT_FORMAT_TRANSFORMERS, ...TEXT_MATCH_TRANSFORMERS, -]; +] diff --git a/packages/blocks-editor/src/Editor/Utils.ts b/packages/blocks-editor/src/Editor/Utils.ts index ae4fec479..6101a2247 100644 --- a/packages/blocks-editor/src/Editor/Utils.ts +++ b/packages/blocks-editor/src/Editor/Utils.ts @@ -1,7 +1,7 @@ export function truncateString(string: string, limit: number) { if (string.length <= limit) { - return string; + return string } else { - return string.substring(0, limit) + '...'; + return string.substring(0, limit) + '...' } } diff --git a/packages/blocks-editor/src/Lexical/Hooks/useModal.tsx b/packages/blocks-editor/src/Lexical/Hooks/useModal.tsx index 3b7dfba8d..310c0e830 100644 --- a/packages/blocks-editor/src/Lexical/Hooks/useModal.tsx +++ b/packages/blocks-editor/src/Lexical/Hooks/useModal.tsx @@ -6,38 +6,35 @@ * */ -import {useCallback, useMemo, useState} from 'react'; +import { useCallback, useMemo, useState } from 'react' -import Modal from '../UI/Modal'; +import Modal from '../UI/Modal' export default function useModal(): [ JSX.Element | null, (title: string, showModal: (onClose: () => void) => JSX.Element) => void, ] { const [modalContent, setModalContent] = useState(null); + closeOnClickOutside: boolean + content: JSX.Element + title: string + }>(null) const onClose = useCallback(() => { - setModalContent(null); - }, []); + setModalContent(null) + }, []) const modal = useMemo(() => { if (modalContent === null) { - return null; + return null } - const {title, content, closeOnClickOutside} = modalContent; + const { title, content, closeOnClickOutside } = modalContent return ( - + {content} - ); - }, [modalContent, onClose]); + ) + }, [modalContent, onClose]) const showModal = useCallback( ( @@ -50,10 +47,10 @@ export default function useModal(): [ closeOnClickOutside, content: getContent(onClose), title, - }); + }) }, [onClose], - ); + ) - return [modal, showModal]; + return [modal, showModal] } diff --git a/packages/blocks-editor/src/Lexical/Nodes/AllNodes.ts b/packages/blocks-editor/src/Lexical/Nodes/AllNodes.ts index 93c0a043e..5db562882 100644 --- a/packages/blocks-editor/src/Lexical/Nodes/AllNodes.ts +++ b/packages/blocks-editor/src/Lexical/Nodes/AllNodes.ts @@ -1,17 +1,17 @@ -import {CodeHighlightNode, CodeNode} from '@lexical/code'; -import {HashtagNode} from '@lexical/hashtag'; -import {AutoLinkNode, LinkNode} from '@lexical/link'; -import {ListItemNode, ListNode} from '@lexical/list'; -import {MarkNode} from '@lexical/mark'; -import {OverflowNode} from '@lexical/overflow'; -import {HorizontalRuleNode} from '@lexical/react/LexicalHorizontalRuleNode'; -import {HeadingNode, QuoteNode} from '@lexical/rich-text'; -import {TableCellNode, TableNode, TableRowNode} from '@lexical/table'; -import {TweetNode} from './TweetNode'; -import {YouTubeNode} from './YouTubeNode'; -import {CollapsibleContainerNode} from '../Plugins/CollapsiblePlugin/CollapsibleContainerNode'; -import {CollapsibleContentNode} from '../Plugins/CollapsiblePlugin/CollapsibleContentNode'; -import {CollapsibleTitleNode} from '../Plugins/CollapsiblePlugin/CollapsibleTitleNode'; +import { CodeHighlightNode, CodeNode } from '@lexical/code' +import { HashtagNode } from '@lexical/hashtag' +import { AutoLinkNode, LinkNode } from '@lexical/link' +import { ListItemNode, ListNode } from '@lexical/list' +import { MarkNode } from '@lexical/mark' +import { OverflowNode } from '@lexical/overflow' +import { HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode' +import { HeadingNode, QuoteNode } from '@lexical/rich-text' +import { TableCellNode, TableNode, TableRowNode } from '@lexical/table' +import { TweetNode } from './TweetNode' +import { YouTubeNode } from './YouTubeNode' +import { CollapsibleContainerNode } from '../Plugins/CollapsiblePlugin/CollapsibleContainerNode' +import { CollapsibleContentNode } from '../Plugins/CollapsiblePlugin/CollapsibleContentNode' +import { CollapsibleTitleNode } from '../Plugins/CollapsiblePlugin/CollapsibleTitleNode' export const BlockEditorNodes = [ AutoLinkNode, @@ -34,4 +34,4 @@ export const BlockEditorNodes = [ TableRowNode, TweetNode, YouTubeNode, -]; +] diff --git a/packages/blocks-editor/src/Lexical/Nodes/TableComponent.tsx b/packages/blocks-editor/src/Lexical/Nodes/TableComponent.tsx index 995cebead..f8c8bf693 100644 --- a/packages/blocks-editor/src/Lexical/Nodes/TableComponent.tsx +++ b/packages/blocks-editor/src/Lexical/Nodes/TableComponent.tsx @@ -10,12 +10,12 @@ import { $generateJSONFromSelectedNodes, $generateNodesFromSerializedNodes, $insertGeneratedNodes, -} from '@lexical/clipboard'; -import {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html'; -import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; -import {LexicalNestedComposer} from '@lexical/react/LexicalNestedComposer'; -import {useLexicalNodeSelection} from '@lexical/react/useLexicalNodeSelection'; -import {mergeRegister} from '@lexical/utils'; +} from '@lexical/clipboard' +import { $generateHtmlFromNodes, $generateNodesFromDOM } from '@lexical/html' +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import { LexicalNestedComposer } from '@lexical/react/LexicalNestedComposer' +import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection' +import { mergeRegister } from '@lexical/utils' import { RangeSelection, TextFormatType, @@ -46,19 +46,12 @@ import { LexicalEditor, NodeKey, PASTE_COMMAND, -} from 'lexical'; -import { - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import {createPortal} from 'react-dom'; -import {IS_APPLE} from '../Shared/environment'; +} from 'lexical' +import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react' +import { createPortal } from 'react-dom' +import { IS_APPLE } from '../Shared/environment' -import {CellContext} from '../Plugins/TablePlugin'; +import { CellContext } from '../Plugins/TablePlugin' import { $isTableNode, Cell, @@ -70,123 +63,105 @@ import { extractRowsFromHTML, Rows, TableNode, -} from './TableNode'; +} from './TableNode' -type SortOptions = {type: 'ascending' | 'descending'; x: number}; +type SortOptions = { type: 'ascending' | 'descending'; x: number } -const NO_CELLS: [] = []; +const NO_CELLS: [] = [] function $createSelectAll(): RangeSelection { - const sel = $createRangeSelection(); - sel.focus.set('root', $getRoot().getChildrenSize(), 'element'); - return sel; + const sel = $createRangeSelection() + sel.focus.set('root', $getRoot().getChildrenSize(), 'element') + return sel } function createEmptyParagraphHTML(theme: EditorThemeClasses): string { - return `


`; + return `


` } function focusCell(tableElem: HTMLElement, id: string): void { - const cellElem = tableElem.querySelector(`[data-id=${id}]`) as HTMLElement; + const cellElem = tableElem.querySelector(`[data-id=${id}]`) as HTMLElement if (cellElem == null) { - return; + return } - cellElem.focus(); + cellElem.focus() } function isStartingResize(target: HTMLElement): boolean { - return target.nodeType === 1 && target.hasAttribute('data-table-resize'); + return target.nodeType === 1 && target.hasAttribute('data-table-resize') } -function generateHTMLFromJSON( - editorStateJSON: string, - cellEditor: LexicalEditor, -): string { - const editorState = cellEditor.parseEditorState(editorStateJSON); - let html = cellHTMLCache.get(editorStateJSON); +function generateHTMLFromJSON(editorStateJSON: string, cellEditor: LexicalEditor): string { + const editorState = cellEditor.parseEditorState(editorStateJSON) + let html = cellHTMLCache.get(editorStateJSON) if (html === undefined) { - html = editorState.read(() => $generateHtmlFromNodes(cellEditor, null)); - const textContent = editorState.read(() => $getRoot().getTextContent()); - cellHTMLCache.set(editorStateJSON, html); - cellTextContentCache.set(editorStateJSON, textContent); + html = editorState.read(() => $generateHtmlFromNodes(cellEditor, null)) + const textContent = editorState.read(() => $getRoot().getTextContent()) + cellHTMLCache.set(editorStateJSON, html) + cellTextContentCache.set(editorStateJSON, textContent) } - return html; + return html } function getCurrentDocument(editor: LexicalEditor): Document { - const rootElement = editor.getRootElement(); - return rootElement !== null ? rootElement.ownerDocument : document; + const rootElement = editor.getRootElement() + return rootElement !== null ? rootElement.ownerDocument : document } -function isCopy( - keyCode: number, - shiftKey: boolean, - metaKey: boolean, - ctrlKey: boolean, -): boolean { +function isCopy(keyCode: number, shiftKey: boolean, metaKey: boolean, ctrlKey: boolean): boolean { if (shiftKey) { - return false; + return false } if (keyCode === 67) { - return IS_APPLE ? metaKey : ctrlKey; + return IS_APPLE ? metaKey : ctrlKey } - return false; + return false } -function isCut( - keyCode: number, - shiftKey: boolean, - metaKey: boolean, - ctrlKey: boolean, -): boolean { +function isCut(keyCode: number, shiftKey: boolean, metaKey: boolean, ctrlKey: boolean): boolean { if (shiftKey) { - return false; + return false } if (keyCode === 88) { - return IS_APPLE ? metaKey : ctrlKey; + return IS_APPLE ? metaKey : ctrlKey } - return false; + return false } -function isPaste( - keyCode: number, - shiftKey: boolean, - metaKey: boolean, - ctrlKey: boolean, -): boolean { +function isPaste(keyCode: number, shiftKey: boolean, metaKey: boolean, ctrlKey: boolean): boolean { if (shiftKey) { - return false; + return false } if (keyCode === 86) { - return IS_APPLE ? metaKey : ctrlKey; + return IS_APPLE ? metaKey : ctrlKey } - return false; + return false } function getCellID(domElement: HTMLElement): null | string { - let node: null | HTMLElement = domElement; + let node: null | HTMLElement = domElement while (node !== null) { - const possibleID = node.getAttribute('data-id'); + const possibleID = node.getAttribute('data-id') if (possibleID != null) { - return possibleID; + return possibleID } - node = node.parentElement; + node = node.parentElement } - return null; + return null } function getTableCellWidth(domElement: HTMLElement): number { - let node: null | HTMLElement = domElement; + let node: null | HTMLElement = domElement while (node !== null) { if (node.nodeName === 'TH' || node.nodeName === 'TD') { - return node.getBoundingClientRect().width; + return node.getBoundingClientRect().width } - node = node.parentElement; + node = node.parentElement } - return 0; + return 0 } function $updateCells( @@ -198,60 +173,56 @@ function $updateCells( fn: () => void, ): void { for (const id of ids) { - const cell = getCell(rows, id, cellCoordMap); + const cell = getCell(rows, id, cellCoordMap) if (cell !== null && cellEditor !== null) { - const editorState = cellEditor.parseEditorState(cell.json); - cellEditor._headless = true; - cellEditor.setEditorState(editorState); - cellEditor.update(fn, {discrete: true}); - cellEditor._headless = false; - const newJSON = JSON.stringify(cellEditor.getEditorState()); + const editorState = cellEditor.parseEditorState(cell.json) + cellEditor._headless = true + cellEditor.setEditorState(editorState) + cellEditor.update(fn, { discrete: true }) + cellEditor._headless = false + const newJSON = JSON.stringify(cellEditor.getEditorState()) updateTableNode((tableNode) => { - const [x, y] = cellCoordMap.get(id) as [number, number]; - $addUpdateTag('history-push'); - tableNode.updateCellJSON(x, y, newJSON); - }); + const [x, y] = cellCoordMap.get(id) as [number, number] + $addUpdateTag('history-push') + tableNode.updateCellJSON(x, y, newJSON) + }) } } } function isTargetOnPossibleUIControl(target: HTMLElement): boolean { - let node: HTMLElement | null = target; + let node: HTMLElement | null = target while (node !== null) { - const nodeName = node.nodeName; - if ( - nodeName === 'BUTTON' || - nodeName === 'INPUT' || - nodeName === 'TEXTAREA' - ) { - return true; + const nodeName = node.nodeName + if (nodeName === 'BUTTON' || nodeName === 'INPUT' || nodeName === 'TEXTAREA') { + return true } - node = node.parentElement; + node = node.parentElement } - return false; + return false } function getSelectedRect( startID: string, endID: string, cellCoordMap: Map, -): null | {startX: number; endX: number; startY: number; endY: number} { - const startCoords = cellCoordMap.get(startID); - const endCoords = cellCoordMap.get(endID); +): null | { startX: number; endX: number; startY: number; endY: number } { + const startCoords = cellCoordMap.get(startID) + const endCoords = cellCoordMap.get(endID) if (startCoords === undefined || endCoords === undefined) { - return null; + return null } - const startX = Math.min(startCoords[0], endCoords[0]); - const endX = Math.max(startCoords[0], endCoords[0]); - const startY = Math.min(startCoords[1], endCoords[1]); - const endY = Math.max(startCoords[1], endCoords[1]); + const startX = Math.min(startCoords[0], endCoords[0]) + const endX = Math.max(startCoords[0], endCoords[0]) + const startY = Math.min(startCoords[1], endCoords[1]) + const endY = Math.max(startCoords[1], endCoords[1]) return { endX, endY, startX, startY, - }; + } } function getSelectedIDs( @@ -260,46 +231,43 @@ function getSelectedIDs( endID: string, cellCoordMap: Map, ): Array { - const rect = getSelectedRect(startID, endID, cellCoordMap); + const rect = getSelectedRect(startID, endID, cellCoordMap) if (rect === null) { - return []; + return [] } - const {startX, endY, endX, startY} = rect; - const ids = []; + const { startX, endY, endX, startY } = rect + const ids = [] for (let x = startX; x <= endX; x++) { for (let y = startY; y <= endY; y++) { - ids.push(rows[y].cells[x].id); + ids.push(rows[y].cells[x].id) } } - return ids; + return ids } -function extractCellsFromRows( - rows: Rows, - rect: {startX: number; endX: number; startY: number; endY: number}, -): Rows { - const {startX, endY, endX, startY} = rect; - const newRows: Rows = []; +function extractCellsFromRows(rows: Rows, rect: { startX: number; endX: number; startY: number; endY: number }): Rows { + const { startX, endY, endX, startY } = rect + const newRows: Rows = [] for (let y = startY; y <= endY; y++) { - const row = rows[y]; - const newRow = createRow(); + const row = rows[y] + const newRow = createRow() for (let x = startX; x <= endX; x++) { - const cellClone = {...row.cells[x]}; - cellClone.id = createUID(); - newRow.cells.push(cellClone); + const cellClone = { ...row.cells[x] } + cellClone.id = createUID() + newRow.cells.push(cellClone) } - newRows.push(newRow); + newRows.push(newRow) } - return newRows; + return newRows } -function TableCellEditor({cellEditor}: {cellEditor: LexicalEditor}) { - const {cellEditorConfig, cellEditorPlugins} = useContext(CellContext); +function TableCellEditor({ cellEditor }: { cellEditor: LexicalEditor }) { + const { cellEditorConfig, cellEditorPlugins } = useContext(CellContext) if (cellEditorPlugins === null || cellEditorConfig === null) { - return null; + return null } return ( @@ -307,24 +275,21 @@ function TableCellEditor({cellEditor}: {cellEditor: LexicalEditor}) { initialEditor={cellEditor} initialTheme={cellEditorConfig.theme} initialNodes={cellEditorConfig.nodes} - skipCollabChecks={true}> + skipCollabChecks={true} + > {cellEditorPlugins} - ); + ) } -function getCell( - rows: Rows, - cellID: string, - cellCoordMap: Map, -): null | Cell { - const coords = cellCoordMap.get(cellID); +function getCell(rows: Rows, cellID: string, cellCoordMap: Map): null | Cell { + const coords = cellCoordMap.get(cellID) if (coords === undefined) { - return null; + return null } - const [x, y] = coords; - const row = rows[y]; - return row.cells[x]; + const [x, y] = coords + const row = rows[y] + return row.cells[x] } function TableActionMenu({ @@ -338,92 +303,85 @@ function TableActionMenu({ setSortingOptions, sortingOptions, }: { - cell: Cell; - menuElem: HTMLElement; - updateCellsByID: (ids: Array, fn: () => void) => void; - onClose: () => void; - updateTableNode: (fn2: (tableNode: TableNode) => void) => void; - cellCoordMap: Map; - rows: Rows; - setSortingOptions: (options: null | SortOptions) => void; - sortingOptions: null | SortOptions; + cell: Cell + menuElem: HTMLElement + updateCellsByID: (ids: Array, fn: () => void) => void + onClose: () => void + updateTableNode: (fn2: (tableNode: TableNode) => void) => void + cellCoordMap: Map + rows: Rows + setSortingOptions: (options: null | SortOptions) => void + sortingOptions: null | SortOptions }) { - const dropDownRef = useRef(null); + const dropDownRef = useRef(null) useEffect(() => { - const dropdownElem = dropDownRef.current; + const dropdownElem = dropDownRef.current if (dropdownElem !== null) { - const rect = menuElem.getBoundingClientRect(); - dropdownElem.style.top = `${rect.y}px`; - dropdownElem.style.left = `${rect.x}px`; + const rect = menuElem.getBoundingClientRect() + dropdownElem.style.top = `${rect.y}px` + dropdownElem.style.left = `${rect.x}px` } - }, [menuElem]); + }, [menuElem]) useEffect(() => { const handleClickOutside = (event: MouseEvent) => { - const dropdownElem = dropDownRef.current; - if ( - dropdownElem !== null && - !dropdownElem.contains(event.target as Node) - ) { - event.stopPropagation(); + const dropdownElem = dropDownRef.current + if (dropdownElem !== null && !dropdownElem.contains(event.target as Node)) { + event.stopPropagation() } - }; + } - window.addEventListener('click', handleClickOutside); - return () => window.removeEventListener('click', handleClickOutside); - }, [onClose]); - const coords = cellCoordMap.get(cell.id); + window.addEventListener('click', handleClickOutside) + return () => window.removeEventListener('click', handleClickOutside) + }, [onClose]) + const coords = cellCoordMap.get(cell.id) if (coords === undefined) { - return null; + return null } - const [x, y] = coords; + const [x, y] = coords return ( - // eslint-disable-next-line jsx-a11y/no-static-element-interactions
{ - e.stopPropagation(); + e.stopPropagation() }} onPointerDown={(e) => { - e.stopPropagation(); + e.stopPropagation() }} onPointerUp={(e) => { - e.stopPropagation(); + e.stopPropagation() }} onClick={(e) => { - e.stopPropagation(); - }}> + e.stopPropagation() + }} + >
@@ -433,33 +391,32 @@ function TableActionMenu({ )} - {(sortingOptions === null || - sortingOptions.x !== x || - sortingOptions.type === 'descending') && ( + {(sortingOptions === null || sortingOptions.x !== x || sortingOptions.type === 'descending') && ( )} - {(sortingOptions === null || - sortingOptions.x !== x || - sortingOptions.type === 'ascending') && ( + {(sortingOptions === null || sortingOptions.x !== x || sortingOptions.type === 'ascending') && ( )} @@ -470,22 +427,24 @@ function TableActionMenu({ className="item" onClick={() => { updateTableNode((tableNode) => { - $addUpdateTag('history-push'); - tableNode.insertRowAt(y); - }); - onClose(); - }}> + $addUpdateTag('history-push') + tableNode.insertRowAt(y) + }) + onClose() + }} + > Insert row above
@@ -493,22 +452,24 @@ function TableActionMenu({ className="item" onClick={() => { updateTableNode((tableNode) => { - $addUpdateTag('history-push'); - tableNode.insertColumnAt(x); - }); - onClose(); - }}> + $addUpdateTag('history-push') + tableNode.insertColumnAt(x) + }) + onClose() + }} + > Insert column left
@@ -517,11 +478,12 @@ function TableActionMenu({ className="item" onClick={() => { updateTableNode((tableNode) => { - $addUpdateTag('history-push'); - tableNode.deleteColumnAt(x); - }); - onClose(); - }}> + $addUpdateTag('history-push') + tableNode.deleteColumnAt(x) + }) + onClose() + }} + > Delete column )} @@ -530,11 +492,12 @@ function TableActionMenu({ className="item" onClick={() => { updateTableNode((tableNode) => { - $addUpdateTag('history-push'); - tableNode.deleteRowAt(y); - }); - onClose(); - }}> + $addUpdateTag('history-push') + tableNode.deleteRowAt(y) + }) + onClose() + }} + > Delete row )} @@ -542,16 +505,17 @@ function TableActionMenu({ className="item" onClick={() => { updateTableNode((tableNode) => { - $addUpdateTag('history-push'); - tableNode.selectNext(); - tableNode.remove(); - }); - onClose(); - }}> + $addUpdateTag('history-push') + tableNode.selectNext() + tableNode.remove() + }) + onClose() + }} + > Delete table
- ); + ) } function TableCell({ @@ -568,38 +532,34 @@ function TableCell({ setSortingOptions, sortingOptions, }: { - cell: Cell; - isEditing: boolean; - isSelected: boolean; - isPrimarySelected: boolean; - theme: EditorThemeClasses; - cellEditor: LexicalEditor; - updateCellsByID: (ids: Array, fn: () => void) => void; - updateTableNode: (fn2: (tableNode: TableNode) => void) => void; - cellCoordMap: Map; - rows: Rows; - setSortingOptions: (options: null | SortOptions) => void; - sortingOptions: null | SortOptions; + cell: Cell + isEditing: boolean + isSelected: boolean + isPrimarySelected: boolean + theme: EditorThemeClasses + cellEditor: LexicalEditor + updateCellsByID: (ids: Array, fn: () => void) => void + updateTableNode: (fn2: (tableNode: TableNode) => void) => void + cellCoordMap: Map + rows: Rows + setSortingOptions: (options: null | SortOptions) => void + sortingOptions: null | SortOptions }) { - const [showMenu, setShowMenu] = useState(false); - const menuRootRef = useRef(null); - const isHeader = cell.type !== 'normal'; - const editorStateJSON = cell.json; - const CellComponent = isHeader ? 'th' : 'td'; - const cellWidth = cell.width; - const menuElem = menuRootRef.current; - const coords = cellCoordMap.get(cell.id); - const isSorted = - sortingOptions !== null && - coords !== undefined && - coords[0] === sortingOptions.x && - coords[1] === 0; + const [showMenu, setShowMenu] = useState(false) + const menuRootRef = useRef(null) + const isHeader = cell.type !== 'normal' + const editorStateJSON = cell.json + const CellComponent = isHeader ? 'th' : 'td' + const cellWidth = cell.width + const menuElem = menuRootRef.current + const coords = cellCoordMap.get(cell.id) + const isSorted = sortingOptions !== null && coords !== undefined && coords[0] === sortingOptions.x && coords[1] === 0 useEffect(() => { if (isEditing || !isPrimarySelected) { - setShowMenu(false); + setShowMenu(false) } - }, [isEditing, isPrimarySelected]); + }, [isEditing, isPrimarySelected]) return ( + style={{ width: cellWidth !== null ? cellWidth : undefined }} + > {isPrimarySelected && ( -
+
)} {isPrimarySelected && isEditing ? ( ) : ( <>
{ - setShowMenu(!showMenu); - e.stopPropagation(); - }}> + setShowMenu(!showMenu) + e.stopPropagation() + }} + >
@@ -662,7 +620,7 @@ function TableCell({ )} {isSorted &&
} - ); + ) } export default function TableComponent({ @@ -670,331 +628,289 @@ export default function TableComponent({ rows: rawRows, theme, }: { - nodeKey: NodeKey; - rows: Rows; - theme: EditorThemeClasses; + nodeKey: NodeKey + rows: Rows + theme: EditorThemeClasses }) { - const [isSelected, setSelected, clearSelection] = - useLexicalNodeSelection(nodeKey); - const resizeMeasureRef = useRef<{size: number; point: number}>({ + const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey) + const resizeMeasureRef = useRef<{ size: number; point: number }>({ point: 0, size: 0, - }); - const [sortingOptions, setSortingOptions] = useState( - null, - ); - const addRowsRef = useRef(null); - const lastCellIDRef = useRef(null); - const tableResizerRulerRef = useRef(null); - const {cellEditorConfig} = useContext(CellContext); - const [isEditing, setIsEditing] = useState(false); - const [showAddColumns, setShowAddColumns] = useState(false); - const [showAddRows, setShowAddRows] = useState(false); - const [editor] = useLexicalComposerContext(); - const mouseDownRef = useRef(false); - const [resizingID, setResizingID] = useState(null); - const tableRef = useRef(null); + }) + const [sortingOptions, setSortingOptions] = useState(null) + const addRowsRef = useRef(null) + const lastCellIDRef = useRef(null) + const tableResizerRulerRef = useRef(null) + const { cellEditorConfig } = useContext(CellContext) + const [isEditing, setIsEditing] = useState(false) + const [showAddColumns, setShowAddColumns] = useState(false) + const [showAddRows, setShowAddRows] = useState(false) + const [editor] = useLexicalComposerContext() + const mouseDownRef = useRef(false) + const [resizingID, setResizingID] = useState(null) + const tableRef = useRef(null) const cellCoordMap = useMemo(() => { - const map = new Map(); + const map = new Map() for (let y = 0; y < rawRows.length; y++) { - const row = rawRows[y]; - const cells = row.cells; + const row = rawRows[y] + const cells = row.cells for (let x = 0; x < cells.length; x++) { - const cell = cells[x]; - map.set(cell.id, [x, y]); + const cell = cells[x] + map.set(cell.id, [x, y]) } } - return map; - }, [rawRows]); + return map + }, [rawRows]) const rows = useMemo(() => { if (sortingOptions === null) { - return rawRows; + return rawRows } - const _rows = rawRows.slice(1); + const _rows = rawRows.slice(1) _rows.sort((a, b) => { - const aCells = a.cells; - const bCells = b.cells; - const x = sortingOptions.x; - const aContent = cellTextContentCache.get(aCells[x].json) || ''; - const bContent = cellTextContentCache.get(bCells[x].json) || ''; + const aCells = a.cells + const bCells = b.cells + const x = sortingOptions.x + const aContent = cellTextContentCache.get(aCells[x].json) || '' + const bContent = cellTextContentCache.get(bCells[x].json) || '' if (aContent === '' || bContent === '') { - return 1; + return 1 } if (sortingOptions.type === 'ascending') { - return aContent.localeCompare(bContent); + return aContent.localeCompare(bContent) } - return bContent.localeCompare(aContent); - }); - _rows.unshift(rawRows[0]); - return _rows; - }, [rawRows, sortingOptions]); - const [primarySelectedCellID, setPrimarySelectedCellID] = useState< - null | string - >(null); + return bContent.localeCompare(aContent) + }) + _rows.unshift(rawRows[0]) + return _rows + }, [rawRows, sortingOptions]) + const [primarySelectedCellID, setPrimarySelectedCellID] = useState(null) const cellEditor = useMemo(() => { if (cellEditorConfig === null) { - return null; + return null } const _cellEditor = createEditor({ namespace: cellEditorConfig.namespace, nodes: cellEditorConfig.nodes, onError: (error) => cellEditorConfig.onError(error, _cellEditor), theme: cellEditorConfig.theme, - }); - return _cellEditor; - }, [cellEditorConfig]); - const [selectedCellIDs, setSelectedCellIDs] = useState>([]); - const selectedCellSet = useMemo>( - () => new Set(selectedCellIDs), - [selectedCellIDs], - ); + }) + return _cellEditor + }, [cellEditorConfig]) + const [selectedCellIDs, setSelectedCellIDs] = useState>([]) + const selectedCellSet = useMemo>(() => new Set(selectedCellIDs), [selectedCellIDs]) useEffect(() => { - const tableElem = tableRef.current; - if ( - isSelected && - document.activeElement === document.body && - tableElem !== null - ) { - tableElem.focus(); + const tableElem = tableRef.current + if (isSelected && document.activeElement === document.body && tableElem !== null) { + tableElem.focus() } - }, [isSelected]); + }, [isSelected]) const updateTableNode = useCallback( (fn: (tableNode: TableNode) => void) => { editor.update(() => { - const tableNode = $getNodeByKey(nodeKey); + const tableNode = $getNodeByKey(nodeKey) if ($isTableNode(tableNode)) { - fn(tableNode); + fn(tableNode) } - }); + }) }, [editor, nodeKey], - ); + ) const addColumns = () => { updateTableNode((tableNode) => { - $addUpdateTag('history-push'); - tableNode.addColumns(1); - }); - }; + $addUpdateTag('history-push') + tableNode.addColumns(1) + }) + } const addRows = () => { updateTableNode((tableNode) => { - $addUpdateTag('history-push'); - tableNode.addRows(1); - }); - }; + $addUpdateTag('history-push') + tableNode.addRows(1) + }) + } const modifySelectedCells = useCallback( (x: number, y: number, extend: boolean) => { - const id = rows[y].cells[x].id; - lastCellIDRef.current = id; + const id = rows[y].cells[x].id + lastCellIDRef.current = id if (extend) { - const selectedIDs = getSelectedIDs( - rows, - primarySelectedCellID as string, - id, - cellCoordMap, - ); - setSelectedCellIDs(selectedIDs); + const selectedIDs = getSelectedIDs(rows, primarySelectedCellID as string, id, cellCoordMap) + setSelectedCellIDs(selectedIDs) } else { - setPrimarySelectedCellID(id); - setSelectedCellIDs(NO_CELLS); - focusCell(tableRef.current as HTMLElement, id); + setPrimarySelectedCellID(id) + setSelectedCellIDs(NO_CELLS) + focusCell(tableRef.current as HTMLElement, id) } }, [cellCoordMap, primarySelectedCellID, rows], - ); + ) const saveEditorToJSON = useCallback(() => { if (cellEditor !== null && primarySelectedCellID !== null) { - const json = JSON.stringify(cellEditor.getEditorState()); + const json = JSON.stringify(cellEditor.getEditorState()) updateTableNode((tableNode) => { - const coords = cellCoordMap.get(primarySelectedCellID); + const coords = cellCoordMap.get(primarySelectedCellID) if (coords === undefined) { - return; + return } - $addUpdateTag('history-push'); - const [x, y] = coords; - tableNode.updateCellJSON(x, y, json); - }); + $addUpdateTag('history-push') + const [x, y] = coords + tableNode.updateCellJSON(x, y, json) + }) } - }, [cellCoordMap, cellEditor, primarySelectedCellID, updateTableNode]); + }, [cellCoordMap, cellEditor, primarySelectedCellID, updateTableNode]) const selectTable = useCallback(() => { setTimeout(() => { - const parentRootElement = editor.getRootElement(); + const parentRootElement = editor.getRootElement() if (parentRootElement !== null) { - parentRootElement.focus({preventScroll: true}); - window.getSelection()?.removeAllRanges(); + parentRootElement.focus({ preventScroll: true }) + window.getSelection()?.removeAllRanges() } - }, 20); - }, [editor]); + }, 20) + }, [editor]) useEffect(() => { - const tableElem = tableRef.current; + const tableElem = tableRef.current if (tableElem === null) { - return; + return } - const doc = getCurrentDocument(editor); + const doc = getCurrentDocument(editor) const isAtEdgeOfTable = (event: PointerEvent) => { - const x = event.clientX - tableRect.x; - const y = event.clientY - tableRect.y; - return x < 5 || y < 5; - }; + const x = event.clientX - tableRect.x + const y = event.clientY - tableRect.y + return x < 5 || y < 5 + } const handlePointerDown = (event: PointerEvent) => { - const possibleID = getCellID(event.target as HTMLElement); - if ( - possibleID !== null && - editor.isEditable() && - tableElem.contains(event.target as HTMLElement) - ) { + const possibleID = getCellID(event.target as HTMLElement) + if (possibleID !== null && editor.isEditable() && tableElem.contains(event.target as HTMLElement)) { if (isAtEdgeOfTable(event)) { - setSelected(true); - setPrimarySelectedCellID(null); - selectTable(); - return; + setSelected(true) + setPrimarySelectedCellID(null) + selectTable() + return } - setSelected(false); + setSelected(false) if (isStartingResize(event.target as HTMLElement)) { - setResizingID(possibleID); - tableElem.style.userSelect = 'none'; + setResizingID(possibleID) + tableElem.style.userSelect = 'none' resizeMeasureRef.current = { point: event.clientX, size: getTableCellWidth(event.target as HTMLElement), - }; - return; + } + return } - mouseDownRef.current = true; + mouseDownRef.current = true if (primarySelectedCellID !== possibleID) { if (isEditing) { - saveEditorToJSON(); + saveEditorToJSON() } - setPrimarySelectedCellID(possibleID); - setIsEditing(false); - lastCellIDRef.current = possibleID; + setPrimarySelectedCellID(possibleID) + setIsEditing(false) + lastCellIDRef.current = possibleID } else { - lastCellIDRef.current = null; + lastCellIDRef.current = null } - setSelectedCellIDs(NO_CELLS); - } else if ( - primarySelectedCellID !== null && - !isTargetOnPossibleUIControl(event.target as HTMLElement) - ) { - setSelected(false); - mouseDownRef.current = false; + setSelectedCellIDs(NO_CELLS) + } else if (primarySelectedCellID !== null && !isTargetOnPossibleUIControl(event.target as HTMLElement)) { + setSelected(false) + mouseDownRef.current = false if (isEditing) { - saveEditorToJSON(); + saveEditorToJSON() } - setPrimarySelectedCellID(null); - setSelectedCellIDs(NO_CELLS); - setIsEditing(false); - lastCellIDRef.current = null; + setPrimarySelectedCellID(null) + setSelectedCellIDs(NO_CELLS) + setIsEditing(false) + lastCellIDRef.current = null } - }; + } - const tableRect = tableElem.getBoundingClientRect(); + const tableRect = tableElem.getBoundingClientRect() const handlePointerMove = (event: PointerEvent) => { if (resizingID !== null) { - const tableResizerRulerElem = tableResizerRulerRef.current; + const tableResizerRulerElem = tableResizerRulerRef.current if (tableResizerRulerElem !== null) { - const {size, point} = resizeMeasureRef.current; - const diff = event.clientX - point; - const newWidth = size + diff; - let x = event.clientX - tableRect.x; + const { size, point } = resizeMeasureRef.current + const diff = event.clientX - point + const newWidth = size + diff + let x = event.clientX - tableRect.x if (x < 10) { - x = 10; + x = 10 } else if (x > tableRect.width - 10) { - x = tableRect.width - 10; + x = tableRect.width - 10 } else if (newWidth < 20) { - x = point - size + 20 - tableRect.x; + x = point - size + 20 - tableRect.x } - tableResizerRulerElem.style.left = `${x}px`; + tableResizerRulerElem.style.left = `${x}px` } - return; + return } if (!isEditing) { - const {clientX, clientY} = event; - const {width, x, y, height} = tableRect; - const isOnRightEdge = - clientX > x + width * 0.9 && - clientX < x + width + 40 && - !mouseDownRef.current; - setShowAddColumns(isOnRightEdge); + const { clientX, clientY } = event + const { width, x, y, height } = tableRect + const isOnRightEdge = clientX > x + width * 0.9 && clientX < x + width + 40 && !mouseDownRef.current + setShowAddColumns(isOnRightEdge) const isOnBottomEdge = event.target === addRowsRef.current || - (clientY > y + height * 0.85 && - clientY < y + height + 5 && - !mouseDownRef.current); - setShowAddRows(isOnBottomEdge); + (clientY > y + height * 0.85 && clientY < y + height + 5 && !mouseDownRef.current) + setShowAddRows(isOnBottomEdge) } - if ( - isEditing || - !mouseDownRef.current || - primarySelectedCellID === null - ) { - return; + if (isEditing || !mouseDownRef.current || primarySelectedCellID === null) { + return } - const possibleID = getCellID(event.target as HTMLElement); + const possibleID = getCellID(event.target as HTMLElement) if (possibleID !== null && possibleID !== lastCellIDRef.current) { if (selectedCellIDs.length === 0) { - tableElem.style.userSelect = 'none'; + tableElem.style.userSelect = 'none' } - const selectedIDs = getSelectedIDs( - rows, - primarySelectedCellID, - possibleID, - cellCoordMap, - ); + const selectedIDs = getSelectedIDs(rows, primarySelectedCellID, possibleID, cellCoordMap) if (selectedIDs.length === 1) { - setSelectedCellIDs(NO_CELLS); + setSelectedCellIDs(NO_CELLS) } else { - setSelectedCellIDs(selectedIDs); + setSelectedCellIDs(selectedIDs) } - lastCellIDRef.current = possibleID; + lastCellIDRef.current = possibleID } - }; + } const handlePointerUp = (event: PointerEvent) => { if (resizingID !== null) { - const {size, point} = resizeMeasureRef.current; - const diff = event.clientX - point; - let newWidth = size + diff; + const { size, point } = resizeMeasureRef.current + const diff = event.clientX - point + let newWidth = size + diff if (newWidth < 10) { - newWidth = 10; + newWidth = 10 } updateTableNode((tableNode) => { - const [x] = cellCoordMap.get(resizingID) as [number, number]; - $addUpdateTag('history-push'); - tableNode.updateColumnWidth(x, newWidth); - }); - setResizingID(null); + const [x] = cellCoordMap.get(resizingID) as [number, number] + $addUpdateTag('history-push') + tableNode.updateColumnWidth(x, newWidth) + }) + setResizingID(null) } - if ( - tableElem !== null && - selectedCellIDs.length > 1 && - mouseDownRef.current - ) { - tableElem.style.userSelect = 'text'; - window.getSelection()?.removeAllRanges(); + if (tableElem !== null && selectedCellIDs.length > 1 && mouseDownRef.current) { + tableElem.style.userSelect = 'text' + window.getSelection()?.removeAllRanges() } - mouseDownRef.current = false; - }; + mouseDownRef.current = false + } - doc.addEventListener('pointerdown', handlePointerDown); - doc.addEventListener('pointermove', handlePointerMove); - doc.addEventListener('pointerup', handlePointerUp); + doc.addEventListener('pointerdown', handlePointerDown) + doc.addEventListener('pointermove', handlePointerMove) + doc.addEventListener('pointerup', handlePointerUp) return () => { - doc.removeEventListener('pointerdown', handlePointerDown); - doc.removeEventListener('pointermove', handlePointerMove); - doc.removeEventListener('pointerup', handlePointerUp); - }; + doc.removeEventListener('pointerdown', handlePointerDown) + doc.removeEventListener('pointermove', handlePointerMove) + doc.removeEventListener('pointerup', handlePointerUp) + } }, [ cellEditor, editor, @@ -1009,33 +925,33 @@ export default function TableComponent({ updateTableNode, setSelected, selectTable, - ]); + ]) useEffect(() => { if (!isEditing && primarySelectedCellID !== null) { - const doc = getCurrentDocument(editor); + const doc = getCurrentDocument(editor) const loadContentIntoCell = (cell: Cell | null) => { if (cell !== null && cellEditor !== null) { - const editorStateJSON = cell.json; - const editorState = cellEditor.parseEditorState(editorStateJSON); - cellEditor.setEditorState(editorState); + const editorStateJSON = cell.json + const editorState = cellEditor.parseEditorState(editorStateJSON) + cellEditor.setEditorState(editorState) } - }; + } const handleDblClick = (event: MouseEvent) => { - const possibleID = getCellID(event.target as HTMLElement); + const possibleID = getCellID(event.target as HTMLElement) if (possibleID === primarySelectedCellID && editor.isEditable()) { - const cell = getCell(rows, possibleID, cellCoordMap); - loadContentIntoCell(cell); - setIsEditing(true); - setSelectedCellIDs(NO_CELLS); + const cell = getCell(rows, possibleID, cellCoordMap) + loadContentIntoCell(cell) + setIsEditing(true) + setSelectedCellIDs(NO_CELLS) } - }; + } const handleKeyDown = (event: KeyboardEvent) => { // Ignore arrow keys, escape or tab - const keyCode = event.keyCode; + const keyCode = event.keyCode if ( keyCode === 16 || keyCode === 27 || @@ -1048,10 +964,10 @@ export default function TableComponent({ keyCode === 46 || !editor.isEditable() ) { - return; + return } if (keyCode === 13) { - event.preventDefault(); + event.preventDefault() } if ( !isEditing && @@ -1060,80 +976,66 @@ export default function TableComponent({ (event.target as HTMLElement).contentEditable !== 'true' ) { if (isCopy(keyCode, event.shiftKey, event.metaKey, event.ctrlKey)) { - editor.dispatchCommand(COPY_COMMAND, event); - return; + editor.dispatchCommand(COPY_COMMAND, event) + return } if (isCut(keyCode, event.shiftKey, event.metaKey, event.ctrlKey)) { - editor.dispatchCommand(CUT_COMMAND, event); - return; + editor.dispatchCommand(CUT_COMMAND, event) + return } if (isPaste(keyCode, event.shiftKey, event.metaKey, event.ctrlKey)) { - editor.dispatchCommand(PASTE_COMMAND, event); - return; + editor.dispatchCommand(PASTE_COMMAND, event) + return } } if (event.metaKey || event.ctrlKey || event.altKey) { - return; + return } - const cell = getCell(rows, primarySelectedCellID, cellCoordMap); - loadContentIntoCell(cell); - setIsEditing(true); - setSelectedCellIDs(NO_CELLS); - }; + const cell = getCell(rows, primarySelectedCellID, cellCoordMap) + loadContentIntoCell(cell) + setIsEditing(true) + setSelectedCellIDs(NO_CELLS) + } - doc.addEventListener('dblclick', handleDblClick); - doc.addEventListener('keydown', handleKeyDown); + doc.addEventListener('dblclick', handleDblClick) + doc.addEventListener('keydown', handleKeyDown) return () => { - doc.removeEventListener('dblclick', handleDblClick); - doc.removeEventListener('keydown', handleKeyDown); - }; + doc.removeEventListener('dblclick', handleDblClick) + doc.removeEventListener('keydown', handleKeyDown) + } } - }, [ - cellEditor, - editor, - isEditing, - rows, - primarySelectedCellID, - cellCoordMap, - ]); + }, [cellEditor, editor, isEditing, rows, primarySelectedCellID, cellCoordMap]) const updateCellsByID = useCallback( (ids: Array, fn: () => void) => { - $updateCells(rows, ids, cellCoordMap, cellEditor, updateTableNode, fn); + $updateCells(rows, ids, cellCoordMap, cellEditor, updateTableNode, fn) }, [cellCoordMap, cellEditor, rows, updateTableNode], - ); + ) const clearCellsCommand = useCallback((): boolean => { if (primarySelectedCellID !== null && !isEditing) { updateCellsByID([primarySelectedCellID, ...selectedCellIDs], () => { - const root = $getRoot(); - root.clear(); - root.append($createParagraphNode()); - }); - return true; + const root = $getRoot() + root.clear() + root.append($createParagraphNode()) + }) + return true } else if (isSelected) { updateTableNode((tableNode) => { - $addUpdateTag('history-push'); - tableNode.selectNext(); - tableNode.remove(); - }); + $addUpdateTag('history-push') + tableNode.selectNext() + tableNode.remove() + }) } - return false; - }, [ - isEditing, - isSelected, - primarySelectedCellID, - selectedCellIDs, - updateCellsByID, - updateTableNode, - ]); + return false + }, [isEditing, isSelected, primarySelectedCellID, selectedCellIDs, updateCellsByID, updateTableNode]) useEffect(() => { - const tableElem = tableRef.current; + const tableElem = tableRef.current if (tableElem === null) { - return; + return } const copyDataToClipboard = ( @@ -1142,16 +1044,15 @@ export default function TableComponent({ lexicalString: string, plainTextString: string, ) => { - const clipboardData = - event instanceof KeyboardEvent ? null : event.clipboardData; - event.preventDefault(); + const clipboardData = event instanceof KeyboardEvent ? null : event.clipboardData + event.preventDefault() if (clipboardData != null) { - clipboardData.setData('text/html', htmlString); - clipboardData.setData('text/plain', plainTextString); - clipboardData.setData('application/x-lexical-editor', lexicalString); + clipboardData.setData('text/html', htmlString) + clipboardData.setData('text/plain', plainTextString) + clipboardData.setData('application/x-lexical-editor', lexicalString) } else { - const clipboard = navigator.clipboard; + const clipboard = navigator.clipboard if (clipboard != null) { // Most browsers only support a single item in the clipboard at one time. // So we optimize by only putting in HTML. @@ -1161,290 +1062,206 @@ export default function TableComponent({ type: 'text/html', }), }), - ]; - clipboard.write(data); + ] + clipboard.write(data).catch(console.error) } } - }; + } - const getTypeFromObject = async ( - clipboardData: DataTransfer | ClipboardItem, - type: string, - ): Promise => { + const getTypeFromObject = async (clipboardData: DataTransfer | ClipboardItem, type: string): Promise => { try { return clipboardData instanceof DataTransfer ? clipboardData.getData(type) : clipboardData instanceof ClipboardItem ? await (await clipboardData.getType(type)).text() - : ''; + : '' } catch { - return ''; + return '' } - }; + } const pasteContent = async (event: ClipboardEvent) => { let clipboardData: null | DataTransfer | ClipboardItem = - (event instanceof InputEvent ? null : event.clipboardData) || null; + (event instanceof InputEvent ? null : event.clipboardData) || null if (primarySelectedCellID !== null && cellEditor !== null) { - event.preventDefault(); + event.preventDefault() if (clipboardData === null) { try { - const items = await navigator.clipboard.read(); - clipboardData = items[0]; + const items = await navigator.clipboard.read() + clipboardData = items[0] } catch { // NO-OP } } const lexicalString = - clipboardData !== null - ? await getTypeFromObject( - clipboardData, - 'application/x-lexical-editor', - ) - : ''; + clipboardData !== null ? await getTypeFromObject(clipboardData, 'application/x-lexical-editor') : '' if (lexicalString) { try { - const payload = JSON.parse(lexicalString); - if ( - payload.namespace === editor._config.namespace && - Array.isArray(payload.nodes) - ) { - $updateCells( - rows, - [primarySelectedCellID], - cellCoordMap, - cellEditor, - updateTableNode, - () => { - const root = $getRoot(); - root.clear(); - root.append($createParagraphNode()); - root.selectEnd(); - const nodes = $generateNodesFromSerializedNodes( - payload.nodes, - ); - const sel = $getSelection(); - if ($isRangeSelection(sel)) { - $insertGeneratedNodes(cellEditor, nodes, sel); - } - }, - ); - return; + const payload = JSON.parse(lexicalString) + if (payload.namespace === editor._config.namespace && Array.isArray(payload.nodes)) { + $updateCells(rows, [primarySelectedCellID], cellCoordMap, cellEditor, updateTableNode, () => { + const root = $getRoot() + root.clear() + root.append($createParagraphNode()) + root.selectEnd() + const nodes = $generateNodesFromSerializedNodes(payload.nodes) + const sel = $getSelection() + if ($isRangeSelection(sel)) { + $insertGeneratedNodes(cellEditor, nodes, sel) + } + }) + return } // eslint-disable-next-line no-empty } catch {} } - const htmlString = - clipboardData !== null - ? await getTypeFromObject(clipboardData, 'text/html') - : ''; + const htmlString = clipboardData !== null ? await getTypeFromObject(clipboardData, 'text/html') : '' if (htmlString) { try { - const parser = new DOMParser(); - const dom = parser.parseFromString(htmlString, 'text/html'); - const possibleTableElement = dom.querySelector('table'); + const parser = new DOMParser() + const dom = parser.parseFromString(htmlString, 'text/html') + const possibleTableElement = dom.querySelector('table') if (possibleTableElement != null) { - const pasteRows = extractRowsFromHTML(possibleTableElement); + const pasteRows = extractRowsFromHTML(possibleTableElement) updateTableNode((tableNode) => { - const [x, y] = cellCoordMap.get(primarySelectedCellID) as [ - number, - number, - ]; - $addUpdateTag('history-push'); - tableNode.mergeRows(x, y, pasteRows); - }); - return; + const [x, y] = cellCoordMap.get(primarySelectedCellID) as [number, number] + $addUpdateTag('history-push') + tableNode.mergeRows(x, y, pasteRows) + }) + return } - $updateCells( - rows, - [primarySelectedCellID], - cellCoordMap, - cellEditor, - updateTableNode, - () => { - const root = $getRoot(); - root.clear(); - root.append($createParagraphNode()); - root.selectEnd(); - const nodes = $generateNodesFromDOM(editor, dom); - const sel = $getSelection(); - if ($isRangeSelection(sel)) { - $insertGeneratedNodes(cellEditor, nodes, sel); - } - }, - ); - return; + $updateCells(rows, [primarySelectedCellID], cellCoordMap, cellEditor, updateTableNode, () => { + const root = $getRoot() + root.clear() + root.append($createParagraphNode()) + root.selectEnd() + const nodes = $generateNodesFromDOM(editor, dom) + const sel = $getSelection() + if ($isRangeSelection(sel)) { + $insertGeneratedNodes(cellEditor, nodes, sel) + } + }) + return // eslint-disable-next-line no-empty } catch {} } // Multi-line plain text in rich text mode pasted as separate paragraphs // instead of single paragraph with linebreaks. - const text = - clipboardData !== null - ? await getTypeFromObject(clipboardData, 'text/plain') - : ''; + const text = clipboardData !== null ? await getTypeFromObject(clipboardData, 'text/plain') : '' if (text != null) { - $updateCells( - rows, - [primarySelectedCellID], - cellCoordMap, - cellEditor, - updateTableNode, - () => { - const root = $getRoot(); - root.clear(); - root.selectEnd(); - const sel = $getSelection(); - if (sel !== null) { - sel.insertRawText(text); - } - }, - ); + $updateCells(rows, [primarySelectedCellID], cellCoordMap, cellEditor, updateTableNode, () => { + const root = $getRoot() + root.clear() + root.selectEnd() + const sel = $getSelection() + if (sel !== null) { + sel.insertRawText(text) + } + }) } } - }; + } const copyPrimaryCell = (event: ClipboardEvent) => { if (primarySelectedCellID !== null && cellEditor !== null) { - const cell = getCell(rows, primarySelectedCellID, cellCoordMap) as Cell; - const json = cell.json; - const htmlString = cellHTMLCache.get(json) || null; + const cell = getCell(rows, primarySelectedCellID, cellCoordMap) as Cell + const json = cell.json + const htmlString = cellHTMLCache.get(json) || null if (htmlString === null) { - return; + return } - const editorState = cellEditor.parseEditorState(json); - const plainTextString = editorState.read(() => - $getRoot().getTextContent(), - ); + const editorState = cellEditor.parseEditorState(json) + const plainTextString = editorState.read(() => $getRoot().getTextContent()) const lexicalString = editorState.read(() => { - return JSON.stringify( - $generateJSONFromSelectedNodes(cellEditor, null), - ); - }); + return JSON.stringify($generateJSONFromSelectedNodes(cellEditor, null)) + }) - copyDataToClipboard(event, htmlString, lexicalString, plainTextString); + copyDataToClipboard(event, htmlString, lexicalString, plainTextString) } - }; + } const copyCellRange = (event: ClipboardEvent) => { - const lastCellID = lastCellIDRef.current; - if ( - primarySelectedCellID !== null && - cellEditor !== null && - lastCellID !== null - ) { - const rect = getSelectedRect( - primarySelectedCellID, - lastCellID, - cellCoordMap, - ); + const lastCellID = lastCellIDRef.current + if (primarySelectedCellID !== null && cellEditor !== null && lastCellID !== null) { + const rect = getSelectedRect(primarySelectedCellID, lastCellID, cellCoordMap) if (rect === null) { - return; + return } - const dom = exportTableCellsToHTML(rows, rect); - const htmlString = dom.outerHTML; - const plainTextString = dom.outerText; + const dom = exportTableCellsToHTML(rows, rect) + const htmlString = dom.outerHTML + const plainTextString = dom.outerText const tableNodeJSON = editor.getEditorState().read(() => { - const tableNode = $getNodeByKey(nodeKey) as TableNode; - return tableNode.exportJSON(); - }); - tableNodeJSON.rows = extractCellsFromRows(rows, rect); + const tableNode = $getNodeByKey(nodeKey) as TableNode + return tableNode.exportJSON() + }) + tableNodeJSON.rows = extractCellsFromRows(rows, rect) const lexicalJSON = { namespace: cellEditor._config.namespace, nodes: [tableNodeJSON], - }; - const lexicalString = JSON.stringify(lexicalJSON); - copyDataToClipboard(event, htmlString, lexicalString, plainTextString); + } + const lexicalString = JSON.stringify(lexicalJSON) + copyDataToClipboard(event, htmlString, lexicalString, plainTextString) } - }; + } - const handlePaste = ( - event: ClipboardEvent, - activeEditor: LexicalEditor, - ) => { - const selection = $getSelection(); - if ( - primarySelectedCellID !== null && - !isEditing && - selection === null && - activeEditor === editor - ) { - pasteContent(event); - mouseDownRef.current = false; - setSelectedCellIDs(NO_CELLS); - return true; + const handlePaste = (event: ClipboardEvent, activeEditor: LexicalEditor) => { + const selection = $getSelection() + if (primarySelectedCellID !== null && !isEditing && selection === null && activeEditor === editor) { + pasteContent(event).catch(console.error) + mouseDownRef.current = false + setSelectedCellIDs(NO_CELLS) + return true } - return false; - }; + return false + } const handleCopy = (event: ClipboardEvent, activeEditor: LexicalEditor) => { - const selection = $getSelection(); - if ( - primarySelectedCellID !== null && - !isEditing && - selection === null && - activeEditor === editor - ) { + const selection = $getSelection() + if (primarySelectedCellID !== null && !isEditing && selection === null && activeEditor === editor) { if (selectedCellIDs.length === 0) { - copyPrimaryCell(event); + copyPrimaryCell(event) } else { - copyCellRange(event); + copyCellRange(event) } - return true; + return true } - return false; - }; + return false + } return mergeRegister( editor.registerCommand( CLICK_COMMAND, - (payload) => { - const selection = $getSelection(); + () => { + const selection = $getSelection() if ($isNodeSelection(selection)) { - return true; + return true } - return false; + return false }, COMMAND_PRIORITY_LOW, ), - editor.registerCommand( - PASTE_COMMAND, - handlePaste, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand( - COPY_COMMAND, - handleCopy, - COMMAND_PRIORITY_LOW, - ), + editor.registerCommand(PASTE_COMMAND, handlePaste, COMMAND_PRIORITY_LOW), + editor.registerCommand(COPY_COMMAND, handleCopy, COMMAND_PRIORITY_LOW), editor.registerCommand( CUT_COMMAND, (event: ClipboardEvent, activeEditor) => { if (handleCopy(event, activeEditor)) { - clearCellsCommand(); - return true; + clearCellsCommand() + return true } - return false; + return false }, COMMAND_PRIORITY_LOW, ), - editor.registerCommand( - KEY_BACKSPACE_COMMAND, - clearCellsCommand, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand( - KEY_DELETE_COMMAND, - clearCellsCommand, - COMMAND_PRIORITY_LOW, - ), + editor.registerCommand(KEY_BACKSPACE_COMMAND, clearCellsCommand, COMMAND_PRIORITY_LOW), + editor.registerCommand(KEY_DELETE_COMMAND, clearCellsCommand, COMMAND_PRIORITY_LOW), editor.registerCommand( FORMAT_TEXT_COMMAND, (payload) => { @@ -1456,20 +1273,20 @@ export default function TableComponent({ cellEditor, updateTableNode, () => { - const sel = $createSelectAll(); - sel.formatText(payload); + const sel = $createSelectAll() + sel.formatText(payload) }, - ); - return true; + ) + return true } - return false; + return false }, COMMAND_PRIORITY_LOW, ), editor.registerCommand( KEY_ENTER_COMMAND, (event, targetEditor) => { - const selection = $getSelection(); + const selection = $getSelection() if ( primarySelectedCellID === null && !isEditing && @@ -1478,221 +1295,198 @@ export default function TableComponent({ selection.getNodes().length === 1 && targetEditor === editor ) { - const firstCellID = rows[0].cells[0].id; - setPrimarySelectedCellID(firstCellID); - focusCell(tableElem, firstCellID); - event.preventDefault(); - event.stopPropagation(); - clearSelection(); - return true; + const firstCellID = rows[0].cells[0].id + setPrimarySelectedCellID(firstCellID) + focusCell(tableElem, firstCellID) + event.preventDefault() + event.stopPropagation() + clearSelection() + return true } - return false; + return false }, COMMAND_PRIORITY_LOW, ), editor.registerCommand( KEY_TAB_COMMAND, (event) => { - const selection = $getSelection(); - if ( - !isEditing && - selection === null && - primarySelectedCellID !== null - ) { - const isBackward = event.shiftKey; - const [x, y] = cellCoordMap.get(primarySelectedCellID) as [ - number, - number, - ]; - event.preventDefault(); - let nextX = null; - let nextY = null; + const selection = $getSelection() + if (!isEditing && selection === null && primarySelectedCellID !== null) { + const isBackward = event.shiftKey + const [x, y] = cellCoordMap.get(primarySelectedCellID) as [number, number] + event.preventDefault() + let nextX = null + let nextY = null if (x === 0 && isBackward) { if (y !== 0) { - nextY = y - 1; - nextX = rows[nextY].cells.length - 1; + nextY = y - 1 + nextX = rows[nextY].cells.length - 1 } } else if (x === rows[y].cells.length - 1 && !isBackward) { if (y !== rows.length - 1) { - nextY = y + 1; - nextX = 0; + nextY = y + 1 + nextX = 0 } } else if (!isBackward) { - nextX = x + 1; - nextY = y; + nextX = x + 1 + nextY = y } else { - nextX = x - 1; - nextY = y; + nextX = x - 1 + nextY = y } if (nextX !== null && nextY !== null) { - modifySelectedCells(nextX, nextY, false); - return true; + modifySelectedCells(nextX, nextY, false) + return true } } - return false; + return false }, COMMAND_PRIORITY_LOW, ), editor.registerCommand( KEY_ARROW_UP_COMMAND, (event, targetEditor) => { - const selection = $getSelection(); + const selection = $getSelection() if (!isEditing && selection === null) { - const extend = event.shiftKey; - const cellID = extend - ? lastCellIDRef.current || primarySelectedCellID - : primarySelectedCellID; + const extend = event.shiftKey + const cellID = extend ? lastCellIDRef.current || primarySelectedCellID : primarySelectedCellID if (cellID !== null) { - const [x, y] = cellCoordMap.get(cellID) as [number, number]; + const [x, y] = cellCoordMap.get(cellID) as [number, number] if (y !== 0) { - modifySelectedCells(x, y - 1, extend); - return true; + modifySelectedCells(x, y - 1, extend) + return true } } } if (!$isRangeSelection(selection) || targetEditor !== cellEditor) { - return false; + return false } if ( selection.isCollapsed() && - selection.anchor - .getNode() - .getTopLevelElementOrThrow() - .getPreviousSibling() === null + selection.anchor.getNode().getTopLevelElementOrThrow().getPreviousSibling() === null ) { - event.preventDefault(); - return true; + event.preventDefault() + return true } - return false; + return false }, COMMAND_PRIORITY_LOW, ), editor.registerCommand( KEY_ARROW_DOWN_COMMAND, (event, targetEditor) => { - const selection = $getSelection(); + const selection = $getSelection() if (!isEditing && selection === null) { - const extend = event.shiftKey; - const cellID = extend - ? lastCellIDRef.current || primarySelectedCellID - : primarySelectedCellID; + const extend = event.shiftKey + const cellID = extend ? lastCellIDRef.current || primarySelectedCellID : primarySelectedCellID if (cellID !== null) { - const [x, y] = cellCoordMap.get(cellID) as [number, number]; + const [x, y] = cellCoordMap.get(cellID) as [number, number] if (y !== rows.length - 1) { - modifySelectedCells(x, y + 1, extend); - return true; + modifySelectedCells(x, y + 1, extend) + return true } } } if (!$isRangeSelection(selection) || targetEditor !== cellEditor) { - return false; + return false } if ( selection.isCollapsed() && - selection.anchor - .getNode() - .getTopLevelElementOrThrow() - .getNextSibling() === null + selection.anchor.getNode().getTopLevelElementOrThrow().getNextSibling() === null ) { - event.preventDefault(); - return true; + event.preventDefault() + return true } - return false; + return false }, COMMAND_PRIORITY_LOW, ), editor.registerCommand( KEY_ARROW_LEFT_COMMAND, (event, targetEditor) => { - const selection = $getSelection(); + const selection = $getSelection() if (!isEditing && selection === null) { - const extend = event.shiftKey; - const cellID = extend - ? lastCellIDRef.current || primarySelectedCellID - : primarySelectedCellID; + const extend = event.shiftKey + const cellID = extend ? lastCellIDRef.current || primarySelectedCellID : primarySelectedCellID if (cellID !== null) { - const [x, y] = cellCoordMap.get(cellID) as [number, number]; + const [x, y] = cellCoordMap.get(cellID) as [number, number] if (x !== 0) { - modifySelectedCells(x - 1, y, extend); - return true; + modifySelectedCells(x - 1, y, extend) + return true } } } if (!$isRangeSelection(selection) || targetEditor !== cellEditor) { - return false; + return false } if (selection.isCollapsed() && selection.anchor.offset === 0) { - event.preventDefault(); - return true; + event.preventDefault() + return true } - return false; + return false }, COMMAND_PRIORITY_LOW, ), editor.registerCommand( KEY_ARROW_RIGHT_COMMAND, (event, targetEditor) => { - const selection = $getSelection(); + const selection = $getSelection() if (!isEditing && selection === null) { - const extend = event.shiftKey; - const cellID = extend - ? lastCellIDRef.current || primarySelectedCellID - : primarySelectedCellID; + const extend = event.shiftKey + const cellID = extend ? lastCellIDRef.current || primarySelectedCellID : primarySelectedCellID if (cellID !== null) { - const [x, y] = cellCoordMap.get(cellID) as [number, number]; + const [x, y] = cellCoordMap.get(cellID) as [number, number] if (x !== rows[y].cells.length - 1) { - modifySelectedCells(x + 1, y, extend); - return true; + modifySelectedCells(x + 1, y, extend) + return true } } } if (!$isRangeSelection(selection) || targetEditor !== cellEditor) { - return false; + return false } if (selection.isCollapsed()) { - const anchor = selection.anchor; + const anchor = selection.anchor if ( - (anchor.type === 'text' && - anchor.offset === anchor.getNode().getTextContentSize()) || - (anchor.type === 'element' && - anchor.offset === anchor.getNode().getChildrenSize()) + (anchor.type === 'text' && anchor.offset === anchor.getNode().getTextContentSize()) || + (anchor.type === 'element' && anchor.offset === anchor.getNode().getChildrenSize()) ) { - event.preventDefault(); - return true; + event.preventDefault() + return true } } - return false; + return false }, COMMAND_PRIORITY_LOW, ), editor.registerCommand( KEY_ESCAPE_COMMAND, (event, targetEditor) => { - const selection = $getSelection(); + const selection = $getSelection() if (!isEditing && selection === null && targetEditor === editor) { - setSelected(true); - setPrimarySelectedCellID(null); - selectTable(); - return true; + setSelected(true) + setPrimarySelectedCellID(null) + selectTable() + return true } if (!$isRangeSelection(selection)) { - return false; + return false } if (isEditing) { - saveEditorToJSON(); - setIsEditing(false); + saveEditorToJSON() + setIsEditing(false) if (primarySelectedCellID !== null) { setTimeout(() => { - focusCell(tableElem, primarySelectedCellID); - }, 20); + focusCell(tableElem, primarySelectedCellID) + }, 20) } - return true; + return true } - return false; + return false }, COMMAND_PRIORITY_LOW, ), - ); + ) }, [ cellCoordMap, cellEditor, @@ -1709,23 +1503,20 @@ export default function TableComponent({ selectedCellIDs, setSelected, updateTableNode, - ]); + ]) if (cellEditor === null) { - return; + return } return ( -
- +
+
{rows.map((row) => ( {row.cells.map((cell) => { - const {id} = cell; + const { id } = cell return ( - ); + ) })} ))}
- {showAddColumns && ( -