diff --git a/package-lock.json b/package-lock.json index 8050410..3258b0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@douyinfe/semi-ui": "^2.36.0", + "@lexical/react": "^0.12.2", "@monaco-editor/react": "^4.5.1", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", @@ -17,14 +18,13 @@ "html-to-image": "^1.11.11", "jsonschema": "^1.4.1", "jspdf": "^2.5.1", + "lexical": "^0.12.2", "node-sql-parser": "^4.7.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.4.1", "react-router-dom": "^6.11.2", "react-scripts": "5.0.1", - "slate": "^0.94.1", - "slate-react": "^0.98.3", "socket.io-client": "^4.7.2", "unique-names-generator": "^4.7.1", "url": "^0.11.1", @@ -3609,16 +3609,247 @@ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, - "node_modules/@juggle/resize-observer": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" - }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@lexical/clipboard": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/clipboard/-/clipboard-0.12.2.tgz", + "integrity": "sha512-RldmfZquuJJJCJ5WquCyoJ1/eZ+AnNgdksqvd+G+Yn/GyJl/+O3dnHM0QVaDSPvh/PynLFcCtz/57ySLo2kQxQ==", + "dependencies": { + "@lexical/html": "0.12.2", + "@lexical/list": "0.12.2", + "@lexical/selection": "0.12.2", + "@lexical/utils": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/code": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/code/-/code-0.12.2.tgz", + "integrity": "sha512-w2JeJdnMUtYnC/Fx78sL3iJBt9Ug8pFSDOcI9ay/BkMQFQV8oqq1iyuLLBBJSG4FAM8b2DXrVdGklRQ+jTfTVw==", + "dependencies": { + "@lexical/utils": "0.12.2", + "prismjs": "^1.27.0" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/dragon": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/dragon/-/dragon-0.12.2.tgz", + "integrity": "sha512-Mt8NLzTOt+VgQtc2DKDbHBwKeRlvKqbLqRIMYUVk60gol+YV7NpVBsP1PAMuYYjrTQLhlckBSC32H1SUHZRavA==", + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/hashtag": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/hashtag/-/hashtag-0.12.2.tgz", + "integrity": "sha512-2vYzIu5Ldf+eYdUrNA2m80c3N3MF3vJ0fIJzpl5QyX8OdViggEWl1bh+lKtw1Ju0H0CUyDIXdDLZ2apW3WDkTA==", + "dependencies": { + "@lexical/utils": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/history": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/history/-/history-0.12.2.tgz", + "integrity": "sha512-PM/EDjnUyBPMWh1UiYb7T+FLbvTk14HwUWLXvZxn72S6Kj8ExH/PfLbWZWLCFL8RfzvbP407VwfSN8S0bF5H6g==", + "dependencies": { + "@lexical/utils": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/html": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/html/-/html-0.12.2.tgz", + "integrity": "sha512-LWUO6OKhDtDZa9X1spHAqzsp+4EF01exis4cz5H9y2sHi7EofogXnRCadZ+fa07NVwPVTZWsStkk5qdSe/NEzg==", + "dependencies": { + "@lexical/selection": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/link": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/link/-/link-0.12.2.tgz", + "integrity": "sha512-etOIONa7uyRDmwg8GN52kDlf8thD2Zk1LOFLeocHWz1V8fe3i2unGUek5s/rNPkc6ynpPpNsHdN1VEghOLCCmw==", + "dependencies": { + "@lexical/utils": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/list": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/list/-/list-0.12.2.tgz", + "integrity": "sha512-3CyWtYQC+IlK4cK/oiD8Uz1gSXD8UcKGOF2vVsDXkMU06O6zvHNmHZOnVJqA0JVNgZAoR9dMR1fi2xd4iuCAiw==", + "dependencies": { + "@lexical/utils": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/mark": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/mark/-/mark-0.12.2.tgz", + "integrity": "sha512-ub+37PDfmThsqAWipRTrwqpgE+83ckqJ5C3mKQUBZvhZfVZW1rEUXZnKjFh2Q3eZK6iT7zVgoVJWJS9ZgEEyag==", + "dependencies": { + "@lexical/utils": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/markdown": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/markdown/-/markdown-0.12.2.tgz", + "integrity": "sha512-F2jTFtBp7Q+yoA11BeUOEcxhROzW+HUhUGdsn20pSLhuxsWRj3oUuryWFeNKFofpzTCVoqU6dwpaMNMI2mL/sQ==", + "dependencies": { + "@lexical/code": "0.12.2", + "@lexical/link": "0.12.2", + "@lexical/list": "0.12.2", + "@lexical/rich-text": "0.12.2", + "@lexical/text": "0.12.2", + "@lexical/utils": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/offset": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/offset/-/offset-0.12.2.tgz", + "integrity": "sha512-rZLZXfOBmpmM8A2UZsX3cr/CQYw5F/ou67AbaKI0WImb5sjnIgICZqzu9VFUnkKlVNUurEpplV3UG3D1YYh1OQ==", + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/overflow": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/overflow/-/overflow-0.12.2.tgz", + "integrity": "sha512-UgE5j3ukO6qRFRpH4T7m/DvnodE9nCtImD7QinyGdsTa0hi5xlRnl0FUo605vH+vz7xEsUNAGwQXYPX9Sc/vig==", + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/plain-text": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/plain-text/-/plain-text-0.12.2.tgz", + "integrity": "sha512-Lcg6+ngRnX70//kz34azYhID3bvW66HSHCfu5UPhCXT+vQ/Jkd/InhRKajBwWXpaJxMM1huoi3sjzVDb3luNtw==", + "peerDependencies": { + "@lexical/clipboard": "0.12.2", + "@lexical/selection": "0.12.2", + "@lexical/utils": "0.12.2", + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/react": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/react/-/react-0.12.2.tgz", + "integrity": "sha512-ZBUvf5xmhiYWBw8pPrhYmLAEwFWrbF/cd15y76TUKD9l/2zDwwPs6nJQxBzfz3ei65r2/nnavLDV8W3QfvxfUA==", + "dependencies": { + "@lexical/clipboard": "0.12.2", + "@lexical/code": "0.12.2", + "@lexical/dragon": "0.12.2", + "@lexical/hashtag": "0.12.2", + "@lexical/history": "0.12.2", + "@lexical/link": "0.12.2", + "@lexical/list": "0.12.2", + "@lexical/mark": "0.12.2", + "@lexical/markdown": "0.12.2", + "@lexical/overflow": "0.12.2", + "@lexical/plain-text": "0.12.2", + "@lexical/rich-text": "0.12.2", + "@lexical/selection": "0.12.2", + "@lexical/table": "0.12.2", + "@lexical/text": "0.12.2", + "@lexical/utils": "0.12.2", + "@lexical/yjs": "0.12.2", + "react-error-boundary": "^3.1.4" + }, + "peerDependencies": { + "lexical": "0.12.2", + "react": ">=17.x", + "react-dom": ">=17.x" + } + }, + "node_modules/@lexical/rich-text": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/rich-text/-/rich-text-0.12.2.tgz", + "integrity": "sha512-igsEuv7CwBOAj5c8jeE41cnx6zkhI/Bkbu4W7shT6S6lNA/3cnyZpAMlgixwyK5RoqjGRCT+IJK5l6yBxQfNkw==", + "peerDependencies": { + "@lexical/clipboard": "0.12.2", + "@lexical/selection": "0.12.2", + "@lexical/utils": "0.12.2", + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/selection": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/selection/-/selection-0.12.2.tgz", + "integrity": "sha512-h+g3oOnihHKIyLTyG6uLCEVR/DmUEVdCcZO1iAoGsuW7nwWiWNPWj6oZ3Cw5J1Mk5u62DHnkkVDQsVSZbAwmtg==", + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/table": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/table/-/table-0.12.2.tgz", + "integrity": "sha512-tiAmTq6RKHDVER9v589Ajm9/RL+WTF1WschrH6HHVCtil6cfJfTJeJ+MF45+XEzB9fkqy2LfrScAfWxqLjVePA==", + "dependencies": { + "@lexical/utils": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/text": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/text/-/text-0.12.2.tgz", + "integrity": "sha512-HyuIGuQvVi5djJKKBf+jYEBjK+0Eo9cKHf6WS7dlFozuCZvcCQEJkFy2yceWOwIVk+f2kptVQ5uO7aiZHExH2A==", + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/utils": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/utils/-/utils-0.12.2.tgz", + "integrity": "sha512-xW4y4l2Yd37+qLwkBvBGyzsKCA9wnh1ljphBJeR2vreT193i2gaIwuku2ZKlER14VHw4192qNJF7vUoAEmwurQ==", + "dependencies": { + "@lexical/list": "0.12.2", + "@lexical/selection": "0.12.2", + "@lexical/table": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2" + } + }, + "node_modules/@lexical/yjs": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@lexical/yjs/-/yjs-0.12.2.tgz", + "integrity": "sha512-OPJhkJD1Mp9W80mfLzASTB3OFWFMzJteUYA+eSyDgiX9zNi1VGxAqmIITTkDvnCMa+qvw4EfhGeGezpjx6Og4A==", + "dependencies": { + "@lexical/offset": "0.12.2" + }, + "peerDependencies": { + "lexical": "0.12.2", + "yjs": ">=13.5.22" + } + }, "node_modules/@monaco-editor/loader": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.3.3.tgz", @@ -4340,11 +4571,6 @@ "@types/node": "*" } }, - "node_modules/@types/is-hotkey": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.7.tgz", - "integrity": "sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ==" - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -4414,11 +4640,6 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, - "node_modules/@types/lodash": { - "version": "4.14.198", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", - "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==" - }, "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -7112,18 +7333,6 @@ "node": ">=8" } }, - "node_modules/direction": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", - "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==", - "bin": { - "direction": "cli.js" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -9694,11 +9903,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-hotkey": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz", - "integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==" - }, "node_modules/is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -9772,14 +9976,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -12462,6 +12658,11 @@ "node": ">= 0.8.0" } }, + "node_modules/lexical": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/lexical/-/lexical-0.12.2.tgz", + "integrity": "sha512-Kxavd+ETjxtVwG/hvPd6WZfXD44sLOKe9Vlkwxy7lBQ1qZArS+rZfs+u5iXwXe6tX9f2PIM0u3RHsrCEDDE0fw==" + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -14747,6 +14948,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -15033,6 +15242,21 @@ "react-dom": ">= 16.3.0" } }, + "node_modules/react-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-error-overlay": { "version": "6.0.11", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", @@ -15977,37 +16201,6 @@ "node": ">=8" } }, - "node_modules/slate": { - "version": "0.94.1", - "resolved": "https://registry.npmjs.org/slate/-/slate-0.94.1.tgz", - "integrity": "sha512-GH/yizXr1ceBoZ9P9uebIaHe3dC/g6Plpf9nlUwnvoyf6V1UOYrRwkabtOCd3ZfIGxomY4P7lfgLr7FPH8/BKA==", - "dependencies": { - "immer": "^9.0.6", - "is-plain-object": "^5.0.0", - "tiny-warning": "^1.0.3" - } - }, - "node_modules/slate-react": { - "version": "0.98.3", - "resolved": "https://registry.npmjs.org/slate-react/-/slate-react-0.98.3.tgz", - "integrity": "sha512-p1BnF9eRyRM0i5hkgOb11KgmpWLQm9Zyp6jVkOAj5fPdIGheKhg48Z7aWKrayeJ4nmRyi/NjRZz/io5hQcphmw==", - "dependencies": { - "@juggle/resize-observer": "^3.4.0", - "@types/is-hotkey": "^0.1.1", - "@types/lodash": "^4.14.149", - "direction": "^1.0.3", - "is-hotkey": "^0.1.6", - "is-plain-object": "^5.0.0", - "lodash": "^4.17.4", - "scroll-into-view-if-needed": "^2.2.20", - "tiny-invariant": "1.0.6" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0", - "slate": ">=0.65.3" - } - }, "node_modules/socket.io-client": { "version": "4.7.2", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", @@ -16859,16 +17052,6 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, - "node_modules/tiny-invariant": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz", - "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/package.json b/package.json index 36e82b2..4b6c537 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@douyinfe/semi-ui": "^2.36.0", + "@lexical/react": "^0.12.2", "@monaco-editor/react": "^4.5.1", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", @@ -12,14 +13,13 @@ "html-to-image": "^1.11.11", "jsonschema": "^1.4.1", "jspdf": "^2.5.1", + "lexical": "^0.12.2", "node-sql-parser": "^4.7.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.4.1", "react-router-dom": "^6.11.2", "react-scripts": "5.0.1", - "slate": "^0.94.1", - "slate-react": "^0.98.3", "socket.io-client": "^4.7.2", "unique-names-generator": "^4.7.1", "url": "^0.11.1", diff --git a/public/images/emoji/1F600.png b/public/images/emoji/1F600.png new file mode 100644 index 0000000..ba0e4fe Binary files /dev/null and b/public/images/emoji/1F600.png differ diff --git a/public/images/emoji/1F641.png b/public/images/emoji/1F641.png new file mode 100644 index 0000000..0dd4e5b Binary files /dev/null and b/public/images/emoji/1F641.png differ diff --git a/public/images/emoji/1F642.png b/public/images/emoji/1F642.png new file mode 100644 index 0000000..b37115f Binary files /dev/null and b/public/images/emoji/1F642.png differ diff --git a/public/images/emoji/2764.png b/public/images/emoji/2764.png new file mode 100644 index 0000000..2c6c24a Binary files /dev/null and b/public/images/emoji/2764.png differ diff --git a/public/images/emoji/LICENSE.md b/public/images/emoji/LICENSE.md new file mode 100644 index 0000000..87b04e9 --- /dev/null +++ b/public/images/emoji/LICENSE.md @@ -0,0 +1,5 @@ +OpenMoji +https://openmoji.org + +Licensed under Attribution-ShareAlike 4.0 International +https://creativecommons.org/licenses/by-sa/4.0/ diff --git a/public/images/icons/LICENSE.md b/public/images/icons/LICENSE.md new file mode 100644 index 0000000..ce74f6a --- /dev/null +++ b/public/images/icons/LICENSE.md @@ -0,0 +1,5 @@ +Bootstrap Icons +https://icons.getbootstrap.com + +Licensed under MIT license +https://github.com/twbs/icons/blob/main/LICENSE.md diff --git a/public/images/icons/arrow-clockwise-dark.svg b/public/images/icons/arrow-clockwise-dark.svg new file mode 100644 index 0000000..e0d492f --- /dev/null +++ b/public/images/icons/arrow-clockwise-dark.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/arrow-clockwise.svg b/public/images/icons/arrow-clockwise.svg new file mode 100644 index 0000000..cc8f6b4 --- /dev/null +++ b/public/images/icons/arrow-clockwise.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/arrow-counterclockwise-dark.svg b/public/images/icons/arrow-counterclockwise-dark.svg new file mode 100644 index 0000000..d2a408d --- /dev/null +++ b/public/images/icons/arrow-counterclockwise-dark.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/arrow-counterclockwise.svg b/public/images/icons/arrow-counterclockwise.svg new file mode 100644 index 0000000..f3a0596 --- /dev/null +++ b/public/images/icons/arrow-counterclockwise.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/chat-square-quote-dark.svg b/public/images/icons/chat-square-quote-dark.svg new file mode 100644 index 0000000..2c8d98f --- /dev/null +++ b/public/images/icons/chat-square-quote-dark.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/chat-square-quote.svg b/public/images/icons/chat-square-quote.svg new file mode 100644 index 0000000..f6f3689 --- /dev/null +++ b/public/images/icons/chat-square-quote.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/chevron-down-dark.svg b/public/images/icons/chevron-down-dark.svg new file mode 100644 index 0000000..cf44f3c --- /dev/null +++ b/public/images/icons/chevron-down-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/chevron-down.svg b/public/images/icons/chevron-down.svg new file mode 100644 index 0000000..a73a90c --- /dev/null +++ b/public/images/icons/chevron-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/code-dark.svg b/public/images/icons/code-dark.svg new file mode 100644 index 0000000..22d5394 --- /dev/null +++ b/public/images/icons/code-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/code.svg b/public/images/icons/code.svg new file mode 100644 index 0000000..3ad8f49 --- /dev/null +++ b/public/images/icons/code.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/journal-code.svg b/public/images/icons/journal-code.svg new file mode 100644 index 0000000..82098b9 --- /dev/null +++ b/public/images/icons/journal-code.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/public/images/icons/journal-text.svg b/public/images/icons/journal-text.svg new file mode 100644 index 0000000..9b66f43 --- /dev/null +++ b/public/images/icons/journal-text.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/public/images/icons/justify-dark.svg b/public/images/icons/justify-dark.svg new file mode 100644 index 0000000..d2adf40 --- /dev/null +++ b/public/images/icons/justify-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/justify.svg b/public/images/icons/justify.svg new file mode 100644 index 0000000..8620437 --- /dev/null +++ b/public/images/icons/justify.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/link-dark.svg b/public/images/icons/link-dark.svg new file mode 100644 index 0000000..8daaaf2 --- /dev/null +++ b/public/images/icons/link-dark.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/link.svg b/public/images/icons/link.svg new file mode 100644 index 0000000..38ac0d1 --- /dev/null +++ b/public/images/icons/link.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/list-ol-dark.svg b/public/images/icons/list-ol-dark.svg new file mode 100644 index 0000000..18164c1 --- /dev/null +++ b/public/images/icons/list-ol-dark.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/list-ol.svg b/public/images/icons/list-ol.svg new file mode 100644 index 0000000..ffd325d --- /dev/null +++ b/public/images/icons/list-ol.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/images/icons/list-ul-dark.svg b/public/images/icons/list-ul-dark.svg new file mode 100644 index 0000000..5309e15 --- /dev/null +++ b/public/images/icons/list-ul-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/list-ul.svg b/public/images/icons/list-ul.svg new file mode 100644 index 0000000..22239e6 --- /dev/null +++ b/public/images/icons/list-ul.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/pencil-fill-dark.svg b/public/images/icons/pencil-fill-dark.svg new file mode 100644 index 0000000..53f7242 --- /dev/null +++ b/public/images/icons/pencil-fill-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/pencil-fill.svg b/public/images/icons/pencil-fill.svg new file mode 100644 index 0000000..bb2dbea --- /dev/null +++ b/public/images/icons/pencil-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/text-center-dark.svg b/public/images/icons/text-center-dark.svg new file mode 100644 index 0000000..22f88d9 --- /dev/null +++ b/public/images/icons/text-center-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/text-center.svg b/public/images/icons/text-center.svg new file mode 100644 index 0000000..ac0b80f --- /dev/null +++ b/public/images/icons/text-center.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/text-left-dark.svg b/public/images/icons/text-left-dark.svg new file mode 100644 index 0000000..a03c3a6 --- /dev/null +++ b/public/images/icons/text-left-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/text-left.svg b/public/images/icons/text-left.svg new file mode 100644 index 0000000..19337cd --- /dev/null +++ b/public/images/icons/text-left.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/text-paragraph-dark.svg b/public/images/icons/text-paragraph-dark.svg new file mode 100644 index 0000000..ea412f3 --- /dev/null +++ b/public/images/icons/text-paragraph-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/text-paragraph.svg b/public/images/icons/text-paragraph.svg new file mode 100644 index 0000000..607ac3b --- /dev/null +++ b/public/images/icons/text-paragraph.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/text-right-dark.svg b/public/images/icons/text-right-dark.svg new file mode 100644 index 0000000..25f5530 --- /dev/null +++ b/public/images/icons/text-right-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/text-right.svg b/public/images/icons/text-right.svg new file mode 100644 index 0000000..99c6238 --- /dev/null +++ b/public/images/icons/text-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-bold-dark.svg b/public/images/icons/type-bold-dark.svg new file mode 100644 index 0000000..aebfb64 --- /dev/null +++ b/public/images/icons/type-bold-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-bold.svg b/public/images/icons/type-bold.svg new file mode 100644 index 0000000..08a9b14 --- /dev/null +++ b/public/images/icons/type-bold.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-h1-dark.svg b/public/images/icons/type-h1-dark.svg new file mode 100644 index 0000000..01e58ff --- /dev/null +++ b/public/images/icons/type-h1-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-h1.svg b/public/images/icons/type-h1.svg new file mode 100644 index 0000000..b8378bb --- /dev/null +++ b/public/images/icons/type-h1.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-h2-dark.svg b/public/images/icons/type-h2-dark.svg new file mode 100644 index 0000000..ae73bc6 --- /dev/null +++ b/public/images/icons/type-h2-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-h2.svg b/public/images/icons/type-h2.svg new file mode 100644 index 0000000..dd6c173 --- /dev/null +++ b/public/images/icons/type-h2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-h3.svg b/public/images/icons/type-h3.svg new file mode 100644 index 0000000..154c293 --- /dev/null +++ b/public/images/icons/type-h3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-italic-dark.svg b/public/images/icons/type-italic-dark.svg new file mode 100644 index 0000000..efdd23a --- /dev/null +++ b/public/images/icons/type-italic-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-italic.svg b/public/images/icons/type-italic.svg new file mode 100644 index 0000000..574c58b --- /dev/null +++ b/public/images/icons/type-italic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-strikethrough-dark.svg b/public/images/icons/type-strikethrough-dark.svg new file mode 100644 index 0000000..d31e307 --- /dev/null +++ b/public/images/icons/type-strikethrough-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-strikethrough.svg b/public/images/icons/type-strikethrough.svg new file mode 100644 index 0000000..406e36e --- /dev/null +++ b/public/images/icons/type-strikethrough.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-underline-dark.svg b/public/images/icons/type-underline-dark.svg new file mode 100644 index 0000000..982c0fc --- /dev/null +++ b/public/images/icons/type-underline-dark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/icons/type-underline.svg b/public/images/icons/type-underline.svg new file mode 100644 index 0000000..21b8dbe --- /dev/null +++ b/public/images/icons/type-underline.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/rich_editor.jsx b/src/components/rich_editor.jsx index c69968c..825befa 100644 --- a/src/components/rich_editor.jsx +++ b/src/components/rich_editor.jsx @@ -1,19 +1,137 @@ -import React, { useState } from "react"; -import { createEditor } from "slate"; -import { Slate, Editable, withReact } from "slate-react"; +import { LexicalComposer } from "@lexical/react/LexicalComposer"; +import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"; +import { ContentEditable } from "@lexical/react/LexicalContentEditable"; +import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin"; +import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin"; +import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary"; +import { HeadingNode, QuoteNode } from "@lexical/rich-text"; +import { TableCellNode, TableNode, TableRowNode } from "@lexical/table"; +import { ListItemNode, ListNode } from "@lexical/list"; +import { CodeHighlightNode, CodeNode } from "@lexical/code"; +import { AutoLinkNode, LinkNode } from "@lexical/link"; +import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin"; +import { ListPlugin } from "@lexical/react/LexicalListPlugin"; +import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin"; +import { TRANSFORMERS } from "@lexical/markdown"; -const initialValue = [ - { - type: "paragraph", - children: [{ text: "A line of text in a paragraph." }], +import ToolbarPlugin from "../plugins/ToolbarPlugin"; +import ListMaxIndentLevelPlugin from "../plugins/ListMaxIndentLevelPlugin"; +import CodeHighlightPlugin from "../plugins/CodeHighlightPlugin"; +import AutoLinkPlugin from "../plugins/AutoLinkPlugin"; + +const exampleTheme = { + ltr: "ltr", + rtl: "rtl", + placeholder: "editor-placeholder", + paragraph: "editor-paragraph", + quote: "editor-quote", + heading: { + h1: "editor-heading-h1", + h2: "editor-heading-h2", + h3: "editor-heading-h3", + h4: "editor-heading-h4", + h5: "editor-heading-h5", }, -]; + list: { + nested: { + listitem: "editor-nested-listitem", + }, + ol: "editor-list-ol", + ul: "editor-list-ul", + listitem: "editor-listitem", + }, + image: "editor-image", + link: "editor-link", + text: { + bold: "editor-text-bold", + italic: "editor-text-italic", + overflowed: "editor-text-overflowed", + hashtag: "editor-text-hashtag", + underline: "editor-text-underline", + strikethrough: "editor-text-strikethrough", + underlineStrikethrough: "editor-text-underlineStrikethrough", + code: "editor-text-code", + }, + code: "editor-code", + codeHighlight: { + atrule: "editor-tokenAttr", + attr: "editor-tokenAttr", + boolean: "editor-tokenProperty", + builtin: "editor-tokenSelector", + cdata: "editor-tokenComment", + char: "editor-tokenSelector", + class: "editor-tokenFunction", + "class-name": "editor-tokenFunction", + comment: "editor-tokenComment", + constant: "editor-tokenProperty", + deleted: "editor-tokenProperty", + doctype: "editor-tokenComment", + entity: "editor-tokenOperator", + function: "editor-tokenFunction", + important: "editor-tokenVariable", + inserted: "editor-tokenSelector", + keyword: "editor-tokenAttr", + namespace: "editor-tokenVariable", + number: "editor-tokenProperty", + operator: "editor-tokenOperator", + prolog: "editor-tokenComment", + property: "editor-tokenProperty", + punctuation: "editor-tokenPunctuation", + regex: "editor-tokenVariable", + selector: "editor-tokenSelector", + string: "editor-tokenSelector", + symbol: "editor-tokenProperty", + tag: "editor-tokenProperty", + url: "editor-tokenOperator", + variable: "editor-tokenVariable", + }, +}; -export default function RichEditor() { - const [editor] = useState(() => withReact(createEditor())); +function Placeholder() { + return
Describe the bug
; +} + +const editorConfig = { + theme: exampleTheme, + onError(error) { + throw error; + }, + nodes: [ + HeadingNode, + ListNode, + ListItemNode, + QuoteNode, + CodeNode, + CodeHighlightNode, + TableNode, + TableCellNode, + TableRowNode, + AutoLinkNode, + LinkNode, + ], +}; + +export default function RichEditor(props) { return ( - - - + +
+ +
+ } + placeholder={} + ErrorBoundary={LexicalErrorBoundary} + /> + + + + + + + + +
+
+
); } diff --git a/src/index.css b/src/index.css index 50c1ed7..2008bd4 100644 --- a/src/index.css +++ b/src/index.css @@ -53,7 +53,7 @@ background-color: var(--semi-color-bg-3); } -.card-theme{ +.card-theme { color: var(--semi-color-text-1); background-color: rgba(var(--semi-grey-0), 1); } @@ -85,3 +85,747 @@ .table-border { border-color: rgba(var(--semi-grey-2), 1); } + +.ltr { + text-align: left; +} + +.rtl { + text-align: right; +} + +.editor-container { + margin: 20px auto 20px auto; + border-radius: 6px; + color: var(--semi-color-text-1); + background-color: rgba(var(--semi-grey-1), 1); + position: relative; + line-height: 20px; + font-weight: 400; + text-align: left; +} + +.editor-inner { + background-color: rgba(var(--semi-grey-1), 1); + position: relative; + border-radius: 6px; +} + +.editor-input { + min-height: 150px; + resize: none; + font-size: 15px; + position: relative; + tab-size: 1; + outline: 0; + padding: 15px 10px; +} + +.editor-placeholder { + color: #999; + position: absolute; + text-overflow: ellipsis; + top: 15px; + left: 10px; + font-size: 15px; + user-select: none; + display: inline-block; + pointer-events: none; +} + +.editor-text-bold { + font-weight: bold; +} + +.editor-text-italic { + font-style: italic; +} + +.editor-text-underline { + text-decoration: underline; +} + +.editor-text-strikethrough { + text-decoration: line-through; +} + +.editor-text-underlineStrikethrough { + text-decoration: underline line-through; +} + +.editor-text-code { + background-color: rgba(var(--semi-grey-2), 1); + padding: 1px 0.25rem; + font-family: Menlo, Consolas, Monaco, monospace; + font-size: 94%; +} + +.editor-link { + color: rgb(33, 111, 219); + text-decoration: none; +} + +.editor-code { + background-color: rgba(var(--semi-grey-0), 1); + font-family: Menlo, Consolas, Monaco, monospace; + display: block; + padding: 8px 8px 8px 52px; + line-height: 1.53; + font-size: 13px; + margin: 0; + margin-top: 8px; + margin-bottom: 8px; + tab-size: 2; + overflow-x: auto; + position: relative; +} + +.editor-code:before { + content: attr(data-gutter); + position: absolute; + background-color: rgba(var(--semi-grey-0), 1); + left: 0; + top: 0; + border-right: 1px solid rgba(var(--semi-grey-3), 1); + padding: 8px; + color: #777; + white-space: pre-wrap; + text-align: right; + min-width: 25px; +} + +.editor-code:after { + content: attr(data-highlight-language); + top: 0; + right: 3px; + padding: 3px; + font-size: 10px; + text-transform: uppercase; + position: absolute; + color: rgba(var(--semi-text-1), 1); +} + +.editor-tokenComment { + color: slategray; +} + +.editor-tokenPunctuation { + color: #999; +} + +.editor-tokenProperty { + color: #905; +} + +.editor-tokenSelector { + color: #690; +} + +.editor-tokenOperator { + color: #9a6e3a; +} + +.editor-tokenAttr { + color: #07a; +} + +.editor-tokenVariable { + color: #e90; +} + +.editor-tokenFunction { + color: #dd4a68; +} + +.editor-paragraph { + margin: 0; + margin-bottom: 8px; + position: relative; +} + +.editor-paragraph:last-child { + margin-bottom: 0; +} + +.editor-heading-h1 { + font-size: 24px; + margin: 0; + margin-bottom: 12px; + padding: 0; +} + +.editor-heading-h2 { + font-size: 16px; + margin: 0; + margin-top: 10px; + padding: 0; +} + +.editor-quote { + margin: 0; + margin-left: 20px; + font-size: 15px; + color: rgb(101, 103, 107); + border-left-color: rgb(206, 208, 212); + border-left-width: 4px; + border-left-style: solid; + padding-left: 16px; +} + +.editor-list-ol { + padding: 0; + margin: 0; + margin-left: 16px; + list-style-type: decimal; +} + +.editor-list-ul { + padding: 0; + margin: 0; + margin-left: 16px; +} + +.editor-listitem { + margin: 8px 32px 8px 32px; +} + +.editor-nested-listitem { + list-style-type: none; +} + +pre::-webkit-scrollbar { + background: transparent; + width: 10px; +} + +pre::-webkit-scrollbar-thumb { + background: #999; +} + +.toolbar { + display: flex; + flex-wrap: wrap; + margin-bottom: 1px; + background-color: rgba(var(--semi-grey-1), 1); + padding: 4px; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + border-bottom: rgba(var(--semi-grey-2), 1) solid 2px; + vertical-align: middle; +} + +.toolbar button.toolbar-item { + border: 0; + display: flex; + border-radius: 6px; + padding: 8px; + cursor: pointer; + vertical-align: middle; + fill: currentColor; +} + +.toolbar button.toolbar-item:disabled { + cursor: not-allowed; +} + +.toolbar button.toolbar-item.spaced { + margin-right: 2px; +} + +.toolbar button.toolbar-item i.format { + background-size: contain; + display: inline-block; + height: 18px; + width: 18px; + margin-top: 2px; + vertical-align: -0.25em; + display: flex; + opacity: 1; + fill: currentColor; +} + +.toolbar button.toolbar-item:disabled i.format { + opacity: 0.4; +} + +.toolbar button.toolbar-item.active { + background-color: rgba(var(--semi-grey-2), 1); +} + +.toolbar button.toolbar-item.active i { + opacity: 1; +} + +.toolbar .toolbar-item:hover:not([disabled]) { + background-color: rgba(var(--semi-grey-2), 1); +} + +.toolbar .divider { + width: 2px; + background-color: rgba(var(--semi-grey-2), 1); + margin: 4px 4px; +} + +.toolbar select.toolbar-item { + border-radius: 6px; + padding: 8px; + vertical-align: middle; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + font-size: 14px; + background-color: rgba(var(--semi-grey-1), 1); + text-overflow: ellipsis; + outline: none; +} + +.toolbar select.code-language { + text-transform: capitalize; + width: 130px; +} + +.toolbar .toolbar-item .text { + line-height: 20px; + width: 200px; + vertical-align: middle; + font-size: 14px; + color: rgba(var(--semi-text-1), 1); + text-overflow: ellipsis; + width: 70px; + overflow: hidden; + height: 20px; + text-align: left; +} + +.toolbar .toolbar-item .icon { + width: 20px; + height: 20px; + user-select: none; + margin-right: 8px; + line-height: 16px; + background-size: contain; +} + +.toolbar i.chevron-down { + margin-top: 3px; + margin-left: 6px; + width: 16px; + height: 16px; + user-select: none; + background-image: url(/public/images/icons/chevron-down.svg); +} + +.toolbar i.chevron-down-dark { + margin-top: 3px; + margin-left: 6px; + width: 16px; + height: 16px; + user-select: none; + background-image: url(/public/images/icons/chevron-down-dark.svg); +} + +.toolbar i.chevron-down.inside { + width: 16px; + height: 16px; + margin-left: -20px; + margin-top: 11px; + margin-right: 10px; + pointer-events: none; +} + +.toolbar i.chevron-down-dark.inside { + width: 16px; + height: 16px; + margin-left: -20px; + margin-top: 11px; + margin-right: 10px; + pointer-events: none; +} + +#block-controls button:focus-visible { + border-color: blue; +} + +#block-controls span.block-type { + background-size: contain; + display: block; + width: 18px; + height: 18px; + margin: 2px; +} + +#block-controls span.block-type.paragraph { + background-image: url(/public/images/icons/text-paragraph.svg); +} + +#block-controls span.block-type.paragraph-dark { + background-image: url(/public/images/icons/text-paragraph-dark.svg); +} + +#block-controls span.block-type.h1 { + background-image: url(/public/images/icons/type-h1.svg); +} + +#block-controls span.block-type.h1-dark { + background-image: url(/public/images/icons/type-h1-dark.svg); +} + +#block-controls span.block-type.h2 { + background-image: url(/public/images/icons/type-h2.svg); +} + +#block-controls span.block-type.h2-dark { + background-image: url(/public/images/icons/type-h2-dark.svg); +} + +#block-controls span.block-type.quote { + background-image: url(/public/images/icons/chat-square-quote.svg); +} + +#block-controls span.block-type.quote-dark { + background-image: url(/public/images/icons/chat-square-quote-dark.svg); +} + +#block-controls span.block-type.ul { + background-image: url(/public/images/icons/list-ul.svg); +} + +#block-controls span.block-type.ul-dark { + background-image: url(/public/images/icons/list-ul-dark.svg); +} + +#block-controls span.block-type.ol { + background-image: url(/public/images/icons/list-ol.svg); +} + +#block-controls span.block-type.ol-dark { + background-image: url(/public/images/icons/list-ol-dark.svg); +} + +#block-controls span.block-type.code { + background-image: url(/public/images/icons/code.svg); +} + +#block-controls span.block-type.code-dark { + background-image: url(/public/images/icons/code-dark.svg); +} + +.dropdown { + z-index: 5; + display: block; + position: absolute; + box-shadow: 0 12px 28px 0 rgba(0, 0, 0, 0.2), 0 2px 4px 0 rgba(0, 0, 0, 0.1), + inset 0 0 0 1px rgba(var(--semi-grey-2), 1); + border-radius: 6px; + min-width: 240px; + min-height: 40px; + background-color: rgba(var(--semi-grey-1), 1); +} + +.dropdown .item { + padding: 6px 16px; + width: 100%; + color: rgba(var(--semi-text-1), 1); + cursor: pointer; + line-height: 16px; + font-size: 14px; + display: flex; + align-content: center; + flex-direction: row; + flex-shrink: 0; + justify-content: space-between; + border: 0; +} + +.dropdown .item .active { + display: flex; + width: 20px; + height: 20px; +} + +.dropdown .item:first-child { + margin-top: 8px; +} + +.dropdown .item:last-child { + margin-bottom: 8px; +} + +.dropdown .item:hover { + background-color: rgba(var(--semi-grey-3), 1); +} + +.dropdown .item .text { + display: flex; + line-height: 20px; + flex-grow: 1; +} + +.dropdown .item .icon { + display: flex; + width: 20px; + height: 20px; + user-select: none; + margin-right: 12px; + line-height: 16px; + background-size: contain; +} + +.link-editor { + position: absolute; + z-index: 100; + top: -10000px; + left: -10000px; + margin-top: -6px; + max-width: 300px; + width: 100%; + opacity: 0; + background-color: rgba(var(--semi-grey-1), 1); + box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.3); + border-radius: 6px; + transition: opacity 0.5s; +} + +.link-editor .link-input { + display: block; + width: calc(100% - 24px); + box-sizing: border-box; + margin: 8px 12px; + padding: 8px 12px; + border-radius: 6px; + background-color: rgba(var(--semi-grey-2), 1); + font-size: 15px; + color: var(--semi-color-text-1); + border: 0; + outline: 0; + position: relative; + font-family: inherit; +} + +.link-edit { + background-image: url(/public/images/icons/pencil-fill.svg); + background-size: 16px; + background-position: center; + background-repeat: no-repeat; + width: 35px; + vertical-align: -0.25em; + position: absolute; + right: 0; + top: 0; + bottom: 0; + cursor: pointer; +} + +.link-edit-dark { + background-image: url(/public/images/icons/pencil-fill-dark.svg); + background-size: 16px; + background-position: center; + background-repeat: no-repeat; + width: 35px; + vertical-align: -0.25em; + position: absolute; + right: 0; + top: 0; + bottom: 0; + cursor: pointer; +} + +.link-editor .link-input a { + color: rgb(33, 111, 219); + text-decoration: none; + display: block; + white-space: nowrap; + overflow: hidden; + margin-right: 30px; + text-overflow: ellipsis; +} + +.link-editor .link-input a:hover { + text-decoration: underline; +} + +.link-editor .button { + width: 20px; + height: 20px; + display: inline-block; + padding: 6px; + border-radius: 8px; + cursor: pointer; + margin: 0 2px; +} + +.link-editor .button.hovered { + width: 20px; + height: 20px; + display: inline-block; + background-color: #eee; +} + +.link-editor .button i, +.actions i { + background-size: contain; + display: inline-block; + height: 20px; + width: 20px; + vertical-align: -0.25em; +} + +i.undo { + background-image: url(/public/images/icons/arrow-counterclockwise.svg); +} + +i.undo-dark { + background-image: url(/public/images/icons/arrow-counterclockwise-dark.svg); +} + +i.redo { + background-image: url(/public/images/icons/arrow-clockwise.svg); +} + +i.redo-dark { + background-image: url(/public/images/icons/arrow-clockwise-dark.svg); +} + +.icon.paragraph { + background-image: url(/public/images/icons/text-paragraph.svg); +} + +.icon.paragraph-dark { + background-image: url(/public/images/icons/text-paragraph-dark.svg); +} + +.icon.large-heading, +.icon.h1 { + background-image: url(/public/images/icons/type-h1.svg); +} + +.icon.large-heading-dark, +.icon.h1-dark { + background-image: url(/public/images/icons/type-h1-dark.svg); +} + +.icon.small-heading, +.icon.h2 { + background-image: url(/public/images/icons/type-h2.svg); +} + +.icon.small-heading-dark, +.icon.h2-dark { + background-image: url(/public/images/icons/type-h2-dark.svg); +} + +.icon.bullet-list, +.icon.ul { + background-image: url(/public/images/icons/list-ul.svg); +} + +.icon.bullet-list-dark, +.icon.ul-dark { + background-image: url(/public/images/icons/list-ul-dark.svg); +} + +.icon.numbered-list, +.icon.ol { + background-image: url(/public/images/icons/list-ol.svg); +} + +.icon.numbered-list-dark, +.icon.ol-dark { + background-image: url(/public/images/icons/list-ol-dark.svg); +} + +.icon.quote { + background-image: url(/public/images/icons/chat-square-quote.svg); +} + +.icon.quote-dark { + background-image: url(/public/images/icons/chat-square-quote-dark.svg); +} + +.icon.code { + background-image: url(/public/images/icons/code.svg); +} + +.icon.code-dark { + background-image: url(/public/images/icons/code-dark.svg); +} + +i.bold { + background-image: url(/public/images/icons/type-bold.svg); +} + +i.bold-dark { + background-image: url(/public/images/icons/type-bold-dark.svg); +} + +i.italic { + background-image: url(/public/images/icons/type-italic.svg); +} + +i.italic-dark { + background-image: url(/public/images/icons/type-italic-dark.svg); +} + +i.underline { + background-image: url(/public/images/icons/type-underline.svg); +} + +i.underline-dark { + background-image: url(/public/images/icons/type-underline-dark.svg); +} + +i.strikethrough { + background-image: url(/public/images/icons/type-strikethrough.svg); +} + +i.strikethrough-dark { + background-image: url(/public/images/icons/type-strikethrough-dark.svg); +} + +i.code { + background-image: url(/public/images/icons/code.svg); +} + +i.code-dark { + background-image: url(/public/images/icons/code-dark.svg); +} + +i.link { + background-image: url(/public/images/icons/link.svg); +} + +i.link-dark { + background-image: url(/public/images/icons/link-dark.svg); +} + +i.left-align { + background-image: url(/public/images/icons/text-left.svg); +} + +i.left-align-dark { + background-image: url(/public/images/icons/text-left-dark.svg); +} + +i.center-align { + background-image: url(/public/images/icons/text-center.svg); +} + +i.center-align-dark { + background-image: url(/public/images/icons/text-center-dark.svg); +} + +i.right-align { + background-image: url(/public/images/icons/text-right.svg); +} + +i.right-align-dark { + background-image: url(/public/images/icons/text-right-dark.svg); +} + +i.justify-align { + background-image: url(/public/images/icons/justify.svg); +} + +i.justify-align-dark { + background-image: url(/public/images/icons/justify-dark.svg); +} diff --git a/src/pages/bug_report.jsx b/src/pages/bug_report.jsx index 3544f1b..d7e2d30 100644 --- a/src/pages/bug_report.jsx +++ b/src/pages/bug_report.jsx @@ -80,68 +80,70 @@ export default function BugReport() { } my-1`} />
-
-
- -
Describe the bug
+
+
+
+ +
Describe the bug
+
+
+ Please provide a clear and concise description of what the bug is. +
+
+ +
Steps to reproduce the bug
+
+
+ Please provide the steps of how to reproduce the bug. +
+
+ +
Expected behaviour
+
+
+ Tell us what you expected to see vs what you saw. +
+
+ +
Your browser and device
+
+
+ What web browser and device did you encounter the bug on. +
+
+ +
Screenshots
+
+
+ Add any relevant images if possible. +
+
+
+
Alternatively
+
+
+
-
- Please provide a clear and concise description of what the bug is. -
-
- -
Steps to reproduce the bug
-
-
- Please provide the steps of how to reproduce the bug. -
-
- -
Expected behaviour
-
-
- Tell us what you expected to see vs what you saw. -
-
- -
Your browser and device
-
-
- What web browser and device did you encounter the bug on. -
-
- -
Screenshots
-
-
- Add any relevant images if possible. -
-
-
-
Alternatively
-
-
-
- + {}} diff --git a/src/plugins/AutoLinkPlugin.js b/src/plugins/AutoLinkPlugin.js new file mode 100644 index 0000000..3230c3d --- /dev/null +++ b/src/plugins/AutoLinkPlugin.js @@ -0,0 +1,36 @@ +import { AutoLinkPlugin } from "@lexical/react/LexicalAutoLinkPlugin"; + +const URL_MATCHER = + /((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/; + +const EMAIL_MATCHER = + /(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/; + +const MATCHERS = [ + (text) => { + const match = URL_MATCHER.exec(text); + return ( + match && { + index: match.index, + length: match[0].length, + text: match[0], + url: match[0], + } + ); + }, + (text) => { + const match = EMAIL_MATCHER.exec(text); + return ( + match && { + index: match.index, + length: match[0].length, + text: match[0], + url: `mailto:${match[0]}`, + } + ); + }, +]; + +export default function PlaygroundAutoLinkPlugin() { + return ; +} diff --git a/src/plugins/CodeHighlightPlugin.js b/src/plugins/CodeHighlightPlugin.js new file mode 100644 index 0000000..f931805 --- /dev/null +++ b/src/plugins/CodeHighlightPlugin.js @@ -0,0 +1,11 @@ +import { registerCodeHighlighting } from "@lexical/code"; +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { useEffect } from "react"; + +export default function CodeHighlightPlugin() { + const [editor] = useLexicalComposerContext(); + useEffect(() => { + return registerCodeHighlighting(editor); + }, [editor]); + return null; +} diff --git a/src/plugins/ListMaxIndentLevelPlugin.js b/src/plugins/ListMaxIndentLevelPlugin.js new file mode 100644 index 0000000..553e4f4 --- /dev/null +++ b/src/plugins/ListMaxIndentLevelPlugin.js @@ -0,0 +1,68 @@ +import { $getListDepth, $isListItemNode, $isListNode } from "@lexical/list"; +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { + $getSelection, + $isElementNode, + $isRangeSelection, + INDENT_CONTENT_COMMAND, + COMMAND_PRIORITY_HIGH, +} from "lexical"; +import { useEffect } from "react"; + +function getElementNodesInSelection(selection) { + const nodesInSelection = selection.getNodes(); + + if (nodesInSelection.length === 0) { + return new Set([ + selection.anchor.getNode().getParentOrThrow(), + selection.focus.getNode().getParentOrThrow(), + ]); + } + + return new Set( + nodesInSelection.map((n) => ($isElementNode(n) ? n : n.getParentOrThrow())) + ); +} + +function isIndentPermitted(maxDepth) { + const selection = $getSelection(); + + if (!$isRangeSelection(selection)) { + return false; + } + + const elementNodesInSelection = getElementNodesInSelection(selection); + + let totalDepth = 0; + + for (const elementNode of elementNodesInSelection) { + if ($isListNode(elementNode)) { + totalDepth = Math.max($getListDepth(elementNode) + 1, totalDepth); + } else if ($isListItemNode(elementNode)) { + const parent = elementNode.getParent(); + if (!$isListNode(parent)) { + throw new Error( + "ListMaxIndentLevelPlugin: A ListItemNode must have a ListNode for a parent." + ); + } + + totalDepth = Math.max($getListDepth(parent) + 1, totalDepth); + } + } + + return totalDepth <= maxDepth; +} + +export default function ListMaxIndentLevelPlugin({ maxDepth }) { + const [editor] = useLexicalComposerContext(); + + useEffect(() => { + return editor.registerCommand( + INDENT_CONTENT_COMMAND, + () => !isIndentPermitted(maxDepth ?? 7), + COMMAND_PRIORITY_HIGH + ); + }, [editor, maxDepth]); + + return null; +} diff --git a/src/plugins/ToolbarPlugin.js b/src/plugins/ToolbarPlugin.js new file mode 100644 index 0000000..a863c8f --- /dev/null +++ b/src/plugins/ToolbarPlugin.js @@ -0,0 +1,740 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { + CAN_REDO_COMMAND, + CAN_UNDO_COMMAND, + REDO_COMMAND, + UNDO_COMMAND, + SELECTION_CHANGE_COMMAND, + FORMAT_TEXT_COMMAND, + FORMAT_ELEMENT_COMMAND, + $getSelection, + $isRangeSelection, + $createParagraphNode, + $getNodeByKey, +} from "lexical"; +import { $isLinkNode, TOGGLE_LINK_COMMAND } from "@lexical/link"; +import { + $isParentElementRTL, + $wrapNodes, + $isAtNodeEnd, +} from "@lexical/selection"; +import { $getNearestNodeOfType, mergeRegister } from "@lexical/utils"; +import { + INSERT_ORDERED_LIST_COMMAND, + INSERT_UNORDERED_LIST_COMMAND, + REMOVE_LIST_COMMAND, + $isListNode, + ListNode, +} from "@lexical/list"; +import { createPortal } from "react-dom"; +import { + $createHeadingNode, + $createQuoteNode, + $isHeadingNode, +} from "@lexical/rich-text"; +import { + $createCodeNode, + $isCodeNode, + getDefaultCodeLanguage, + getCodeLanguages, +} from "@lexical/code"; + +const LowPriority = 1; + +const supportedBlockTypes = new Set([ + "paragraph", + "quote", + "code", + "h1", + "h2", + "ul", + "ol", +]); + +const blockTypeToBlockName = { + code: "Code Block", + h1: "Large Heading", + h2: "Small Heading", + h3: "Heading", + h4: "Heading", + h5: "Heading", + ol: "Numbered List", + paragraph: "Paragraph", + quote: "Quote", + ul: "Bulleted List", +}; + +function Divider() { + return
; +} + +function positionEditorElement(editor, rect) { + if (rect === null) { + editor.style.opacity = "0"; + editor.style.top = "-1000px"; + editor.style.left = "-1000px"; + } else { + editor.style.opacity = "1"; + editor.style.top = `${rect.top + rect.height + window.pageYOffset + 10}px`; + editor.style.left = `${ + rect.left + window.pageXOffset - editor.offsetWidth / 2 + rect.width / 2 + }px`; + } +} + +function FloatingLinkEditor({ editor, theme }) { + const editorRef = useRef(null); + const inputRef = useRef(null); + const mouseDownRef = useRef(false); + const [linkUrl, setLinkUrl] = useState(""); + const [isEditMode, setEditMode] = useState(false); + const [lastSelection, setLastSelection] = useState(null); + + const updateLinkEditor = useCallback(() => { + const selection = $getSelection(); + if ($isRangeSelection(selection)) { + const node = getSelectedNode(selection); + const parent = node.getParent(); + if ($isLinkNode(parent)) { + setLinkUrl(parent.getURL()); + } else if ($isLinkNode(node)) { + setLinkUrl(node.getURL()); + } else { + setLinkUrl(""); + } + } + const editorElem = editorRef.current; + const nativeSelection = window.getSelection(); + const activeElement = document.activeElement; + + if (editorElem === null) { + return; + } + + const rootElement = editor.getRootElement(); + if ( + selection !== null && + !nativeSelection.isCollapsed && + rootElement !== null && + rootElement.contains(nativeSelection.anchorNode) + ) { + const domRange = nativeSelection.getRangeAt(0); + let rect; + if (nativeSelection.anchorNode === rootElement) { + let inner = rootElement; + while (inner.firstElementChild != null) { + inner = inner.firstElementChild; + } + rect = inner.getBoundingClientRect(); + } else { + rect = domRange.getBoundingClientRect(); + } + + if (!mouseDownRef.current) { + positionEditorElement(editorElem, rect); + } + setLastSelection(selection); + } else if (!activeElement || activeElement.className !== "link-input") { + positionEditorElement(editorElem, null); + setLastSelection(null); + setEditMode(false); + setLinkUrl(""); + } + + return true; + }, [editor]); + + useEffect(() => { + return mergeRegister( + editor.registerUpdateListener(({ editorState }) => { + editorState.read(() => { + updateLinkEditor(); + }); + }), + + editor.registerCommand( + SELECTION_CHANGE_COMMAND, + () => { + updateLinkEditor(); + return true; + }, + LowPriority + ) + ); + }, [editor, updateLinkEditor]); + + useEffect(() => { + editor.getEditorState().read(() => { + updateLinkEditor(); + }); + }, [editor, updateLinkEditor]); + + useEffect(() => { + if (isEditMode && inputRef.current) { + inputRef.current.focus(); + } + }, [isEditMode]); + + return ( +
+ {isEditMode ? ( + { + setLinkUrl(event.target.value); + }} + onKeyDown={(event) => { + if (event.key === "Enter") { + event.preventDefault(); + if (lastSelection !== null) { + if (linkUrl !== "") { + editor.dispatchCommand(TOGGLE_LINK_COMMAND, linkUrl); + } + setEditMode(false); + } + } else if (event.key === "Escape") { + event.preventDefault(); + setEditMode(false); + } + }} + /> + ) : ( + <> +
+ + {linkUrl} + +
event.preventDefault()} + onClick={() => { + setEditMode(true); + }} + /> +
+ + )} +
+ ); +} + +function Select({ onChange, className, options, value }) { + return ( + + ); +} + +function getSelectedNode(selection) { + const anchor = selection.anchor; + const focus = selection.focus; + const anchorNode = selection.anchor.getNode(); + const focusNode = selection.focus.getNode(); + if (anchorNode === focusNode) { + return anchorNode; + } + const isBackward = selection.isBackward(); + if (isBackward) { + return $isAtNodeEnd(focus) ? anchorNode : focusNode; + } else { + return $isAtNodeEnd(anchor) ? focusNode : anchorNode; + } +} + +function BlockOptionsDropdownList({ + editor, + blockType, + toolbarRef, + setShowBlockOptionsDropDown, + theme, +}) { + const dropDownRef = useRef(null); + + useEffect(() => { + const toolbar = toolbarRef.current; + const dropDown = dropDownRef.current; + + if (toolbar !== null && dropDown !== null) { + const { top, left } = toolbar.getBoundingClientRect(); + dropDown.style.top = `${top + 40}px`; + dropDown.style.left = `${left}px`; + } + }, [dropDownRef, toolbarRef]); + + useEffect(() => { + const dropDown = dropDownRef.current; + const toolbar = toolbarRef.current; + + if (dropDown !== null && toolbar !== null) { + const handle = (event) => { + const target = event.target; + + if (!dropDown.contains(target) && !toolbar.contains(target)) { + setShowBlockOptionsDropDown(false); + } + }; + document.addEventListener("click", handle); + + return () => { + document.removeEventListener("click", handle); + }; + } + }, [dropDownRef, setShowBlockOptionsDropDown, toolbarRef]); + + const formatParagraph = () => { + if (blockType !== "paragraph") { + editor.update(() => { + const selection = $getSelection(); + + if ($isRangeSelection(selection)) { + $wrapNodes(selection, () => $createParagraphNode()); + } + }); + } + setShowBlockOptionsDropDown(false); + }; + + const formatLargeHeading = () => { + if (blockType !== "h1") { + editor.update(() => { + const selection = $getSelection(); + + if ($isRangeSelection(selection)) { + $wrapNodes(selection, () => $createHeadingNode("h1")); + } + }); + } + setShowBlockOptionsDropDown(false); + }; + + const formatSmallHeading = () => { + if (blockType !== "h2") { + editor.update(() => { + const selection = $getSelection(); + + if ($isRangeSelection(selection)) { + $wrapNodes(selection, () => $createHeadingNode("h2")); + } + }); + } + setShowBlockOptionsDropDown(false); + }; + + const formatBulletList = () => { + if (blockType !== "ul") { + editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND); + } else { + editor.dispatchCommand(REMOVE_LIST_COMMAND); + } + setShowBlockOptionsDropDown(false); + }; + + const formatNumberedList = () => { + if (blockType !== "ol") { + editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND); + } else { + editor.dispatchCommand(REMOVE_LIST_COMMAND); + } + setShowBlockOptionsDropDown(false); + }; + + const formatQuote = () => { + if (blockType !== "quote") { + editor.update(() => { + const selection = $getSelection(); + + if ($isRangeSelection(selection)) { + $wrapNodes(selection, () => $createQuoteNode()); + } + }); + } + setShowBlockOptionsDropDown(false); + }; + + const formatCode = () => { + if (blockType !== "code") { + editor.update(() => { + const selection = $getSelection(); + + if ($isRangeSelection(selection)) { + $wrapNodes(selection, () => $createCodeNode()); + } + }); + } + setShowBlockOptionsDropDown(false); + }; + + return ( +
+ + + + + + + +
+ ); +} + +export default function ToolbarPlugin(props) { + const { theme } = props; + const [editor] = useLexicalComposerContext(); + const toolbarRef = useRef(null); + const [canUndo, setCanUndo] = useState(false); + const [canRedo, setCanRedo] = useState(false); + const [blockType, setBlockType] = useState("paragraph"); + const [selectedElementKey, setSelectedElementKey] = useState(null); + const [showBlockOptionsDropDown, setShowBlockOptionsDropDown] = + useState(false); + const [codeLanguage, setCodeLanguage] = useState(""); + const [, setIsRTL] = useState(false); + const [isLink, setIsLink] = useState(false); + const [isBold, setIsBold] = useState(false); + const [isItalic, setIsItalic] = useState(false); + const [isUnderline, setIsUnderline] = useState(false); + const [isStrikethrough, setIsStrikethrough] = useState(false); + const [isCode, setIsCode] = useState(false); + + const updateToolbar = useCallback(() => { + const selection = $getSelection(); + if ($isRangeSelection(selection)) { + const anchorNode = selection.anchor.getNode(); + const element = + anchorNode.getKey() === "root" + ? anchorNode + : anchorNode.getTopLevelElementOrThrow(); + const elementKey = element.getKey(); + const elementDOM = editor.getElementByKey(elementKey); + if (elementDOM !== null) { + setSelectedElementKey(elementKey); + if ($isListNode(element)) { + const parentList = $getNearestNodeOfType(anchorNode, ListNode); + const type = parentList ? parentList.getTag() : element.getTag(); + setBlockType(type); + } else { + const type = $isHeadingNode(element) + ? element.getTag() + : element.getType(); + setBlockType(type); + if ($isCodeNode(element)) { + setCodeLanguage(element.getLanguage() || getDefaultCodeLanguage()); + } + } + } + // Update text format + setIsBold(selection.hasFormat("bold")); + setIsItalic(selection.hasFormat("italic")); + setIsUnderline(selection.hasFormat("underline")); + setIsStrikethrough(selection.hasFormat("strikethrough")); + setIsCode(selection.hasFormat("code")); + setIsRTL($isParentElementRTL(selection)); + + // Update links + const node = getSelectedNode(selection); + const parent = node.getParent(); + if ($isLinkNode(parent) || $isLinkNode(node)) { + setIsLink(true); + } else { + setIsLink(false); + } + } + }, [editor]); + + useEffect(() => { + return mergeRegister( + editor.registerUpdateListener(({ editorState }) => { + editorState.read(() => { + updateToolbar(); + }); + }), + editor.registerCommand( + SELECTION_CHANGE_COMMAND, + (_payload, newEditor) => { + updateToolbar(); + return false; + }, + LowPriority + ), + editor.registerCommand( + CAN_UNDO_COMMAND, + (payload) => { + setCanUndo(payload); + return false; + }, + LowPriority + ), + editor.registerCommand( + CAN_REDO_COMMAND, + (payload) => { + setCanRedo(payload); + return false; + }, + LowPriority + ) + ); + }, [editor, updateToolbar]); + + const codeLanguges = useMemo(() => getCodeLanguages(), []); + const onCodeLanguageSelect = useCallback( + (e) => { + editor.update(() => { + if (selectedElementKey !== null) { + const node = $getNodeByKey(selectedElementKey); + if ($isCodeNode(node)) { + node.setLanguage(e.target.value); + } + } + }); + }, + [editor, selectedElementKey] + ); + + const insertLink = useCallback(() => { + if (!isLink) { + editor.dispatchCommand(TOGGLE_LINK_COMMAND, "https://"); + } else { + editor.dispatchCommand(TOGGLE_LINK_COMMAND, null); + } + }, [editor, isLink]); + + return ( +
+ + + + {supportedBlockTypes.has(blockType) && ( + <> + + {showBlockOptionsDropDown && + createPortal( + , + document.body + )} + + + )} + {blockType === "code" ? ( + <> +