diff --git a/src/pages/Note/Hlexical/index.jsx b/src/pages/Note/Hlexical/index.jsx
index 56da82e..101021a 100644
--- a/src/pages/Note/Hlexical/index.jsx
+++ b/src/pages/Note/Hlexical/index.jsx
@@ -31,6 +31,8 @@ import InlineImagePlugin from "./plugins/InlineImagePlugin";
import {TablePlugin} from "@lexical/react/LexicalTablePlugin";
import TableCellActionMenuPlugin from './plugins/TableActionMenuPlugin';
import ExcalidrawPlugin from "./plugins/ExcalidrawPlugin";
+import TableOfContentsPlugin from "./plugins/TableOfContentsPlugin";
+import ContextMenuPlugin from "./plugins/ContextMenuPlugin"
function Placeholder() {
return
Enter some rich text...
;
}
@@ -93,7 +95,9 @@ export default function Hlexical(props) {
{/*页分割线*/}
{/*目录加载*/}
-
+
+ {/*右键菜单*/}
+
{/* 画图 */}
diff --git a/src/pages/Note/Hlexical/index.less b/src/pages/Note/Hlexical/index.less
index a4bd306..06c9dc1 100644
--- a/src/pages/Note/Hlexical/index.less
+++ b/src/pages/Note/Hlexical/index.less
@@ -1,754 +1,777 @@
body {
- margin: 0;
- background: #eee;
- font-family: system-ui, -apple-system, BlinkMacSystemFont, ".SFNSText-Regular",
- sans-serif;
- font-weight: 500;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- }
-
- .other h2 {
- font-size: 18px;
- color: #444;
- margin-bottom: 7px;
- }
-
- .other a {
- color: #777;
- text-decoration: underline;
- font-size: 14px;
- }
-
- .other ul {
- padding: 0;
- margin: 0;
- list-style-type: none;
- }
+ margin: 0;
+ background: #eee;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, ".SFNSText-Regular",
+ sans-serif;
+ font-weight: 500;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
- h1 {
- font-size: 24px;
- color: #333;
- }
-
- .ltr {
- text-align: left;
- }
-
- .rtl {
- text-align: right;
- }
-
- .editor-container {
- // margin: 20px auto 20px auto;
- border-radius: 2px;
- // max-width: 600px;
- color: #000;
- position: relative;
- line-height: 20px;
- font-weight: 400;
- text-align: left;
- border-top-left-radius: 10px;
- border-top-right-radius: 10px;
- height: 100%;
- }
- .toolbar{
- height:4.9%;
- margin-bottom:0.1%
- }
- .editor-inner {
- background: #fff;
- position: relative;
- height:95%;
- overflow: hidden auto;
- }
-
- .editor-input {
- min-height: 150px;
- resize: none;
- font-size: 15px;
- caret-color: rgb(5, 5, 5);
- position: relative;
- tab-size: 1;
- outline: 0;
- padding: 15px 10px;
- caret-color: #444;
- }
-
- .editor-placeholder {
- color: #999;
- overflow: hidden;
- 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: rgb(240, 242, 245);
- padding: 1px 0.25rem;
- font-family: Menlo, Consolas, Monaco, monospace;
- font-size: 94%;
- }
-
- .editor-link {
- color: rgb(33, 111, 219);
- text-decoration: none;
- }
-
- .tree-view-output {
- display: block;
- background: #222;
- color: #fff;
- padding: 5px;
- font-size: 12px;
- white-space: pre-wrap;
- margin: 1px auto 10px auto;
- max-height: 250px;
- position: relative;
- border-bottom-left-radius: 10px;
- border-bottom-right-radius: 10px;
- overflow: auto;
- line-height: 14px;
- }
-
- .editor-code {
- background-color: rgb(240, 242, 245);
- 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;
- white-space: pre;
- overflow: auto;
- position: relative;
- }
-
- .editor-code:before {
- content: attr(data-gutter);
- position: absolute;
- background-color: #eee;
- left: 0;
- top: 0;
- border-right: 1px solid #ccc;
- 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(0, 0, 0, 0.5);
- }
-
- .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;
- color: rgb(5, 5, 5);
- font-weight: 400;
- margin: 0;
- margin-bottom: 12px;
- padding: 0;
- }
-
- .editor-heading-h2 {
- font-size: 15px;
- color: rgb(101, 103, 107);
- font-weight: 700;
- margin: 0;
- margin-top: 10px;
- padding: 0;
- text-transform: uppercase;
- }
-
- .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;
- }
-
- .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;
- }
-
- .debug-timetravel-panel {
- overflow: hidden;
- padding: 0 0 10px 0;
- margin: auto;
- display: flex;
- }
-
- .debug-timetravel-panel-slider {
- padding: 0;
- flex: 8;
- }
-
- .debug-timetravel-panel-button {
- padding: 0;
- border: 0;
- background: none;
- flex: 1;
- color: #fff;
- font-size: 12px;
- }
-
- .debug-timetravel-panel-button:hover {
- text-decoration: underline;
- }
-
- .debug-timetravel-button {
- border: 0;
- padding: 0;
- font-size: 12px;
- top: 10px;
- right: 15px;
- position: absolute;
- background: none;
- color: #fff;
- }
-
- .debug-timetravel-button:hover {
- text-decoration: underline;
- }
-
- .emoji {
- color: transparent;
- background-size: 16px 16px;
- background-position: center;
- background-repeat: no-repeat;
- vertical-align: middle;
- margin: 0 -1px;
- }
-
- .emoji-inner {
- padding: 0 0.15em;
- }
-
- .emoji-inner::selection {
- color: transparent;
- background-color: rgba(150, 150, 150, 0.4);
- }
-
- .emoji-inner::moz-selection {
- color: transparent;
- background-color: rgba(150, 150, 150, 0.4);
- }
-
- .emoji.happysmile {
- background-image: url(./images/emoji/1F642.png);
- }
-
- .toolbar {
- display: flex;
- margin-bottom: 1px;
- background: #fff;
- padding: 4px;
- border-top-left-radius: 10px;
- border-top-right-radius: 10px;
- vertical-align: middle;
- }
-
- .toolbar button.toolbar-item {
- border: 0;
- display: flex;
- background: none;
- border-radius: 10px;
- padding: 8px;
- cursor: pointer;
- vertical-align: middle;
- }
-
- .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: 0.6;
- }
-
- .toolbar button.toolbar-item:disabled i.format {
- opacity: 0.2;
- }
-
- .toolbar button.toolbar-item.active {
- background-color: rgba(223, 232, 250, 0.3);
- }
-
- .toolbar button.toolbar-item.active i {
- opacity: 1;
- }
-
- .toolbar .toolbar-item:hover:not([disabled]) {
- background-color: #eee;
- }
-
- .toolbar .divider {
- width: 1px;
- background-color: #eee;
- margin: 0 4px;
- }
-
- .toolbar select.toolbar-item {
- border: 0;
- display: flex;
- background: none;
- border-radius: 10px;
- padding: 8px;
- vertical-align: middle;
- -webkit-appearance: none;
- -moz-appearance: none;
- width: 70px;
- font-size: 14px;
- color: #777;
- text-overflow: ellipsis;
- }
-
- .toolbar select.code-language {
- text-transform: capitalize;
- width: 130px;
- }
-
- .toolbar .toolbar-item .text {
- display: flex;
- line-height: 20px;
- vertical-align: middle;
- font-size: 14px;
- color: #777;
- text-overflow: ellipsis;
- width: 70px;
- overflow: hidden;
- height: 20px;
- text-align: left;
- }
-
- .toolbar .toolbar-item .icon {
- display: flex;
- width: 20px;
- height: 20px;
- user-select: none;
- margin-right: 8px;
- line-height: 16px;
- background-size: contain;
- }
-
- .toolbar i.chevron-down {
- margin-top: 3px;
- width: 16px;
- height: 16px;
- display: flex;
- user-select: none;
- }
-
- .toolbar i.chevron-down.inside {
- width: 16px;
- height: 16px;
- display: flex;
- margin-left: -25px;
- margin-top: 11px;
- margin-right: 10px;
- pointer-events: none;
- }
-
- i.chevron-down {
- background-color: transparent;
- background-size: contain;
- display: inline-block;
- height: 8px;
- width: 8px;
- background-image: url(images/icons/chevron-down.svg);
- }
-
- #block-controls button:hover {
- background-color: #efefef;
- }
-
- #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(images/icons/text-paragraph.svg);
- }
-
- #block-controls span.block-type.h1 {
- background-image: url(images/icons/type-h1.svg);
- }
-
- #block-controls span.block-type.h2 {
- background-image: url(images/icons/type-h2.svg);
- }
-
- #block-controls span.block-type.quote {
- background-image: url(images/icons/chat-square-quote.svg);
- }
-
- #block-controls span.block-type.ul {
- background-image: url(images/icons/list-ul.svg);
- }
-
- #block-controls span.block-type.ol {
- background-image: url(images/icons/list-ol.svg);
- }
-
- #block-controls span.block-type.code {
- background-image: url(images/icons/code.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(255, 255, 255, 0.5);
- border-radius: 8px;
- min-width: 100px;
- min-height: 40px;
- background-color: #fff;
- }
-
- .dropdown .item {
- margin: 0 8px 0 8px;
- padding: 8px;
- color: #050505;
- cursor: pointer;
- line-height: 16px;
- font-size: 15px;
- display: flex;
- align-content: center;
- flex-direction: row;
- flex-shrink: 0;
- justify-content: space-between;
- background-color: #fff;
- border-radius: 8px;
- border: 0;
- min-width: 268px;
- }
-
- .dropdown .item .active {
- display: flex;
- width: 20px;
- height: 20px;
- background-size: contain;
- }
-
- .dropdown .item:first-child {
- margin-top: 8px;
- }
-
- .dropdown .item:last-child {
- margin-bottom: 8px;
- }
-
- .dropdown .item:hover {
- background-color: #eee;
- }
-
- .dropdown .item .text {
- display: flex;
- line-height: 20px;
- flex-grow: 1;
- width: 200px;
- }
-
- .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: #fff;
- box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.3);
- border-radius: 8px;
- 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: 15px;
- background-color: #eee;
- font-size: 15px;
- color: rgb(5, 5, 5);
- border: 0;
- outline: 0;
- position: relative;
- font-family: inherit;
- }
-
- .link-editor div.link-edit {
- background-image: url(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-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(images/icons/arrow-counterclockwise.svg);
- }
-
- i.redo {
- background-image: url(images/icons/arrow-clockwise.svg);
- }
-
- .icon.paragraph {
- background-image: url(images/icons/text-paragraph.svg);
- }
-
- .icon.large-heading,
- .icon.h1 {
- background-image: url(images/icons/type-h1.svg);
- }
-
- .icon.small-heading,
- .icon.h2 {
- background-image: url(images/icons/type-h2.svg);
- }
-
- .icon.bullet-list,
- .icon.ul {
- background-image: url(images/icons/list-ul.svg);
- }
-
- .icon.numbered-list,
- .icon.ol {
- background-image: url(images/icons/list-ol.svg);
- }
-
- .icon.quote {
- background-image: url(images/icons/chat-square-quote.svg);
- }
-
- .icon.code {
- background-image: url(images/icons/code.svg);
- }
-
- i.bold {
- background-image: url(images/icons/type-bold.svg);
- }
-
- i.italic {
- background-image: url(images/icons/type-italic.svg);
- }
-
- i.underline {
- background-image: url(images/icons/type-underline.svg);
- }
-
- i.strikethrough {
- background-image: url(images/icons/type-strikethrough.svg);
- }
-
- i.code {
- background-image: url(images/icons/code.svg);
- }
-
- i.link {
- background-image: url(images/icons/link.svg);
- }
-
- i.left-align {
- background-image: url(images/icons/text-left.svg);
- }
-
- i.center-align {
- background-image: url(images/icons/text-center.svg);
- }
-
- i.right-align {
- background-image: url(images/icons/text-right.svg);
- }
-
- i.justify-align {
- background-image: url(images/icons/justify.svg);
- }
+.other h2 {
+ font-size: 18px;
+ color: #444;
+ margin-bottom: 7px;
+}
+
+.other a {
+ color: #777;
+ text-decoration: underline;
+ font-size: 14px;
+}
+
+.other ul {
+ padding: 0;
+ margin: 0;
+ list-style-type: none;
+}
+
+h1 {
+ font-size: 24px;
+ color: #333;
+}
+
+.ltr {
+ text-align: left;
+}
+
+.rtl {
+ text-align: right;
+}
+
+.editor-container {
+ // margin: 20px auto 20px auto;
+ border-radius: 2px;
+ // max-width: 600px;
+ color: #000;
+ position: relative;
+ line-height: 20px;
+ font-weight: 400;
+ text-align: left;
+ border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+ height: 100%;
+}
+
+.toolbar {
+ height: 4.9%;
+ margin-bottom: 0.1%
+}
+
+.editor-inner {
+ background: #fff;
+ position: relative;
+ height: 95%;
+ overflow: hidden auto;
+}
+
+.editor-input {
+ min-height: 150px;
+ resize: none;
+ font-size: 15px;
+ caret-color: rgb(5, 5, 5);
+ position: relative;
+ tab-size: 1;
+ outline: 0;
+ padding: 15px 10px;
+ caret-color: #444;
+}
+
+.editor-placeholder {
+ color: #999;
+ overflow: hidden;
+ 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: rgb(240, 242, 245);
+ padding: 1px 0.25rem;
+ font-family: Menlo, Consolas, Monaco, monospace;
+ font-size: 94%;
+}
+
+.editor-link {
+ color: rgb(33, 111, 219);
+ text-decoration: none;
+}
+
+.tree-view-output {
+ display: block;
+ background: #222;
+ color: #fff;
+ padding: 5px;
+ font-size: 12px;
+ white-space: pre-wrap;
+ margin: 1px auto 10px auto;
+ max-height: 250px;
+ position: relative;
+ border-bottom-left-radius: 10px;
+ border-bottom-right-radius: 10px;
+ overflow: auto;
+ line-height: 14px;
+}
+
+.editor-code {
+ background-color: rgb(240, 242, 245);
+ 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;
+ white-space: pre;
+ overflow: auto;
+ position: relative;
+}
+
+.editor-code:before {
+ content: attr(data-gutter);
+ position: absolute;
+ background-color: #eee;
+ left: 0;
+ top: 0;
+ border-right: 1px solid #ccc;
+ 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(0, 0, 0, 0.5);
+}
+
+.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;
+ color: rgb(5, 5, 5);
+ font-weight: 400;
+ margin: 0;
+ margin-bottom: 12px;
+ padding: 0;
+}
+
+.editor-heading-h2 {
+ font-size: 15px;
+ color: rgb(101, 103, 107);
+ font-weight: 700;
+ margin: 0;
+ margin-top: 10px;
+ padding: 0;
+ text-transform: uppercase;
+}
+
+.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;
+}
+
+.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;
+}
+
+.debug-timetravel-panel {
+ overflow: hidden;
+ padding: 0 0 10px 0;
+ margin: auto;
+ display: flex;
+}
+
+.debug-timetravel-panel-slider {
+ padding: 0;
+ flex: 8;
+}
+
+.debug-timetravel-panel-button {
+ padding: 0;
+ border: 0;
+ background: none;
+ flex: 1;
+ color: #fff;
+ font-size: 12px;
+}
+
+.debug-timetravel-panel-button:hover {
+ text-decoration: underline;
+}
+
+.debug-timetravel-button {
+ border: 0;
+ padding: 0;
+ font-size: 12px;
+ top: 10px;
+ right: 15px;
+ position: absolute;
+ background: none;
+ color: #fff;
+}
+
+.debug-timetravel-button:hover {
+ text-decoration: underline;
+}
+
+.emoji {
+ color: transparent;
+ background-size: 16px 16px;
+ background-position: center;
+ background-repeat: no-repeat;
+ vertical-align: middle;
+ margin: 0 -1px;
+}
+
+.emoji-inner {
+ padding: 0 0.15em;
+}
+
+.emoji-inner::selection {
+ color: transparent;
+ background-color: rgba(150, 150, 150, 0.4);
+}
+
+.emoji-inner::moz-selection {
+ color: transparent;
+ background-color: rgba(150, 150, 150, 0.4);
+}
+
+.emoji.happysmile {
+ background-image: url(./images/emoji/1F642.png);
+}
+
+.toolbar {
+ display: flex;
+ margin-bottom: 1px;
+ background: #fff;
+ padding: 4px;
+ border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+ vertical-align: middle;
+}
+
+.toolbar button.toolbar-item {
+ border: 0;
+ display: flex;
+ background: none;
+ border-radius: 10px;
+ padding: 8px;
+ cursor: pointer;
+ vertical-align: middle;
+}
+
+.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: 0.6;
+}
+
+.toolbar button.toolbar-item:disabled i.format {
+ opacity: 0.2;
+}
+
+.toolbar button.toolbar-item.active {
+ background-color: rgba(223, 232, 250, 0.3);
+}
+
+.toolbar button.toolbar-item.active i {
+ opacity: 1;
+}
+
+.toolbar .toolbar-item:hover:not([disabled]) {
+ background-color: #eee;
+}
+
+.toolbar .divider {
+ width: 1px;
+ background-color: #eee;
+ margin: 0 4px;
+}
+
+.toolbar select.toolbar-item {
+ border: 0;
+ display: flex;
+ background: none;
+ border-radius: 10px;
+ padding: 8px;
+ vertical-align: middle;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ width: 70px;
+ font-size: 14px;
+ color: #777;
+ text-overflow: ellipsis;
+}
+
+.toolbar select.code-language {
+ text-transform: capitalize;
+ width: 130px;
+}
+
+.toolbar .toolbar-item .text {
+ display: flex;
+ line-height: 20px;
+ vertical-align: middle;
+ font-size: 14px;
+ color: #777;
+ text-overflow: ellipsis;
+ width: 70px;
+ overflow: hidden;
+ height: 20px;
+ text-align: left;
+}
+
+.toolbar .toolbar-item .icon {
+ display: flex;
+ width: 20px;
+ height: 20px;
+ user-select: none;
+ margin-right: 8px;
+ line-height: 16px;
+ background-size: contain;
+}
+
+.toolbar i.chevron-down {
+ margin-top: 3px;
+ width: 16px;
+ height: 16px;
+ display: flex;
+ user-select: none;
+}
+
+.toolbar i.chevron-down.inside {
+ width: 16px;
+ height: 16px;
+ display: flex;
+ margin-left: -25px;
+ margin-top: 11px;
+ margin-right: 10px;
+ pointer-events: none;
+}
+
+i.chevron-down {
+ background-color: transparent;
+ background-size: contain;
+ display: inline-block;
+ height: 8px;
+ width: 8px;
+ background-image: url(images/icons/chevron-down.svg);
+}
+
+#block-controls button:hover {
+ background-color: #efefef;
+}
+
+#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(images/icons/text-paragraph.svg);
+}
+
+#block-controls span.block-type.h1 {
+ background-image: url(images/icons/type-h1.svg);
+}
+
+#block-controls span.block-type.h2 {
+ background-image: url(images/icons/type-h2.svg);
+}
+
+#block-controls span.block-type.quote {
+ background-image: url(images/icons/chat-square-quote.svg);
+}
+
+#block-controls span.block-type.ul {
+ background-image: url(images/icons/list-ul.svg);
+}
+
+#block-controls span.block-type.ol {
+ background-image: url(images/icons/list-ol.svg);
+}
+
+#block-controls span.block-type.code {
+ background-image: url(images/icons/code.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(255, 255, 255, 0.5);
+ border-radius: 8px;
+ min-width: 100px;
+ min-height: 40px;
+ background-color: #fff;
+}
+
+.dropdown .item {
+ margin: 0 8px 0 8px;
+ padding: 8px;
+ color: #050505;
+ cursor: pointer;
+ line-height: 16px;
+ font-size: 15px;
+ display: flex;
+ align-content: center;
+ flex-direction: row;
+ flex-shrink: 0;
+ justify-content: space-between;
+ background-color: #fff;
+ border-radius: 8px;
+ border: 0;
+ min-width: 268px;
+}
+
+.dropdown .item .active {
+ display: flex;
+ width: 20px;
+ height: 20px;
+ background-size: contain;
+}
+
+.dropdown .item:first-child {
+ margin-top: 8px;
+}
+
+.dropdown .item:last-child {
+ margin-bottom: 8px;
+}
+
+.dropdown .item:hover {
+ background-color: #eee;
+}
+
+.dropdown .item .text {
+ display: flex;
+ line-height: 20px;
+ flex-grow: 1;
+ width: 200px;
+}
+
+.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: #fff;
+ box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.3);
+ border-radius: 8px;
+ 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: 15px;
+ background-color: #eee;
+ font-size: 15px;
+ color: rgb(5, 5, 5);
+ border: 0;
+ outline: 0;
+ position: relative;
+ font-family: inherit;
+}
+
+.link-editor div.link-edit {
+ background-image: url(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-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(images/icons/arrow-counterclockwise.svg);
+}
+
+i.redo {
+ background-image: url(images/icons/arrow-clockwise.svg);
+}
+
+.icon.paragraph {
+ background-image: url(images/icons/text-paragraph.svg);
+}
+
+.icon.large-heading,
+.icon.h1 {
+ background-image: url(images/icons/type-h1.svg);
+}
+
+.icon.small-heading,
+.icon.h2 {
+ background-image: url(images/icons/type-h2.svg);
+}
+
+.icon.small-heading,
+.icon.h3 {
+ background-image: url(images/icons/type-h3.svg);
+}
+
+.icon.small-heading,
+.icon.h4 {
+ background-image: url(images/icons/type-h4.svg);
+}
+
+.icon.small-heading,
+.icon.h5 {
+ background-image: url(images/icons/type-h5.svg);
+}
+
+.icon.small-heading,
+.icon.h6 {
+ background-image: url(images/icons/type-h6.svg);
+}
+
+.icon.bullet-list,
+.icon.ul {
+ background-image: url(images/icons/list-ul.svg);
+}
+
+.icon.numbered-list,
+.icon.ol {
+ background-image: url(images/icons/list-ol.svg);
+}
+
+.icon.quote {
+ background-image: url(images/icons/chat-square-quote.svg);
+}
+
+.icon.code {
+ background-image: url(images/icons/code.svg);
+}
+
+i.bold {
+ background-image: url(images/icons/type-bold.svg);
+}
+
+i.italic {
+ background-image: url(images/icons/type-italic.svg);
+}
+
+i.underline {
+ background-image: url(images/icons/type-underline.svg);
+}
+
+i.strikethrough {
+ background-image: url(images/icons/type-strikethrough.svg);
+}
+
+i.code {
+ background-image: url(images/icons/code.svg);
+}
+
+i.link {
+ background-image: url(images/icons/link.svg);
+}
+
+i.left-align {
+ background-image: url(images/icons/text-left.svg);
+}
+
+i.center-align {
+ background-image: url(images/icons/text-center.svg);
+}
+
+i.right-align {
+ background-image: url(images/icons/text-right.svg);
+}
+
+i.justify-align {
+ background-image: url(images/icons/justify.svg);
+}
.editor-table {
border-collapse: collapse;
@@ -759,12 +782,15 @@ body {
width: max-content;
margin: 30px 0;
}
+
.editor-tableSelection *::selection {
background-color: transparent;
}
+
.editor-tableSelected {
outline: 2px solid rgb(60, 132, 244);
}
+
.editor-tableCell {
border: 1px solid #bbb;
width: 75px;
@@ -775,6 +801,7 @@ body {
position: relative;
outline: none;
}
+
.editor-tableCellSortedIndicator {
display: block;
opacity: 0.5;
@@ -785,6 +812,7 @@ body {
height: 4px;
background-color: #999;
}
+
.editor-tableCellResizer {
position: absolute;
right: -4px;
@@ -794,13 +822,16 @@ body {
z-index: 10;
top: 0;
}
+
.editor-tableCellHeader {
background-color: #f2f3f5;
text-align: start;
}
+
.editor-tableCellSelected {
background-color: #c9dbf0;
}
+
.editor-tableCellPrimarySelected {
border: 2px solid rgb(60, 132, 244);
display: block;
@@ -811,10 +842,12 @@ body {
top: -1px;
z-index: 2;
}
+
.editor-tableCellEditing {
box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
border-radius: 3px;
}
+
.editor-tableAddColumns {
position: absolute;
top: 0;
@@ -826,6 +859,7 @@ body {
border: 0;
cursor: pointer;
}
+
.editor-tableAddColumns:after {
background-image: url(images/icons/plus.svg);
background-size: contain;
@@ -840,9 +874,11 @@ body {
height: 100%;
opacity: 0.4;
}
+
.editor-tableAddColumns:hover {
background-color: #c9dbf0;
}
+
.editor-tableAddRows {
position: absolute;
bottom: -25px;
@@ -854,6 +890,7 @@ body {
border: 0;
cursor: pointer;
}
+
.editor-tableAddRows:after {
background-image: url(images/icons/plus.svg);
background-size: contain;
@@ -868,9 +905,11 @@ body {
height: 100%;
opacity: 0.4;
}
+
.editor-tableAddRows:hover {
background-color: #c9dbf0;
}
+
@keyframes table-controls {
0% {
opacity: 0;
@@ -879,6 +918,7 @@ body {
opacity: 1;
}
}
+
.editor-tableCellResizeRuler {
display: block;
position: absolute;
@@ -887,6 +927,7 @@ body {
height: 100%;
top: 0;
}
+
.editor-tableCellActionButtonContainer {
display: block;
right: 5px;
@@ -896,6 +937,7 @@ body {
width: 20px;
height: 20px;
}
+
.editor-tableCellActionButton {
background-color: #eee;
display: block;
@@ -906,6 +948,7 @@ body {
color: #222;
cursor: pointer;
}
+
.editor-tableCellActionButton:hover {
background-color: #ddd;
}
diff --git a/src/pages/Note/Hlexical/plugins/ContextMenuPlugin/index.jsx b/src/pages/Note/Hlexical/plugins/ContextMenuPlugin/index.jsx
new file mode 100644
index 0000000..4e8ac02
--- /dev/null
+++ b/src/pages/Note/Hlexical/plugins/ContextMenuPlugin/index.jsx
@@ -0,0 +1,221 @@
+import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
+import {
+ LexicalContextMenuPlugin,
+ MenuOption,
+} from '@lexical/react/LexicalContextMenuPlugin';
+import {useCallback, useMemo} from 'react';
+import * as React from 'react';
+import * as ReactDOM from 'react-dom';
+import {
+ $getSelection,
+ $isRangeSelection,
+ COPY_COMMAND,
+ CUT_COMMAND,
+ PASTE_COMMAND,
+} from 'lexical';
+function ContextMenuItem({
+ index,
+ isSelected,
+ onClick,
+ onMouseEnter,
+ option,
+ }) {
+ let className = 'item';
+ if (isSelected) {
+ className += ' selected';
+ }
+ return (
+
+ {option.title}
+
+ );
+}
+
+function ContextMenu({
+ options,
+ selectedItemIndex,
+ onOptionClick,
+ onOptionMouseEnter,
+ }) {
+ return (
+
+
+ {options.map((option, i) => (
+ onOptionClick(option, i)}
+ onMouseEnter={() => onOptionMouseEnter(i)}
+ key={option.key}
+ option={option}
+ />
+ ))}
+
+
+ );
+}
+
+export class ContextMenuOption extends MenuOption {
+ title;
+ onSelect;
+ constructor(
+ title,
+ options,
+ ) {
+ super(title);
+ this.title = title;
+ this.onSelect = options.onSelect.bind(this);
+ }
+}
+
+export default function ContextMenuPlugin(){
+ const [editor] = useLexicalComposerContext();
+
+ const options = useMemo(() => {
+ return [
+ new ContextMenuOption(`复制`, {
+ onSelect: (_node) => {
+ editor.dispatchCommand(COPY_COMMAND, null);
+ },
+ }),
+ new ContextMenuOption(`剪切`, {
+ onSelect: (_node) => {
+ editor.dispatchCommand(CUT_COMMAND, null);
+ },
+ }),
+ new ContextMenuOption(`粘贴`, {
+ onSelect: (_node) => {
+ navigator.clipboard.read().then(async (...args) => {
+ const data = new DataTransfer();
+
+ const items = await navigator.clipboard.read();
+ const item = items[0];
+
+ const permission = await navigator.permissions.query({
+ // @ts-expect-error These types are incorrect.
+ name: 'clipboard-read',
+ });
+ if (permission.state === 'denied') {
+ alert('Not allowed to paste from clipboard.');
+ return;
+ }
+
+ for (const type of item.types) {
+ const dataString = await (await item.getType(type)).text();
+ data.setData(type, dataString);
+ }
+
+ const event = new ClipboardEvent('paste', {
+ clipboardData: data,
+ });
+
+ editor.dispatchCommand(PASTE_COMMAND, event);
+ });
+ },
+ }),
+ new ContextMenuOption(`作为文本复制`, {
+ onSelect: (_node) => {
+ navigator.clipboard.read().then(async (...args) => {
+ const permission = await navigator.permissions.query({
+ // @ts-expect-error These types are incorrect.
+ name: 'clipboard-read',
+ });
+
+ if (permission.state === 'denied') {
+ alert('Not allowed to paste from clipboard.');
+ return;
+ }
+
+ const data = new DataTransfer();
+ const items = await navigator.clipboard.readText();
+ data.setData('text/plain', items);
+
+ const event = new ClipboardEvent('paste', {
+ clipboardData: data,
+ });
+ editor.dispatchCommand(PASTE_COMMAND, event);
+ });
+ },
+ }),
+ new ContextMenuOption(`删除文本块`, {
+ onSelect: (_node) => {
+ const selection = $getSelection();
+ if ($isRangeSelection(selection)) {
+ const currentNode = selection.anchor.getNode();
+ const ancestorNodeWithRootAsParent = currentNode
+ .getParents()
+ .at(-2);
+
+ ancestorNodeWithRootAsParent?.remove();
+ }
+ },
+ }),
+ ];
+ }, [editor]);
+
+ const onSelectOption = useCallback(
+ (
+ selectedOption,
+ targetNode,
+ closeMenu,
+ ) => {
+ editor.update(() => {
+ selectedOption.onSelect(targetNode);
+ closeMenu();
+ });
+ },
+ [editor],
+ );
+
+ return (
+
+ anchorElementRef.current
+ ? ReactDOM.createPortal(
+
+ {
+ setHighlightedIndex(index);
+ selectOptionAndCleanUp(option);
+ }}
+ onOptionMouseEnter={(index) => {
+ setHighlightedIndex(index);
+ }}
+ />
+
,
+ anchorElementRef.current,
+ )
+ : null
+ }
+ />
+ );
+}
diff --git a/src/pages/Note/Hlexical/plugins/ContextMenuPlugin/index.less b/src/pages/Note/Hlexical/plugins/ContextMenuPlugin/index.less
new file mode 100644
index 0000000..e69de29
diff --git a/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js b/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js
index 06209df..9049d2e 100644
--- a/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js
+++ b/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js
@@ -10,6 +10,7 @@ import {useDispatch, useSelector} from "react-redux";
import md5 from "md5"
import {message} from "antd";
const {ipcRenderer} = window.require('electron')
+import "./ToobarPlugin.less"
const SaveFilePlugin=(props)=> {
let activeKey = useSelector(state => state.tableBarItem.activeKey);
const dispatch = useDispatch();
diff --git a/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.jsx b/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.jsx
new file mode 100644
index 0000000..e2d4de1
--- /dev/null
+++ b/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.jsx
@@ -0,0 +1,206 @@
+import './index.less';
+import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
+import LexicalTableOfContents from '@lexical/react/LexicalTableOfContents';
+import {useEffect, useRef, useState} from 'react';
+import * as React from 'react';
+import {createPortal} from "react-dom";
+import {useSelector} from "react-redux";
+
+const MARGIN_ABOVE_EDITOR = 624;
+const HEADING_WIDTH = 9;
+
+function indent(tagName) {
+ if (tagName === 'h1') {
+ return 'heading1';
+ } else if (tagName === 'h2') {
+ return 'heading2';
+ } else if (tagName === 'h3') {
+ return 'heading3';
+ } else if (tagName === 'h4') {
+ return 'heading4';
+ } else if (tagName === 'h5') {
+ return 'heading5';
+ } else if (tagName === 'h6') {
+ return 'heading6';
+ }
+}
+
+function isHeadingAtTheTopOfThePage(element) {
+ const elementYPosition = element?.getClientRects()[0].y;
+ return (
+ elementYPosition >= MARGIN_ABOVE_EDITOR &&
+ elementYPosition <= MARGIN_ABOVE_EDITOR + HEADING_WIDTH
+ );
+}
+
+function isHeadingAboveViewport(element) {
+ const elementYPosition = element?.getClientRects()[0].y;
+ return elementYPosition < MARGIN_ABOVE_EDITOR;
+}
+
+function isHeadingBelowTheTopOfThePage(element) {
+ const elementYPosition = element?.getClientRects()[0].y;
+ return elementYPosition >= MARGIN_ABOVE_EDITOR + HEADING_WIDTH;
+}
+
+function TableOfContentsList({
+ tableOfContents,
+ filePath
+ }) {
+ const [selectedKey, setSelectedKey] = useState('');
+ const selectedIndex = useRef(0);
+ const [editor] = useLexicalComposerContext();
+ const [show,setShow]=useState(false)
+ const activeFilePath=useSelector(state => state.tableBarItem.activeKey)
+ function scrollToNode(key, currIndex) {
+ editor.getEditorState().read(() => {
+ const domElement = editor.getElementByKey(key);
+ if (domElement !== null) {
+ domElement.scrollIntoView();
+ setSelectedKey(key);
+ selectedIndex.current = currIndex;
+ }
+ });
+ }
+
+ useEffect(() => {
+ function scrollCallback() {
+ if (
+ tableOfContents.length !== 0 &&
+ selectedIndex.current < tableOfContents.length - 1
+ ) {
+ let currentHeading = editor.getElementByKey(
+ tableOfContents[selectedIndex.current][0],
+ );
+ if (currentHeading !== null) {
+ if (isHeadingBelowTheTopOfThePage(currentHeading)) {
+ //On natural scroll, user is scrolling up
+ while (
+ currentHeading !== null &&
+ isHeadingBelowTheTopOfThePage(currentHeading) &&
+ selectedIndex.current > 0
+ ) {
+ const prevHeading = editor.getElementByKey(
+ tableOfContents[selectedIndex.current - 1][0],
+ );
+ if (
+ prevHeading !== null &&
+ (isHeadingAboveViewport(prevHeading) ||
+ isHeadingBelowTheTopOfThePage(prevHeading))
+ ) {
+ selectedIndex.current--;
+ }
+ currentHeading = prevHeading;
+ }
+ const prevHeadingKey = tableOfContents[selectedIndex.current][0];
+ setSelectedKey(prevHeadingKey);
+ } else if (isHeadingAboveViewport(currentHeading)) {
+ //On natural scroll, user is scrolling down
+ while (
+ currentHeading !== null &&
+ isHeadingAboveViewport(currentHeading) &&
+ selectedIndex.current < tableOfContents.length - 1
+ ) {
+ const nextHeading = editor.getElementByKey(
+ tableOfContents[selectedIndex.current + 1][0],
+ );
+ if (
+ nextHeading !== null &&
+ (isHeadingAtTheTopOfThePage(nextHeading) ||
+ isHeadingAboveViewport(nextHeading))
+ ) {
+ selectedIndex.current++;
+ }
+ currentHeading = nextHeading;
+ }
+ const nextHeadingKey = tableOfContents[selectedIndex.current][0];
+ setSelectedKey(nextHeadingKey);
+ }
+ }
+ } else {
+ selectedIndex.current = 0;
+ }
+ }
+
+ let timerId;
+
+ function debounceFunction(func, delay) {
+ clearTimeout(timerId);
+ timerId = setTimeout(func, delay);
+ }
+
+ function onScroll() {
+ debounceFunction(scrollCallback, 10);
+ }
+
+ document.addEventListener('scroll', onScroll);
+ if (document.getElementById("leftTableOfContents")&&filePath===activeFilePath){
+ setShow(true)
+ }else {
+ setShow(false)
+ }
+ return () => document.removeEventListener('scroll', onScroll);
+ }, [tableOfContents, editor,
+ useSelector(state => state.tableBarItem.leftTableOfContents),
+ useSelector(state => state.tableBarItem.activeKey)
+ ]);
+ return <>
+ {show && createPortal(
+
+ {tableOfContents.map(([key, text, tag], index) => {
+ if (index === 0) {
+ return (
+
+
scrollToNode(key, index)}
+ role="button"
+ tabIndex={0}>
+ {('' + text).length > 20
+ ? text.substring(0, 20) + '...'
+ : text}
+
+
+ );
+ } else {
+ return (
+
+
scrollToNode(key, index)}
+ role="button"
+ className={indent(tag)}
+ tabIndex={0}>
+
-
+ {('' + text).length > 27
+ ? text.substring(0, 27) + '...'
+ : text}
+
+
+
+ );
+ }
+ })}
+
+
, document.getElementById("leftTableOfContents"))}
+ >
+
+ ;
+}
+
+export default function TableOfContentsPlugin(prop) {
+ return (
+
+ {(tableOfContents) => {
+ return ;
+ }}
+
+ );
+}
diff --git a/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.css b/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.less
similarity index 87%
rename from src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.css
rename to src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.less
index ecf6672..bce7fbd 100644
--- a/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.css
+++ b/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.less
@@ -6,6 +6,18 @@
margin-left: 20px;
}
+.table-of-contents .heading4 {
+ margin-left: 30px;
+}
+
+.table-of-contents .heading5 {
+ margin-left: 40px;
+}
+
+.table-of-contents .heading6 {
+ margin-left: 50px;
+}
+
.selected-heading {
color: #3578e5;
position: relative;
@@ -34,8 +46,8 @@
.table-of-contents {
color: #65676b;
position: fixed;
- top: 200px;
- right: -35px;
+ //top: 200px;
+ //right: -35px;
padding: 10px;
width: 250px;
display: flex;
diff --git a/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.tsx b/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.tsx
deleted file mode 100644
index 56fbbf7..0000000
--- a/src/pages/Note/Hlexical/plugins/TableOfContentsPlugin/index.tsx
+++ /dev/null
@@ -1,197 +0,0 @@
-/**
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-import type {TableOfContentsEntry} from '@lexical/react/LexicalTableOfContents';
-import type {HeadingTagType} from '@lexical/rich-text';
-import type {NodeKey} from 'lexical';
-
-import './index.css';
-
-import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
-import LexicalTableOfContents from '@lexical/react/LexicalTableOfContents';
-import {useEffect, useRef, useState} from 'react';
-import * as React from 'react';
-
-const MARGIN_ABOVE_EDITOR = 624;
-const HEADING_WIDTH = 9;
-
-function indent(tagName: HeadingTagType) {
- if (tagName === 'h2') {
- return 'heading2';
- } else if (tagName === 'h3') {
- return 'heading3';
- }
-}
-
-function isHeadingAtTheTopOfThePage(element: HTMLElement): boolean {
- const elementYPosition = element?.getClientRects()[0].y;
- return (
- elementYPosition >= MARGIN_ABOVE_EDITOR &&
- elementYPosition <= MARGIN_ABOVE_EDITOR + HEADING_WIDTH
- );
-}
-function isHeadingAboveViewport(element: HTMLElement): boolean {
- const elementYPosition = element?.getClientRects()[0].y;
- return elementYPosition < MARGIN_ABOVE_EDITOR;
-}
-function isHeadingBelowTheTopOfThePage(element: HTMLElement): boolean {
- const elementYPosition = element?.getClientRects()[0].y;
- return elementYPosition >= MARGIN_ABOVE_EDITOR + HEADING_WIDTH;
-}
-
-function TableOfContentsList({
- tableOfContents,
-}: {
- tableOfContents: Array;
-}): JSX.Element {
- const [selectedKey, setSelectedKey] = useState('');
- const selectedIndex = useRef(0);
- const [editor] = useLexicalComposerContext();
-
- function scrollToNode(key: NodeKey, currIndex: number) {
- editor.getEditorState().read(() => {
- const domElement = editor.getElementByKey(key);
- if (domElement !== null) {
- domElement.scrollIntoView();
- setSelectedKey(key);
- selectedIndex.current = currIndex;
- }
- });
- }
-
- useEffect(() => {
- function scrollCallback() {
- if (
- tableOfContents.length !== 0 &&
- selectedIndex.current < tableOfContents.length - 1
- ) {
- let currentHeading = editor.getElementByKey(
- tableOfContents[selectedIndex.current][0],
- );
- if (currentHeading !== null) {
- if (isHeadingBelowTheTopOfThePage(currentHeading)) {
- //On natural scroll, user is scrolling up
- while (
- currentHeading !== null &&
- isHeadingBelowTheTopOfThePage(currentHeading) &&
- selectedIndex.current > 0
- ) {
- const prevHeading = editor.getElementByKey(
- tableOfContents[selectedIndex.current - 1][0],
- );
- if (
- prevHeading !== null &&
- (isHeadingAboveViewport(prevHeading) ||
- isHeadingBelowTheTopOfThePage(prevHeading))
- ) {
- selectedIndex.current--;
- }
- currentHeading = prevHeading;
- }
- const prevHeadingKey = tableOfContents[selectedIndex.current][0];
- setSelectedKey(prevHeadingKey);
- } else if (isHeadingAboveViewport(currentHeading)) {
- //On natural scroll, user is scrolling down
- while (
- currentHeading !== null &&
- isHeadingAboveViewport(currentHeading) &&
- selectedIndex.current < tableOfContents.length - 1
- ) {
- const nextHeading = editor.getElementByKey(
- tableOfContents[selectedIndex.current + 1][0],
- );
- if (
- nextHeading !== null &&
- (isHeadingAtTheTopOfThePage(nextHeading) ||
- isHeadingAboveViewport(nextHeading))
- ) {
- selectedIndex.current++;
- }
- currentHeading = nextHeading;
- }
- const nextHeadingKey = tableOfContents[selectedIndex.current][0];
- setSelectedKey(nextHeadingKey);
- }
- }
- } else {
- selectedIndex.current = 0;
- }
- }
- let timerId: ReturnType;
-
- function debounceFunction(func: () => void, delay: number) {
- clearTimeout(timerId);
- timerId = setTimeout(func, delay);
- }
-
- function onScroll(): void {
- debounceFunction(scrollCallback, 10);
- }
-
- document.addEventListener('scroll', onScroll);
- return () => document.removeEventListener('scroll', onScroll);
- }, [tableOfContents, editor]);
-
- return (
-
-
- {tableOfContents.map(([key, text, tag], index) => {
- if (index === 0) {
- return (
-
-
scrollToNode(key, index)}
- role="button"
- tabIndex={0}>
- {('' + text).length > 20
- ? text.substring(0, 20) + '...'
- : text}
-
-
-
- );
- } else {
- return (
-
-
scrollToNode(key, index)}
- role="button"
- className={indent(tag)}
- tabIndex={0}>
-
-
- {('' + text).length > 27
- ? text.substring(0, 27) + '...'
- : text}
-
-
-
- );
- }
- })}
-
-
- );
-}
-
-export default function TableOfContentsPlugin() {
- return (
-
- {(tableOfContents) => {
- return ;
- }}
-
- );
-}
diff --git a/src/pages/Note/Hlexical/plugins/ToobarPlugin.less b/src/pages/Note/Hlexical/plugins/ToobarPlugin.less
new file mode 100644
index 0000000..794de07
--- /dev/null
+++ b/src/pages/Note/Hlexical/plugins/ToobarPlugin.less
@@ -0,0 +1,16 @@
+i.diagram-2 {
+ background-image: url(../images/icons/diagram-2.svg);
+}
+i.horizontal-rule {
+ background-image: url(../images/icons/horizontal-rule.svg);
+}
+i.image {
+ background-image: url(../images/icons/file-image.svg);
+}
+
+i.table {
+ background-image: url(../images/icons/table.svg);
+}
+.icon.plus {
+ background-image: url(../images/icons/plus.svg);
+}
diff --git a/src/pages/Note/Hlexical/plugins/ToolbarPlugin.js b/src/pages/Note/Hlexical/plugins/ToolbarPlugin.js
index f0f812f..f3673bf 100644
--- a/src/pages/Note/Hlexical/plugins/ToolbarPlugin.js
+++ b/src/pages/Note/Hlexical/plugins/ToolbarPlugin.js
@@ -59,6 +59,7 @@ const supportedBlockTypes = new Set([
"h3",
"h4",
"h5",
+ "h6",
"ul",
"ol"
]);
@@ -70,6 +71,7 @@ const blockTypeToBlockName = {
h3: "三级标题",
h4: "四级标题",
h5: "五级标题",
+ h6: "六级标题",
ol: "有序序列",
paragraph: "普通文本",
quote: "引用",
@@ -720,14 +722,6 @@ export default function ToolbarPlugin() {
分割线
- {/* {*/}
- {/* activeEditor.dispatchCommand(INSERT_PAGE_BREAK, undefined);*/}
- {/* }}*/}
- {/* className="item">*/}
- {/* */}
- {/* 页分割线*/}
- {/**/}
{
showModal('插入图片', (onClose) => (
diff --git a/src/pages/Note/index.jsx b/src/pages/Note/index.jsx
index 263ae61..7ba481d 100644
--- a/src/pages/Note/index.jsx
+++ b/src/pages/Note/index.jsx
@@ -7,7 +7,7 @@ import Hlexical from './Hlexical';
import ItemTree from "../../components/ItemTree";
import './index.less'
import {useSelector, useDispatch} from "react-redux";
-import {addTableBarItem, removeTableBarItem, setActiveKey,updatedSavedFile} from "../../redux/tableBarItem_reducer"
+import {addTableBarItem, removeTableBarItem, setActiveKey,editLeftTableOfContents} from "../../redux/tableBarItem_reducer"
const {Sider} = Layout;
const Note = () => {
@@ -31,13 +31,17 @@ const Note = () => {
}, {
key: '2',
label: '标题',
- children: '开发中,尽情期待。。',
+ children: ,
+ forceRender:true
}
]
const onChange = (newActiveKey) => {
console.log("setActiveKey(newActiveKey)",newActiveKey)
dispatch(setActiveKey({"activeKey":newActiveKey}));
};
+ const onChangeLeftTableOfContents = (activeKey)=>{
+ dispatch(editLeftTableOfContents({"leftTableOfContents":activeKey}))
+ }
const add = () => {
const newActiveKey = `newTab${newTabIndex.current++}`;
dispatch(addTableBarItem(
@@ -90,8 +94,8 @@ const Note = () => {
- state.tableBarItem.leftTableOfContents)} items={itemTreeTab}
+ onChange={onChangeLeftTableOfContents}
style ={{background:"#fff"}}
/>
@@ -109,4 +113,4 @@ const Note = () => {
);
};
-export default Note;
\ No newline at end of file
+export default Note;
diff --git a/src/redux/tableBarItem_reducer.js b/src/redux/tableBarItem_reducer.js
index 35f7121..08c0d1d 100644
--- a/src/redux/tableBarItem_reducer.js
+++ b/src/redux/tableBarItem_reducer.js
@@ -1,5 +1,4 @@
import { createSlice } from '@reduxjs/toolkit'
-import {isEmpty} from "../utils/ObjectUtils";
import {getFileFullNameByPath} from "../utils/PathOperate";
/**
@@ -16,7 +15,8 @@ export const tableBarItemSlice = createSlice({
type:"tableBarItem",
data: [],
activeKey:"",
- expandedKeyList:[]
+ expandedKeyList:[],
+ leftTableOfContents:""
},
reducers: {
addTableBarItem: (state, action) => {
@@ -82,6 +82,9 @@ export const tableBarItemSlice = createSlice({
},
removeExpandedKeys:(state, action)=>{
state.expandedKeyList=state.expandedKeyList.filter(key=>key!==action.payload)
+ },
+ editLeftTableOfContents:(state, action)=>{
+ state.leftTableOfContents=action.payload.leftTableOfContents
}
}
})
@@ -92,6 +95,7 @@ export const { addTableBarItem,
setExpandedKeys,
removeExpandedKeys,
addExpandedKeys,
- updateFileName
+ updateFileName,
+ editLeftTableOfContents
} = tableBarItemSlice.actions
export default tableBarItemSlice.reducer