x-star-editor 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/XStarEditor/index.d.ts +12 -0
- package/dist/XStarEditor/index.js +114 -0
- package/dist/XStarEditor/index.module.css +19 -0
- package/dist/XStarMdEditor/index.d.ts +31 -0
- package/dist/XStarMdEditor/index.js +263 -0
- package/dist/XStarMdEditor/index.module.css +13 -0
- package/dist/XStarMdViewer/index.d.ts +18 -0
- package/dist/XStarMdViewer/index.js +33 -0
- package/dist/XStarMdViewer/index.module.css +10 -0
- package/dist/components/FileInput.d.ts +15 -0
- package/dist/components/FileInput.js +107 -0
- package/dist/components/FileInput.module.css +81 -0
- package/dist/components/Toolbar.d.ts +22 -0
- package/dist/components/Toolbar.js +294 -0
- package/dist/components/Toolbar.module.css +116 -0
- package/dist/components/icons/bold.svg +1 -0
- package/dist/components/icons/code-library.svg +1 -0
- package/dist/components/icons/code.svg +1 -0
- package/dist/components/icons/image.svg +1 -0
- package/dist/components/icons/italic.svg +1 -0
- package/dist/components/icons/link.svg +1 -0
- package/dist/components/icons/strikethrough.svg +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +5 -0
- package/dist/styles/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/dist/styles/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/dist/styles/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Main-Bold.woff +0 -0
- package/dist/styles/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/dist/styles/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Main-Italic.woff +0 -0
- package/dist/styles/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Main-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/dist/styles/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Math-Italic.woff +0 -0
- package/dist/styles/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/dist/styles/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/dist/styles/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/dist/styles/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/dist/styles/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Script-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/styles/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/dist/styles/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/dist/styles/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/styles/katex.css +1120 -0
- package/dist/styles/markdown.css +129 -0
- package/dist/styles/prism.css +182 -0
- package/dist/utils/container.d.ts +45 -0
- package/dist/utils/container.js +366 -0
- package/dist/utils/handler.d.ts +44 -0
- package/dist/utils/handler.js +227 -0
- package/dist/utils/history.d.ts +59 -0
- package/dist/utils/history.js +305 -0
- package/dist/utils/jsx-runtime.d.ts +7 -0
- package/dist/utils/markdown.d.ts +38 -0
- package/dist/utils/markdown.js +292 -0
- package/dist/utils/module-css.d.ts +3 -0
- package/package.json +88 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
.x-star-md-editor-block {
|
|
2
|
+
margin: 0.2em;
|
|
3
|
+
padding: 0.2em;
|
|
4
|
+
border-radius: 0.2em;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.x-star-md-editor-block:hover {
|
|
8
|
+
background-color: #f8f8f8;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.x-star-md-editor-delimiter,
|
|
12
|
+
.x-star-md-editor-break {
|
|
13
|
+
color: #f5771280;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.x-star-md-editor-heading {
|
|
17
|
+
font-weight: bold;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.x-star-md-editor-heading1 {
|
|
21
|
+
font-size: 1.6em;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.x-star-md-editor-heading2 {
|
|
25
|
+
font-size: 1.3em;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.x-star-md-editor-heading3 {
|
|
29
|
+
font-size: 1.2em;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.x-star-md-editor-heading4 {
|
|
33
|
+
font-size: 1.1em;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.x-star-md-editor-heading5 {
|
|
37
|
+
font-size: 1em;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.x-star-md-editor-heading6 {
|
|
41
|
+
font-size: 0.9em;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.x-star-md-editor-thematicBreak,
|
|
45
|
+
.x-star-md-editor-listItem > .x-star-md-editor-delimiter,
|
|
46
|
+
.x-star-md-editor-definition,
|
|
47
|
+
.x-star-md-editor-link,
|
|
48
|
+
.x-star-md-editor-image,
|
|
49
|
+
.x-star-md-editor-linkReference,
|
|
50
|
+
.x-star-md-editor-imageReference,
|
|
51
|
+
.x-star-md-editor-footnoteDefinition,
|
|
52
|
+
.x-star-md-editor-footnoteReference {
|
|
53
|
+
color: #f57712;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.x-star-md-editor-html,
|
|
57
|
+
.x-star-md-editor-code {
|
|
58
|
+
padding: 0 0.2em;
|
|
59
|
+
tab-size: 4;
|
|
60
|
+
font-family: Consolas;
|
|
61
|
+
color: #022679;
|
|
62
|
+
background-color: #02267910;
|
|
63
|
+
border-right: 1px solid #02267920;
|
|
64
|
+
border-left: 1px solid #02267920;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
:is(.x-star-md-editor-html, .x-star-md-editor-code).x-star-md-editor-first {
|
|
68
|
+
border-top: 1px solid #02267920;
|
|
69
|
+
border-top-left-radius: 0.2em;
|
|
70
|
+
border-top-right-radius: 0.2em;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
:is(.x-star-md-editor-html, .x-star-md-editor-code).x-star-md-editor-last {
|
|
74
|
+
border-bottom: 1px solid #02267920;
|
|
75
|
+
border-bottom-right-radius: 0.2em;
|
|
76
|
+
border-bottom-left-radius: 0.2em;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.x-star-md-editor-inlineCode {
|
|
80
|
+
margin: 0 0.2em;
|
|
81
|
+
padding: 0 0.2em;
|
|
82
|
+
tab-size: 4;
|
|
83
|
+
font-family: Consolas;
|
|
84
|
+
color: #022679;
|
|
85
|
+
background-color: #02267910;
|
|
86
|
+
border: 1px solid #02267920;
|
|
87
|
+
border-radius: 0.2em;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.x-star-md-editor-emphasis {
|
|
91
|
+
font-style: italic;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.x-star-md-editor-strong {
|
|
95
|
+
font-weight: bold;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.x-star-md-editor-delete > :not(.x-star-md-editor-delimiter) {
|
|
99
|
+
text-decoration: line-through;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.x-star-md-editor-math {
|
|
103
|
+
padding: 0 0.2em;
|
|
104
|
+
color: #f57712;
|
|
105
|
+
background-color: #f5771210;
|
|
106
|
+
border-right: 1px solid #f5771220;
|
|
107
|
+
border-left: 1px solid #f5771220;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.x-star-md-editor-math.x-star-md-editor-first {
|
|
111
|
+
border-top: 1px solid #f5771220;
|
|
112
|
+
border-top-left-radius: 0.2em;
|
|
113
|
+
border-top-right-radius: 0.2em;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.x-star-md-editor-math.x-star-md-editor-last {
|
|
117
|
+
border-bottom: 1px solid #f5771220;
|
|
118
|
+
border-bottom-right-radius: 0.2em;
|
|
119
|
+
border-bottom-left-radius: 0.2em;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.x-star-md-editor-inlineMath {
|
|
123
|
+
margin: 0 0.2em;
|
|
124
|
+
padding: 0 0.2em;
|
|
125
|
+
color: #f57712;
|
|
126
|
+
background-color: #f5771210;
|
|
127
|
+
border: 1px solid #f5771220;
|
|
128
|
+
border-radius: 0.2em;
|
|
129
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VS theme by Andrew Lock (https://andrewlock.net)
|
|
3
|
+
* Inspired by Visual Studio syntax coloring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
.x-star-md-viewer.x-star-md-viewer code,
|
|
7
|
+
.x-star-md-viewer.x-star-md-viewer pre {
|
|
8
|
+
margin: 0.2em;
|
|
9
|
+
color: #f57712;
|
|
10
|
+
border-radius: 0.2em;
|
|
11
|
+
font-family: 'Consolas', 'Bitstream Vera Sans Mono', 'Courier New', Courier,
|
|
12
|
+
monospace;
|
|
13
|
+
direction: ltr;
|
|
14
|
+
text-align: left;
|
|
15
|
+
white-space: pre;
|
|
16
|
+
word-spacing: normal;
|
|
17
|
+
word-break: normal;
|
|
18
|
+
font-size: 0.9em;
|
|
19
|
+
line-height: 1.2em;
|
|
20
|
+
|
|
21
|
+
-moz-tab-size: 4;
|
|
22
|
+
-o-tab-size: 4;
|
|
23
|
+
tab-size: 4;
|
|
24
|
+
|
|
25
|
+
-webkit-hyphens: none;
|
|
26
|
+
-moz-hyphens: none;
|
|
27
|
+
-ms-hyphens: none;
|
|
28
|
+
hyphens: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.x-star-md-viewer.x-star-md-viewer pre > code {
|
|
32
|
+
margin: 0;
|
|
33
|
+
color: #393a34;
|
|
34
|
+
font-size: 1em;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.x-star-md-viewer.x-star-md-viewer pre::-moz-selection,
|
|
38
|
+
.x-star-md-viewer.x-star-md-viewer pre ::-moz-selection,
|
|
39
|
+
.x-star-md-viewer.x-star-md-viewer code::-moz-selection,
|
|
40
|
+
.x-star-md-viewer.x-star-md-viewer code ::-moz-selection {
|
|
41
|
+
background-color: #c1def1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.x-star-md-viewer.x-star-md-viewer pre::selection,
|
|
45
|
+
.x-star-md-viewer.x-star-md-viewer pre ::selection,
|
|
46
|
+
.x-star-md-viewer.x-star-md-viewer code::selection,
|
|
47
|
+
.x-star-md-viewer.x-star-md-viewer code ::selection {
|
|
48
|
+
background-color: #c1def1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Code blocks */
|
|
52
|
+
.x-star-md-viewer.x-star-md-viewer pre {
|
|
53
|
+
padding: 1em;
|
|
54
|
+
margin: 0.5em 0;
|
|
55
|
+
overflow: auto;
|
|
56
|
+
border: 1px solid #dddddd;
|
|
57
|
+
background-color: white;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Inline code */
|
|
61
|
+
.x-star-md-viewer.x-star-md-viewer :not(pre) > code {
|
|
62
|
+
padding: 0.2em;
|
|
63
|
+
padding-top: 1px;
|
|
64
|
+
padding-bottom: 1px;
|
|
65
|
+
background-color: #f8f8f8;
|
|
66
|
+
border: 1px solid #dddddd;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.x-star-md-viewer .token.comment,
|
|
70
|
+
.x-star-md-viewer .token.prolog,
|
|
71
|
+
.x-star-md-viewer .token.doctype,
|
|
72
|
+
.x-star-md-viewer .token.cdata {
|
|
73
|
+
color: #008000;
|
|
74
|
+
font-style: italic;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.x-star-md-viewer .token.namespace {
|
|
78
|
+
opacity: 0.7;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.x-star-md-viewer .token.string {
|
|
82
|
+
color: #a31515;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.x-star-md-viewer .token.punctuation,
|
|
86
|
+
.x-star-md-viewer .token.operator {
|
|
87
|
+
color: #393a34; /* no highlight */
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.x-star-md-viewer .token.url,
|
|
91
|
+
.x-star-md-viewer .token.symbol,
|
|
92
|
+
.x-star-md-viewer .token.number,
|
|
93
|
+
.x-star-md-viewer .token.boolean,
|
|
94
|
+
.x-star-md-viewer .token.variable,
|
|
95
|
+
.x-star-md-viewer .token.constant,
|
|
96
|
+
.x-star-md-viewer .token.inserted {
|
|
97
|
+
color: #36acaa;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.x-star-md-viewer .token.atrule,
|
|
101
|
+
.x-star-md-viewer .token.keyword,
|
|
102
|
+
.x-star-md-viewer .token.attr-value,
|
|
103
|
+
.x-star-md-viewer .language-autohotkey .token.selector,
|
|
104
|
+
.x-star-md-viewer .language-json .token.boolean,
|
|
105
|
+
.x-star-md-viewer .language-json .token.number,
|
|
106
|
+
.x-star-md-viewer code[class*='language-css'] {
|
|
107
|
+
color: #0000ff;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.x-star-md-viewer .token.function {
|
|
111
|
+
color: #393a34;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.x-star-md-viewer .token.deleted,
|
|
115
|
+
.x-star-md-viewer .language-autohotkey .token.tag {
|
|
116
|
+
color: #9a050f;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.x-star-md-viewer .token.selector,
|
|
120
|
+
.x-star-md-viewer .language-autohotkey .token.keyword {
|
|
121
|
+
color: #00009f;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.x-star-md-viewer .token.important {
|
|
125
|
+
color: #e90;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.x-star-md-viewer .token.important,
|
|
129
|
+
.x-star-md-viewer .token.bold {
|
|
130
|
+
font-weight: bold;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.x-star-md-viewer .token.italic {
|
|
134
|
+
font-style: italic;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.x-star-md-viewer .token.class-name,
|
|
138
|
+
.x-star-md-viewer .language-json .token.property {
|
|
139
|
+
color: #2b91af;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.x-star-md-viewer .token.tag,
|
|
143
|
+
.x-star-md-viewer .token.selector {
|
|
144
|
+
color: #800000;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.x-star-md-viewer .token.attr-name,
|
|
148
|
+
.x-star-md-viewer .token.property,
|
|
149
|
+
.x-star-md-viewer .token.regex,
|
|
150
|
+
.x-star-md-viewer .token.entity {
|
|
151
|
+
color: #ff0000;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.x-star-md-viewer .token.directive.tag .tag {
|
|
155
|
+
background-color: #ffff00;
|
|
156
|
+
color: #393a34;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.x-star-md-viewer .line-number::before {
|
|
160
|
+
width: 2em;
|
|
161
|
+
margin-right: 16px;
|
|
162
|
+
margin-left: -8px;
|
|
163
|
+
display: inline-block;
|
|
164
|
+
text-align: right;
|
|
165
|
+
color: #2b91af;
|
|
166
|
+
content: attr(line);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.x-star-md-viewer .highlight-line {
|
|
170
|
+
display: block;
|
|
171
|
+
background-color: rgba(193, 222, 241, 0.2);
|
|
172
|
+
background-image: -webkit-linear-gradient(
|
|
173
|
+
left,
|
|
174
|
+
rgba(193, 222, 241, 0.2) 70%,
|
|
175
|
+
rgba(221, 222, 241, 0)
|
|
176
|
+
);
|
|
177
|
+
background-image: linear-gradient(
|
|
178
|
+
to right,
|
|
179
|
+
rgba(193, 222, 241, 0.2) 70%,
|
|
180
|
+
rgba(221, 222, 241, 0)
|
|
181
|
+
);
|
|
182
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
/**
|
|
3
|
+
* 容器选区,表示从锚点到焦点的文本
|
|
4
|
+
*/
|
|
5
|
+
export interface ContainerSelection {
|
|
6
|
+
/**
|
|
7
|
+
* 锚点偏移量
|
|
8
|
+
*/
|
|
9
|
+
anchorOffset: number;
|
|
10
|
+
/**
|
|
11
|
+
* 焦点偏移量
|
|
12
|
+
*/
|
|
13
|
+
focusOffset: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 创建容器选区
|
|
17
|
+
*
|
|
18
|
+
* @param anchorOffset 锚点偏移量
|
|
19
|
+
* @param focusOffset 焦点偏移量,默认与锚点偏移量相同
|
|
20
|
+
* @returns 容器选区
|
|
21
|
+
*/
|
|
22
|
+
export declare const createSelection: (anchorOffset: number, focusOffset?: number) => ContainerSelection;
|
|
23
|
+
/**
|
|
24
|
+
* 根据容器选区获取容器范围
|
|
25
|
+
*
|
|
26
|
+
* @param selection 容器选区
|
|
27
|
+
* @returns 容器范围
|
|
28
|
+
*/
|
|
29
|
+
export declare const getRange: ({ anchorOffset, focusOffset, }: ContainerSelection) => {
|
|
30
|
+
startOffset: number;
|
|
31
|
+
endOffset: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* 获取一个可以设置文本和选区的容器
|
|
35
|
+
*
|
|
36
|
+
* @returns 容器
|
|
37
|
+
*/
|
|
38
|
+
export declare const useContainer: () => {
|
|
39
|
+
ref: import("react").RefObject<HTMLDivElement>;
|
|
40
|
+
normalize: () => void;
|
|
41
|
+
getText: () => string;
|
|
42
|
+
setText: (text: string) => void;
|
|
43
|
+
getSelection: () => ContainerSelection;
|
|
44
|
+
setSelection: ({ anchorOffset, focusOffset }: ContainerSelection) => void;
|
|
45
|
+
};
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
2
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
3
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
4
|
+
import { useEffect, useRef } from 'react';
|
|
5
|
+
import { editorRender } from "./markdown";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 容器选区,表示从锚点到焦点的文本
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 创建容器选区
|
|
13
|
+
*
|
|
14
|
+
* @param anchorOffset 锚点偏移量
|
|
15
|
+
* @param focusOffset 焦点偏移量,默认与锚点偏移量相同
|
|
16
|
+
* @returns 容器选区
|
|
17
|
+
*/
|
|
18
|
+
export var createSelection = function createSelection(anchorOffset) {
|
|
19
|
+
var focusOffset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : anchorOffset;
|
|
20
|
+
return {
|
|
21
|
+
anchorOffset: anchorOffset,
|
|
22
|
+
focusOffset: focusOffset
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 根据容器选区获取容器范围
|
|
28
|
+
*
|
|
29
|
+
* @param selection 容器选区
|
|
30
|
+
* @returns 容器范围
|
|
31
|
+
*/
|
|
32
|
+
export var getRange = function getRange(_ref) {
|
|
33
|
+
var anchorOffset = _ref.anchorOffset,
|
|
34
|
+
focusOffset = _ref.focusOffset;
|
|
35
|
+
return {
|
|
36
|
+
startOffset: anchorOffset < focusOffset ? anchorOffset : focusOffset,
|
|
37
|
+
endOffset: anchorOffset < focusOffset ? focusOffset : anchorOffset
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 获取一个可以设置文本和选区的容器
|
|
43
|
+
*
|
|
44
|
+
* @returns 容器
|
|
45
|
+
*/
|
|
46
|
+
export var useContainer = function useContainer() {
|
|
47
|
+
/**
|
|
48
|
+
* 容器挂载的 DOM 节点
|
|
49
|
+
*/
|
|
50
|
+
var ref = useRef(null);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 将 DOM 树规范化
|
|
54
|
+
*
|
|
55
|
+
* 检查每个行节点的末尾是否为零宽空格,如果不是,则删除行中的零宽空格并在末尾添加零宽空格
|
|
56
|
+
*/
|
|
57
|
+
var normalize = function normalize() {
|
|
58
|
+
if (!ref.current) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
var traverse = function traverse(node, depth) {
|
|
62
|
+
if (depth === 2) {
|
|
63
|
+
var _node$lastChild;
|
|
64
|
+
// 检查行节点的末尾
|
|
65
|
+
if (((_node$lastChild = node.lastChild) === null || _node$lastChild === void 0 ? void 0 : _node$lastChild.nodeType) === Node.TEXT_NODE) {
|
|
66
|
+
var _node$lastChild$nodeV;
|
|
67
|
+
if ((_node$lastChild$nodeV = node.lastChild.nodeValue) !== null && _node$lastChild$nodeV !== void 0 && _node$lastChild$nodeV.endsWith("\u200B")) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} else if (depth > 2) {
|
|
72
|
+
// 删除行中的零宽空格
|
|
73
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
74
|
+
var _node$nodeValue;
|
|
75
|
+
if ((_node$nodeValue = node.nodeValue) !== null && _node$nodeValue !== void 0 && _node$nodeValue.includes("\u200B")) {
|
|
76
|
+
node.nodeValue = node.nodeValue.replace(/\u200B/g, '');
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (var i = 0; i < node.childNodes.length; i++) {
|
|
82
|
+
traverse(node.childNodes[i], depth + 1);
|
|
83
|
+
}
|
|
84
|
+
if (depth === 2) {
|
|
85
|
+
// 添加零宽空格
|
|
86
|
+
node.appendChild(document.createTextNode("\u200B"));
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
traverse(ref.current, 0);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 获取容器文本
|
|
94
|
+
*
|
|
95
|
+
* 在 DOM 树上遍历,将文本节点的值连接起来
|
|
96
|
+
*
|
|
97
|
+
* @returns 容器文本
|
|
98
|
+
*/
|
|
99
|
+
var getText = function getText() {
|
|
100
|
+
if (!ref.current) {
|
|
101
|
+
return '';
|
|
102
|
+
}
|
|
103
|
+
var text = '';
|
|
104
|
+
var traverse = function traverse(node) {
|
|
105
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
106
|
+
if (node.nodeValue) {
|
|
107
|
+
text += node.nodeValue;
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
for (var i = 0; i < node.childNodes.length; i++) {
|
|
111
|
+
traverse(node.childNodes[i]);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
traverse(ref.current);
|
|
116
|
+
return text.slice(0, -1).replace(/\u200B/g, '\n');
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 设置容器文本
|
|
121
|
+
*
|
|
122
|
+
* 将文本映射到 DOM 树,用 diff 算法更新 DOM 树
|
|
123
|
+
*
|
|
124
|
+
* @param text 容器文本
|
|
125
|
+
*/
|
|
126
|
+
var setText = function setText(text) {
|
|
127
|
+
if (!ref.current) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 将一棵 DOM 树更新为另一棵 DOM 树
|
|
133
|
+
*
|
|
134
|
+
* @param newNode 新节点
|
|
135
|
+
* @param oldNode 旧节点
|
|
136
|
+
*/
|
|
137
|
+
var diff = function diff(newNode, oldNode) {
|
|
138
|
+
var newChild = newNode.firstChild;
|
|
139
|
+
var oldChild = oldNode.firstChild;
|
|
140
|
+
|
|
141
|
+
// 依次更新子节点
|
|
142
|
+
for (; newChild && oldChild;) {
|
|
143
|
+
var newNext = newChild.nextSibling;
|
|
144
|
+
var oldNext = oldChild.nextSibling;
|
|
145
|
+
if (newChild.nodeName !== oldChild.nodeName) {
|
|
146
|
+
// 节点名称不同,直接替换
|
|
147
|
+
oldChild.replaceWith(newChild);
|
|
148
|
+
} else if (newChild.nodeType === Node.TEXT_NODE) {
|
|
149
|
+
// 节点均为文本类型,更新文本内容
|
|
150
|
+
if (newChild.nodeValue !== oldChild.nodeValue) {
|
|
151
|
+
oldChild.nodeValue = newChild.nodeValue;
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
// 节点名称相同,更新属性和子节点
|
|
155
|
+
if (newChild instanceof Element && oldChild instanceof Element) {
|
|
156
|
+
var _iterator = _createForOfIteratorHelper(oldChild.getAttributeNames()),
|
|
157
|
+
_step;
|
|
158
|
+
try {
|
|
159
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
160
|
+
var oldAttrName = _step.value;
|
|
161
|
+
if (!newChild.hasAttribute(oldAttrName)) {
|
|
162
|
+
// 移除新节点没有的属性
|
|
163
|
+
oldChild.removeAttribute(oldAttrName);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} catch (err) {
|
|
167
|
+
_iterator.e(err);
|
|
168
|
+
} finally {
|
|
169
|
+
_iterator.f();
|
|
170
|
+
}
|
|
171
|
+
var _iterator2 = _createForOfIteratorHelper(newChild.getAttributeNames()),
|
|
172
|
+
_step2;
|
|
173
|
+
try {
|
|
174
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
175
|
+
var newAttrName = _step2.value;
|
|
176
|
+
var newAttrValue = newChild.getAttribute(newAttrName);
|
|
177
|
+
var oldAttrValue = oldChild.getAttribute(newAttrName);
|
|
178
|
+
if (newAttrValue !== null && newAttrValue !== oldAttrValue) {
|
|
179
|
+
oldChild.setAttribute(newAttrName, newAttrValue);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} catch (err) {
|
|
183
|
+
_iterator2.e(err);
|
|
184
|
+
} finally {
|
|
185
|
+
_iterator2.f();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
diff(newChild, oldChild);
|
|
189
|
+
}
|
|
190
|
+
newChild = newNext;
|
|
191
|
+
oldChild = oldNext;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 移除节点
|
|
195
|
+
for (; oldChild;) {
|
|
196
|
+
var _oldNext = oldChild.nextSibling;
|
|
197
|
+
oldChild.remove();
|
|
198
|
+
oldChild = _oldNext;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 添加节点
|
|
202
|
+
for (; newChild;) {
|
|
203
|
+
var _newNext = newChild.nextSibling;
|
|
204
|
+
oldNode.appendChild(newChild);
|
|
205
|
+
newChild = _newNext;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
diff(editorRender(text), ref.current);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 获取容器选区
|
|
213
|
+
*
|
|
214
|
+
* 用 Selection API 获取选中的 DOM 节点和节点内偏移量,在 DOM 树上遍历获取对应的容器内偏移量
|
|
215
|
+
*
|
|
216
|
+
* @returns 容器选区
|
|
217
|
+
*/
|
|
218
|
+
var getSelection = function getSelection() {
|
|
219
|
+
var selection = window.getSelection();
|
|
220
|
+
if (!selection || !selection.anchorNode || !selection.focusNode) {
|
|
221
|
+
return createSelection(-1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 根据节点和节点内偏移量获取容器内偏移量
|
|
226
|
+
*
|
|
227
|
+
* @param targetNode 节点
|
|
228
|
+
* @param targetOffset 节点内偏移量
|
|
229
|
+
* @returns 容器内偏移量
|
|
230
|
+
*/
|
|
231
|
+
var getOffset = function getOffset(targetNode, targetOffset) {
|
|
232
|
+
if (!ref.current) {
|
|
233
|
+
return -1;
|
|
234
|
+
}
|
|
235
|
+
var offset = 0;
|
|
236
|
+
var traverse = function traverse(node) {
|
|
237
|
+
if (node === targetNode) {
|
|
238
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
239
|
+
if (node.nodeValue) {
|
|
240
|
+
offset += targetOffset;
|
|
241
|
+
if (node.nodeValue[targetOffset - 1] === "\u200B") {
|
|
242
|
+
offset--;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
for (var i = 0; i < targetOffset; i++) {
|
|
247
|
+
traverse(node.childNodes[i]);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
253
|
+
if (node.nodeValue) {
|
|
254
|
+
offset += node.nodeValue.length;
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
for (var _i = 0; _i < node.childNodes.length; _i++) {
|
|
258
|
+
if (traverse(node.childNodes[_i])) {
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
return traverse(ref.current) ? offset : -1;
|
|
265
|
+
};
|
|
266
|
+
return createSelection(getOffset(selection.anchorNode, selection.anchorOffset), getOffset(selection.focusNode, selection.focusOffset));
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 设置容器选区
|
|
271
|
+
*
|
|
272
|
+
* 根据容器内偏移量,在 DOM 树上遍历获取对应的 DOM 节点和节点内偏移量,用 Selection API 选中
|
|
273
|
+
*
|
|
274
|
+
* @param selection 容器选区
|
|
275
|
+
*/
|
|
276
|
+
var setSelection = function setSelection(_ref2) {
|
|
277
|
+
var anchorOffset = _ref2.anchorOffset,
|
|
278
|
+
focusOffset = _ref2.focusOffset;
|
|
279
|
+
var selection = window.getSelection();
|
|
280
|
+
if (!selection || anchorOffset === -1 || focusOffset === -1) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 根据容器内偏移量获取节点和节点内偏移量
|
|
286
|
+
*
|
|
287
|
+
* @param targetOffset 容器内偏移量
|
|
288
|
+
* @returns 节点和节点内偏移量
|
|
289
|
+
*/
|
|
290
|
+
var getNodeOffset = function getNodeOffset(targetOffset) {
|
|
291
|
+
if (!ref.current) {
|
|
292
|
+
return {
|
|
293
|
+
node: undefined,
|
|
294
|
+
offset: 0
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
var offset = targetOffset;
|
|
298
|
+
var traverse = function traverse(node) {
|
|
299
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
300
|
+
if (node.nodeValue) {
|
|
301
|
+
if (offset < node.nodeValue.length) {
|
|
302
|
+
return node;
|
|
303
|
+
}
|
|
304
|
+
offset -= node.nodeValue.length;
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
for (var i = 0; i < node.childNodes.length; i++) {
|
|
308
|
+
var result = traverse(node.childNodes[i]);
|
|
309
|
+
if (result) {
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
return {
|
|
316
|
+
node: traverse(ref.current),
|
|
317
|
+
offset: offset
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
var anchor = getNodeOffset(anchorOffset);
|
|
321
|
+
var focus = getNodeOffset(focusOffset);
|
|
322
|
+
if (!anchor.node || !focus.node) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
selection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// 容器选区改变后将光标滚动到视口内
|
|
329
|
+
useEffect(function () {
|
|
330
|
+
var listener = function listener() {
|
|
331
|
+
var selection = window.getSelection();
|
|
332
|
+
if (!selection || !selection.focusNode || !ref.current || !ref.current.contains(selection.focusNode)) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
var range = document.createRange();
|
|
336
|
+
range.setStart(selection.focusNode, selection.focusOffset);
|
|
337
|
+
range.setEnd(selection.focusNode, selection.focusOffset);
|
|
338
|
+
var rangeRect = range.getBoundingClientRect();
|
|
339
|
+
var containerRect = ref.current.getBoundingClientRect();
|
|
340
|
+
var topOffset = rangeRect.top - containerRect.top;
|
|
341
|
+
var bottomOffset = rangeRect.bottom - containerRect.bottom;
|
|
342
|
+
if (topOffset < 10) {
|
|
343
|
+
ref.current.scrollTop += topOffset - 10;
|
|
344
|
+
} else if (bottomOffset > -10) {
|
|
345
|
+
ref.current.scrollTop += bottomOffset + 10;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
document.addEventListener('selectionchange', listener, {
|
|
349
|
+
capture: true,
|
|
350
|
+
passive: true
|
|
351
|
+
});
|
|
352
|
+
return function () {
|
|
353
|
+
return document.removeEventListener('selectionchange', listener, {
|
|
354
|
+
capture: true
|
|
355
|
+
});
|
|
356
|
+
};
|
|
357
|
+
}, []);
|
|
358
|
+
return useRef({
|
|
359
|
+
ref: ref,
|
|
360
|
+
normalize: normalize,
|
|
361
|
+
getText: getText,
|
|
362
|
+
setText: setText,
|
|
363
|
+
getSelection: getSelection,
|
|
364
|
+
setSelection: setSelection
|
|
365
|
+
}).current;
|
|
366
|
+
};
|