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
+
+
+
+
+ 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.
+
+
+
+ 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.
+
+
+
+ Add any relevant images if possible.
+
+
+
}
+ style={{ backgroundColor: "#239144", color: "white" }}
+ onClick={() => {
+ window.open(
+ "https://github.com/drawdb-io/drawdb-issues/issues",
+ "_self"
+ );
+ }}
+ >
+ Add an issue
+
-
- 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.
-
-
-
- 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.
-
-
-
- Add any relevant images if possible.
-
-
-
}
- style={{ backgroundColor: "#239144", color: "white" }}
- onClick={() => {
- window.open(
- "https://github.com/drawdb-io/drawdb-issues/issues",
- "_self"
- );
- }}
- >
- Add an issue
-
-
+
{}}
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" ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+
+
+
+
+ {isLink &&
+ createPortal(
+
,
+ document.body
+ )}
+
+
+
+
+
{" "}
+ >
+ )}
+
+ );
+}