Add rich text editot
349
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
BIN
public/images/emoji/1F600.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
public/images/emoji/1F641.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
public/images/emoji/1F642.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
public/images/emoji/2764.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
5
public/images/emoji/LICENSE.md
Normal file
@ -0,0 +1,5 @@
|
||||
OpenMoji
|
||||
https://openmoji.org
|
||||
|
||||
Licensed under Attribution-ShareAlike 4.0 International
|
||||
https://creativecommons.org/licenses/by-sa/4.0/
|
5
public/images/icons/LICENSE.md
Normal file
@ -0,0 +1,5 @@
|
||||
Bootstrap Icons
|
||||
https://icons.getbootstrap.com
|
||||
|
||||
Licensed under MIT license
|
||||
https://github.com/twbs/icons/blob/main/LICENSE.md
|
4
public/images/icons/arrow-clockwise-dark.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 349 B |
4
public/images/icons/arrow-clockwise.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 353 B |
4
public/images/icons/arrow-counterclockwise-dark.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 356 B |
4
public/images/icons/arrow-counterclockwise.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 360 B |
4
public/images/icons/chat-square-quote-dark.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-chat-square-quote" viewBox="0 0 16 16">
|
||||
<path d="M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-2.5a2 2 0 0 0-1.6.8L8 14.333 6.1 11.8a2 2 0 0 0-1.6-.8H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2.5a1 1 0 0 1 .8.4l1.9 2.533a1 1 0 0 0 1.6 0l1.9-2.533a1 1 0 0 1 .8-.4H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
<path d="M7.066 4.76A1.665 1.665 0 0 0 4 5.668a1.667 1.667 0 0 0 2.561 1.406c-.131.389-.375.804-.777 1.22a.417.417 0 1 0 .6.58c1.486-1.54 1.293-3.214.682-4.112zm4 0A1.665 1.665 0 0 0 8 5.668a1.667 1.667 0 0 0 2.561 1.406c-.131.389-.375.804-.777 1.22a.417.417 0 1 0 .6.58c1.486-1.54 1.293-3.214.682-4.112z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 732 B |
4
public/images/icons/chat-square-quote.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-chat-square-quote" viewBox="0 0 16 16">
|
||||
<path d="M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-2.5a2 2 0 0 0-1.6.8L8 14.333 6.1 11.8a2 2 0 0 0-1.6-.8H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2.5a1 1 0 0 1 .8.4l1.9 2.533a1 1 0 0 0 1.6 0l1.9-2.533a1 1 0 0 1 .8-.4H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
<path d="M7.066 4.76A1.665 1.665 0 0 0 4 5.668a1.667 1.667 0 0 0 2.561 1.406c-.131.389-.375.804-.777 1.22a.417.417 0 1 0 .6.58c1.486-1.54 1.293-3.214.682-4.112zm4 0A1.665 1.665 0 0 0 8 5.668a1.667 1.667 0 0 0 2.561 1.406c-.131.389-.375.804-.777 1.22a.417.417 0 1 0 .6.58c1.486-1.54 1.293-3.214.682-4.112z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 736 B |
3
public/images/icons/chevron-down-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-chevron-down" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 287 B |
3
public/images/icons/chevron-down.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-chevron-down" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 291 B |
3
public/images/icons/code-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-code" viewBox="0 0 16 16">
|
||||
<path d="M5.854 4.854a.5.5 0 1 0-.708-.708l-3.5 3.5a.5.5 0 0 0 0 .708l3.5 3.5a.5.5 0 0 0 .708-.708L2.707 8l3.147-3.146zm4.292 0a.5.5 0 0 1 .708-.708l3.5 3.5a.5.5 0 0 1 0 .708l-3.5 3.5a.5.5 0 0 1-.708-.708L13.293 8l-3.147-3.146z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 359 B |
3
public/images/icons/code.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-code" viewBox="0 0 16 16">
|
||||
<path d="M5.854 4.854a.5.5 0 1 0-.708-.708l-3.5 3.5a.5.5 0 0 0 0 .708l3.5 3.5a.5.5 0 0 0 .708-.708L2.707 8l3.147-3.146zm4.292 0a.5.5 0 0 1 .708-.708l3.5 3.5a.5.5 0 0 1 0 .708l-3.5 3.5a.5.5 0 0 1-.708-.708L13.293 8l-3.147-3.146z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 363 B |
5
public/images/icons/journal-code.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-journal-code" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8.646 5.646a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708-.708L10.293 8 8.646 6.354a.5.5 0 0 1 0-.708zm-1.292 0a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0 0 .708l2 2a.5.5 0 0 0 .708-.708L5.707 8l1.647-1.646a.5.5 0 0 0 0-.708z"/>
|
||||
<path d="M3 0h10a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-1h1v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v1H1V2a2 2 0 0 1 2-2z"/>
|
||||
<path d="M1 5v-.5a.5.5 0 0 1 1 0V5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1H1zm0 3v-.5a.5.5 0 0 1 1 0V8h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1H1zm0 3v-.5a.5.5 0 0 1 1 0v.5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1H1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 772 B |
5
public/images/icons/journal-text.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-journal-text" viewBox="0 0 16 16">
|
||||
<path d="M5 10.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5zm0-2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0-2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0-2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z"/>
|
||||
<path d="M3 0h10a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-1h1v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v1H1V2a2 2 0 0 1 2-2z"/>
|
||||
<path d="M1 5v-.5a.5.5 0 0 1 1 0V5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1H1zm0 3v-.5a.5.5 0 0 1 1 0V8h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1H1zm0 3v-.5a.5.5 0 0 1 1 0v.5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1H1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 759 B |
3
public/images/icons/justify-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-justify" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2 12.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 411 B |
3
public/images/icons/justify.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-justify" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2 12.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 415 B |
4
public/images/icons/link-dark.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-link" viewBox="0 0 16 16">
|
||||
<path d="M6.354 5.5H4a3 3 0 0 0 0 6h3a3 3 0 0 0 2.83-4H9c-.086 0-.17.01-.25.031A2 2 0 0 1 7 10.5H4a2 2 0 1 1 0-4h1.535c.218-.376.495-.714.82-1z"/>
|
||||
<path d="M9 5.5a3 3 0 0 0-2.83 4h1.098A2 2 0 0 1 9 6.5h3a2 2 0 1 1 0 4h-1.535a4.02 4.02 0 0 1-.82 1H12a3 3 0 1 0 0-6H9z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 400 B |
4
public/images/icons/link.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-link" viewBox="0 0 16 16">
|
||||
<path d="M6.354 5.5H4a3 3 0 0 0 0 6h3a3 3 0 0 0 2.83-4H9c-.086 0-.17.01-.25.031A2 2 0 0 1 7 10.5H4a2 2 0 1 1 0-4h1.535c.218-.376.495-.714.82-1z"/>
|
||||
<path d="M9 5.5a3 3 0 0 0-2.83 4h1.098A2 2 0 0 1 9 6.5h3a2 2 0 1 1 0 4h-1.535a4.02 4.02 0 0 1-.82 1H12a3 3 0 1 0 0-6H9z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 404 B |
4
public/images/icons/list-ol-dark.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-list-ol" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M5 11.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5z"/>
|
||||
<path d="M1.713 11.865v-.474H2c.217 0 .363-.137.363-.317 0-.185-.158-.31-.361-.31-.223 0-.367.152-.373.31h-.59c.016-.467.373-.787.986-.787.588-.002.954.291.957.703a.595.595 0 0 1-.492.594v.033a.615.615 0 0 1 .569.631c.003.533-.502.8-1.051.8-.656 0-1-.37-1.008-.794h.582c.008.178.186.306.422.309.254 0 .424-.145.422-.35-.002-.195-.155-.348-.414-.348h-.3zm-.004-4.699h-.604v-.035c0-.408.295-.844.958-.844.583 0 .96.326.96.756 0 .389-.257.617-.476.848l-.537.572v.03h1.054V9H1.143v-.395l.957-.99c.138-.142.293-.304.293-.508 0-.18-.147-.32-.342-.32a.33.33 0 0 0-.342.338v.041zM2.564 5h-.635V2.924h-.031l-.598.42v-.567l.629-.443h.635V5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 980 B |
4
public/images/icons/list-ol.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-list-ol" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M5 11.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5z"/>
|
||||
<path d="M1.713 11.865v-.474H2c.217 0 .363-.137.363-.317 0-.185-.158-.31-.361-.31-.223 0-.367.152-.373.31h-.59c.016-.467.373-.787.986-.787.588-.002.954.291.957.703a.595.595 0 0 1-.492.594v.033a.615.615 0 0 1 .569.631c.003.533-.502.8-1.051.8-.656 0-1-.37-1.008-.794h.582c.008.178.186.306.422.309.254 0 .424-.145.422-.35-.002-.195-.155-.348-.414-.348h-.3zm-.004-4.699h-.604v-.035c0-.408.295-.844.958-.844.583 0 .96.326.96.756 0 .389-.257.617-.476.848l-.537.572v.03h1.054V9H1.143v-.395l.957-.99c.138-.142.293-.304.293-.508 0-.18-.147-.32-.342-.32a.33.33 0 0 0-.342.338v.041zM2.564 5h-.635V2.924h-.031l-.598.42v-.567l.629-.443h.635V5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 984 B |
3
public/images/icons/list-ul-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-list-ul" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M5 11.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm-3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 444 B |
3
public/images/icons/list-ul.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-list-ul" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M5 11.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm-3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 448 B |
3
public/images/icons/pencil-fill-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-pencil-fill" viewBox="0 0 16 16">
|
||||
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 586 B |
3
public/images/icons/pencil-fill.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-pencil-fill" viewBox="0 0 16 16">
|
||||
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 590 B |
3
public/images/icons/text-center-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-text-center" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M4 12.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 413 B |
3
public/images/icons/text-center.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-text-center" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M4 12.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 417 B |
3
public/images/icons/text-left-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-text-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2 12.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 409 B |
3
public/images/icons/text-left.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-text-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2 12.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 413 B |
3
public/images/icons/text-paragraph-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-text-paragraph" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2 12.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 414 B |
3
public/images/icons/text-paragraph.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-text-paragraph" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2 12.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 418 B |
3
public/images/icons/text-right-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-text-right" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M6 12.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-4-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-4-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 412 B |
3
public/images/icons/text-right.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-text-right" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M6 12.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-4-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-4-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 416 B |
3
public/images/icons/type-bold-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-type-bold" viewBox="0 0 16 16">
|
||||
<path d="M8.21 13c2.106 0 3.412-1.087 3.412-2.823 0-1.306-.984-2.283-2.324-2.386v-.055a2.176 2.176 0 0 0 1.852-2.14c0-1.51-1.162-2.46-3.014-2.46H3.843V13H8.21zM5.908 4.674h1.696c.963 0 1.517.451 1.517 1.244 0 .834-.629 1.32-1.73 1.32H5.908V4.673zm0 6.788V8.598h1.73c1.217 0 1.88.492 1.88 1.415 0 .943-.643 1.449-1.832 1.449H5.907z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 467 B |
3
public/images/icons/type-bold.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-type-bold" viewBox="0 0 16 16">
|
||||
<path d="M8.21 13c2.106 0 3.412-1.087 3.412-2.823 0-1.306-.984-2.283-2.324-2.386v-.055a2.176 2.176 0 0 0 1.852-2.14c0-1.51-1.162-2.46-3.014-2.46H3.843V13H8.21zM5.908 4.674h1.696c.963 0 1.517.451 1.517 1.244 0 .834-.629 1.32-1.73 1.32H5.908V4.673zm0 6.788V8.598h1.73c1.217 0 1.88.492 1.88 1.415 0 .943-.643 1.449-1.832 1.449H5.907z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 471 B |
3
public/images/icons/type-h1-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-type-h1" viewBox="0 0 16 16">
|
||||
<path d="M8.637 13V3.669H7.379V7.62H2.758V3.67H1.5V13h1.258V8.728h4.62V13h1.259zm5.329 0V3.669h-1.244L10.5 5.316v1.265l2.16-1.565h.062V13h1.244z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 279 B |
3
public/images/icons/type-h1.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-type-h1" viewBox="0 0 16 16">
|
||||
<path d="M8.637 13V3.669H7.379V7.62H2.758V3.67H1.5V13h1.258V8.728h4.62V13h1.259zm5.329 0V3.669h-1.244L10.5 5.316v1.265l2.16-1.565h.062V13h1.244z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 283 B |
3
public/images/icons/type-h2-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-type-h2" viewBox="0 0 16 16">
|
||||
<path d="M7.638 13V3.669H6.38V7.62H1.759V3.67H.5V13h1.258V8.728h4.62V13h1.259zm3.022-6.733v-.048c0-.889.63-1.668 1.716-1.668.957 0 1.675.608 1.675 1.572 0 .855-.554 1.504-1.067 2.085l-3.513 3.999V13H15.5v-1.094h-4.245v-.075l2.481-2.844c.875-.998 1.586-1.784 1.586-2.953 0-1.463-1.155-2.556-2.919-2.556-1.941 0-2.966 1.326-2.966 2.74v.049h1.223z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 479 B |
3
public/images/icons/type-h2.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-type-h2" viewBox="0 0 16 16">
|
||||
<path d="M7.638 13V3.669H6.38V7.62H1.759V3.67H.5V13h1.258V8.728h4.62V13h1.259zm3.022-6.733v-.048c0-.889.63-1.668 1.716-1.668.957 0 1.675.608 1.675 1.572 0 .855-.554 1.504-1.067 2.085l-3.513 3.999V13H15.5v-1.094h-4.245v-.075l2.481-2.844c.875-.998 1.586-1.784 1.586-2.953 0-1.463-1.155-2.556-2.919-2.556-1.941 0-2.966 1.326-2.966 2.74v.049h1.223z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 483 B |
3
public/images/icons/type-h3.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-type-h3" viewBox="0 0 16 16">
|
||||
<path d="M7.637 13V3.669H6.379V7.62H1.758V3.67H.5V13h1.258V8.728h4.62V13h1.259zm3.625-4.272h1.018c1.142 0 1.935.67 1.949 1.674.013 1.005-.78 1.737-2.01 1.73-1.08-.007-1.853-.588-1.935-1.32H9.108c.069 1.327 1.224 2.386 3.083 2.386 1.935 0 3.343-1.155 3.309-2.789-.027-1.51-1.251-2.16-2.037-2.249v-.068c.704-.123 1.764-.91 1.723-2.229-.035-1.353-1.176-2.4-2.954-2.385-1.873.006-2.857 1.162-2.898 2.358h1.196c.062-.69.711-1.299 1.696-1.299.998 0 1.695.622 1.695 1.525.007.922-.718 1.592-1.695 1.592h-.964v1.074z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 646 B |
3
public/images/icons/type-italic-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-type-italic" viewBox="0 0 16 16">
|
||||
<path d="M7.991 11.674 9.53 4.455c.123-.595.246-.71 1.347-.807l.11-.52H7.211l-.11.52c1.06.096 1.128.212 1.005.807L6.57 11.674c-.123.595-.246.71-1.346.806l-.11.52h3.774l.11-.52c-1.06-.095-1.129-.211-1.006-.806z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 348 B |
3
public/images/icons/type-italic.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-type-italic" viewBox="0 0 16 16">
|
||||
<path d="M7.991 11.674 9.53 4.455c.123-.595.246-.71 1.347-.807l.11-.52H7.211l-.11.52c1.06.096 1.128.212 1.005.807L6.57 11.674c-.123.595-.246.71-1.346.806l-.11.52h3.774l.11-.52c-1.06-.095-1.129-.211-1.006-.806z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 352 B |
3
public/images/icons/type-strikethrough-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-type-strikethrough" viewBox="0 0 16 16">
|
||||
<path d="M6.333 5.686c0 .31.083.581.27.814H5.166a2.776 2.776 0 0 1-.099-.76c0-1.627 1.436-2.768 3.48-2.768 1.969 0 3.39 1.175 3.445 2.85h-1.23c-.11-1.08-.964-1.743-2.25-1.743-1.23 0-2.18.602-2.18 1.607zm2.194 7.478c-2.153 0-3.589-1.107-3.705-2.81h1.23c.144 1.06 1.129 1.703 2.544 1.703 1.34 0 2.31-.705 2.31-1.675 0-.827-.547-1.374-1.914-1.675L8.046 8.5H1v-1h14v1h-3.504c.468.437.675.994.675 1.697 0 1.826-1.436 2.967-3.644 2.967z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 576 B |
3
public/images/icons/type-strikethrough.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 61 85)" class="bi bi-type-strikethrough" viewBox="0 0 16 16">
|
||||
<path d="M6.333 5.686c0 .31.083.581.27.814H5.166a2.776 2.776 0 0 1-.099-.76c0-1.627 1.436-2.768 3.48-2.768 1.969 0 3.39 1.175 3.445 2.85h-1.23c-.11-1.08-.964-1.743-2.25-1.743-1.23 0-2.18.602-2.18 1.607zm2.194 7.478c-2.153 0-3.589-1.107-3.705-2.81h1.23c.144 1.06 1.129 1.703 2.544 1.703 1.34 0 2.31-.705 2.31-1.675 0-.827-.547-1.374-1.914-1.675L8.046 8.5H1v-1h14v1h-3.504c.468.437.675.994.675 1.697 0 1.826-1.436 2.967-3.644 2.967z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 580 B |
3
public/images/icons/type-underline-dark.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgrey" class="bi bi-type-underline" viewBox="0 0 16 16">
|
||||
<path d="M5.313 3.136h-1.23V9.54c0 2.105 1.47 3.623 3.917 3.623s3.917-1.518 3.917-3.623V3.136h-1.23v6.323c0 1.49-.978 2.57-2.687 2.57-1.709 0-2.687-1.08-2.687-2.57V3.136zM12.5 15h-9v-1h9v1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 331 B |
3
public/images/icons/type-underline.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(51 65 85)" class="bi bi-type-underline" viewBox="0 0 16 16">
|
||||
<path d="M5.313 3.136h-1.23V9.54c0 2.105 1.47 3.623 3.917 3.623s3.917-1.518 3.917-3.623V3.136h-1.23v6.323c0 1.49-.978 2.57-2.687 2.57-1.709 0-2.687-1.08-2.687-2.57V3.136zM12.5 15h-9v-1h9v1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 335 B |
@ -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 <div className="editor-placeholder">Describe the bug</div>;
|
||||
}
|
||||
|
||||
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 (
|
||||
<Slate editor={editor} initialValue={initialValue}>
|
||||
<Editable />
|
||||
</Slate>
|
||||
<LexicalComposer initialConfig={editorConfig}>
|
||||
<div className="editor-container">
|
||||
<ToolbarPlugin theme={props.theme} />
|
||||
<div className="editor-inner">
|
||||
<RichTextPlugin
|
||||
contentEditable={<ContentEditable className="editor-input" />}
|
||||
placeholder={<Placeholder />}
|
||||
ErrorBoundary={LexicalErrorBoundary}
|
||||
/>
|
||||
<HistoryPlugin />
|
||||
<AutoFocusPlugin />
|
||||
<CodeHighlightPlugin />
|
||||
<ListPlugin />
|
||||
<LinkPlugin />
|
||||
<AutoLinkPlugin />
|
||||
<ListMaxIndentLevelPlugin maxDepth={7} />
|
||||
<MarkdownShortcutPlugin transformers={TRANSFORMERS} />
|
||||
</div>
|
||||
</div>
|
||||
</LexicalComposer>
|
||||
);
|
||||
}
|
||||
|
744
src/index.css
@ -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);
|
||||
}
|
||||
|
@ -80,7 +80,8 @@ export default function BugReport() {
|
||||
} my-1`}
|
||||
/>
|
||||
<div className="grid grid-cols-12 gap-8 my-6 mx-8">
|
||||
<div className="col-span-4 card-theme p-5 rounded-md">
|
||||
<div className="col-span-4">
|
||||
<div className="card-theme p-6 rounded-md">
|
||||
<div className="flex items-center">
|
||||
<IconPaperclip />
|
||||
<div className="font-bold ms-1">Describe the bug </div>
|
||||
@ -143,6 +144,7 @@ export default function BugReport() {
|
||||
Add an issue
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-8">
|
||||
<Banner
|
||||
fullMode={false}
|
||||
@ -159,7 +161,7 @@ export default function BugReport() {
|
||||
/>
|
||||
<div className="p-5 mt-6 card-theme rounded-md">
|
||||
<Input placeholder="Title" />
|
||||
<RichEditor />
|
||||
<RichEditor theme={theme} />
|
||||
<Upload
|
||||
action="#"
|
||||
beforeUpload={({ file, fileList }) => {}}
|
||||
|
36
src/plugins/AutoLinkPlugin.js
Normal file
@ -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 <AutoLinkPlugin matchers={MATCHERS} />;
|
||||
}
|
11
src/plugins/CodeHighlightPlugin.js
Normal file
@ -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;
|
||||
}
|
68
src/plugins/ListMaxIndentLevelPlugin.js
Normal file
@ -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;
|
||||
}
|
740
src/plugins/ToolbarPlugin.js
Normal file
@ -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 <div className="divider" />;
|
||||
}
|
||||
|
||||
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 (
|
||||
<div ref={editorRef} className="link-editor">
|
||||
{isEditMode ? (
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="link-input"
|
||||
value={linkUrl}
|
||||
onChange={(event) => {
|
||||
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);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<div className="link-input">
|
||||
<a href={linkUrl} target="_blank" rel="noopener noreferrer">
|
||||
{linkUrl}
|
||||
</a>
|
||||
<div
|
||||
className={`link-edit${theme === "dark" ? "-dark" : ""}`}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={() => {
|
||||
setEditMode(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Select({ onChange, className, options, value }) {
|
||||
return (
|
||||
<select className={className} onChange={onChange} value={value}>
|
||||
<option hidden={true} value="" />
|
||||
{options.map((option) => (
|
||||
<option className="option" key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className="dropdown" ref={dropDownRef}>
|
||||
<button className="item" onClick={formatParagraph}>
|
||||
<span className={`icon paragraph${theme === "dark" ? "-dark" : ""}`} />
|
||||
<span className="text">Paragraph</span>
|
||||
{blockType === "paragraph" && <span className="active" />}
|
||||
</button>
|
||||
<button className="item" onClick={formatLargeHeading}>
|
||||
<span
|
||||
className={`icon large-heading${theme === "dark" ? "-dark" : ""}`}
|
||||
/>
|
||||
<span className="text">Large Heading</span>
|
||||
{blockType === "h1" && <span className="active" />}
|
||||
</button>
|
||||
<button className="item" onClick={formatSmallHeading}>
|
||||
<span
|
||||
className={`icon small-heading${theme === "dark" ? "-dark" : ""}`}
|
||||
/>
|
||||
<span className="text">Small Heading</span>
|
||||
{blockType === "h2" && <span className="active" />}
|
||||
</button>
|
||||
<button className="item" onClick={formatBulletList}>
|
||||
<span
|
||||
className={`icon bullet-list${theme === "dark" ? "-dark" : ""}`}
|
||||
/>
|
||||
<span className="text">Bullet List</span>
|
||||
{blockType === "ul" && <span className="active" />}
|
||||
</button>
|
||||
<button className="item" onClick={formatNumberedList}>
|
||||
<span
|
||||
className={`icon numbered-list${theme === "dark" ? "-dark" : ""}`}
|
||||
/>
|
||||
<span className="text">Numbered List</span>
|
||||
{blockType === "ol" && <span className="active" />}
|
||||
</button>
|
||||
<button className="item" onClick={formatQuote}>
|
||||
<span className={`icon quote${theme === "dark" ? "-dark" : ""}`} />
|
||||
<span className="text">Quote</span>
|
||||
{blockType === "quote" && <span className="active" />}
|
||||
</button>
|
||||
<button className="item" onClick={formatCode}>
|
||||
<span className={`icon code${theme === "dark" ? "-dark" : ""}`} />
|
||||
<span className="text">Code Block</span>
|
||||
{blockType === "code" && <span className="active" />}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className="toolbar" ref={toolbarRef}>
|
||||
<button
|
||||
disabled={!canUndo}
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(UNDO_COMMAND);
|
||||
}}
|
||||
className="toolbar-item spaced"
|
||||
aria-label="Undo"
|
||||
>
|
||||
<i className={`format ${theme === "dark" ? "undo-dark" : "undo"}`} />
|
||||
</button>
|
||||
<button
|
||||
disabled={!canRedo}
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(REDO_COMMAND);
|
||||
}}
|
||||
className="toolbar-item"
|
||||
aria-label="Redo"
|
||||
>
|
||||
<i className={`format ${theme === "dark" ? "redo-dark" : "redo"}`} />
|
||||
</button>
|
||||
<Divider />
|
||||
{supportedBlockTypes.has(blockType) && (
|
||||
<>
|
||||
<button
|
||||
className="toolbar-item block-controls"
|
||||
onClick={() =>
|
||||
setShowBlockOptionsDropDown(!showBlockOptionsDropDown)
|
||||
}
|
||||
aria-label="Formatting Options"
|
||||
>
|
||||
<span
|
||||
className={
|
||||
"icon block-type " +
|
||||
blockType +
|
||||
(theme === "dark" ? "-dark" : "")
|
||||
}
|
||||
/>
|
||||
<span className="text">{blockTypeToBlockName[blockType]}</span>
|
||||
<i className={`chevron-down${theme === "dark" ? "-dark" : ""}`} />
|
||||
</button>
|
||||
{showBlockOptionsDropDown &&
|
||||
createPortal(
|
||||
<BlockOptionsDropdownList
|
||||
editor={editor}
|
||||
blockType={blockType}
|
||||
toolbarRef={toolbarRef}
|
||||
setShowBlockOptionsDropDown={setShowBlockOptionsDropDown}
|
||||
theme={theme}
|
||||
/>,
|
||||
document.body
|
||||
)}
|
||||
<Divider />
|
||||
</>
|
||||
)}
|
||||
{blockType === "code" ? (
|
||||
<>
|
||||
<Select
|
||||
className="toolbar-item code-language"
|
||||
onChange={onCodeLanguageSelect}
|
||||
options={codeLanguges}
|
||||
value={codeLanguage}
|
||||
/>
|
||||
<i
|
||||
className={`chevron-down${theme === "dark" ? "-dark" : ""} inside`}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold");
|
||||
}}
|
||||
className={"toolbar-item spaced " + (isBold ? "active" : "")}
|
||||
aria-label="Format Bold"
|
||||
>
|
||||
<i className={`format bold${theme === "dark" ? "-dark" : ""}`} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic");
|
||||
}}
|
||||
className={"toolbar-item spaced " + (isItalic ? "active" : "")}
|
||||
aria-label="Format Italics"
|
||||
>
|
||||
<i className={`format italic${theme === "dark" ? "-dark" : ""}`} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, "underline");
|
||||
}}
|
||||
className={"toolbar-item spaced " + (isUnderline ? "active" : "")}
|
||||
aria-label="Format Underline"
|
||||
>
|
||||
<i
|
||||
className={`format underline${theme === "dark" ? "-dark" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, "strikethrough");
|
||||
}}
|
||||
className={
|
||||
"toolbar-item spaced " + (isStrikethrough ? "active" : "")
|
||||
}
|
||||
aria-label="Format Strikethrough"
|
||||
>
|
||||
<i
|
||||
className={`format strikethrough${
|
||||
theme === "dark" ? "-dark" : ""
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, "code");
|
||||
}}
|
||||
className={"toolbar-item spaced " + (isCode ? "active" : "")}
|
||||
aria-label="Insert Code"
|
||||
>
|
||||
<i className={`format code${theme === "dark" ? "-dark" : ""}`} />
|
||||
</button>
|
||||
<button
|
||||
onClick={insertLink}
|
||||
className={"toolbar-item spaced " + (isLink ? "active" : "")}
|
||||
aria-label="Insert Link"
|
||||
>
|
||||
<i className={`format link${theme === "dark" ? "-dark" : ""}`} />
|
||||
</button>
|
||||
{isLink &&
|
||||
createPortal(
|
||||
<FloatingLinkEditor editor={editor} theme={theme} />,
|
||||
document.body
|
||||
)}
|
||||
<Divider />
|
||||
<button
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, "left");
|
||||
}}
|
||||
className="toolbar-item spaced"
|
||||
aria-label="Left Align"
|
||||
>
|
||||
<i
|
||||
className={`format ${
|
||||
theme === "dark" ? "left-align-dark" : "left-align"
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, "center");
|
||||
}}
|
||||
className="toolbar-item spaced"
|
||||
aria-label="Center Align"
|
||||
>
|
||||
<i
|
||||
className={`format ${
|
||||
theme === "dark" ? "center-align-dark" : "center-align"
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, "right");
|
||||
}}
|
||||
className="toolbar-item spaced"
|
||||
aria-label="Right Align"
|
||||
>
|
||||
<i
|
||||
className={`format ${
|
||||
theme === "dark" ? "right-align-dark" : "right-align"
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, "justify");
|
||||
}}
|
||||
className="toolbar-item"
|
||||
aria-label="Justify Align"
|
||||
>
|
||||
<i
|
||||
className={`format ${
|
||||
theme === "dark" ? "justify-align-dark" : "justify-align"
|
||||
}`}
|
||||
/>
|
||||
</button>{" "}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|