x-star-editor 0.2.5 → 0.3.0

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.
@@ -1,5 +1,5 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
2
- import { SKIP, visit } from 'unist-util-visit';
2
+ import { EXIT, SKIP, visit } from 'unist-util-visit';
3
3
 
4
4
  /**
5
5
  * 判断一个 Hast 节点是否属于行内代码或代码块
@@ -130,31 +130,39 @@ var parseMath = function parseMath(text) {
130
130
  */
131
131
  var rehypeMath = function rehypeMath() {
132
132
  return function (root) {
133
- var _child$properties;
134
- var child = root.children[root.children.length - 1];
135
- if ((child === null || child === void 0 ? void 0 : child.type) !== 'element' || typeof ((_child$properties = child.properties) === null || _child$properties === void 0 ? void 0 : _child$properties.rawPositions) !== 'string') {
136
- return;
137
- }
138
- root.children.pop();
133
+ var rawPositions = [];
134
+ var i = 0;
139
135
 
140
136
  // 获取 rehype-raw-positions 记录的 Raw 节点的位置信息
141
- var rawPositions = child.properties.rawPositions.split(' ').map(function (offset) {
142
- return parseInt(offset);
143
- });
144
- var i = 0;
137
+ visit(root, {
138
+ tagName: 'raw-positions'
139
+ }, function (node, index, parent) {
140
+ var _node$properties3;
141
+ if (typeof ((_node$properties3 = node.properties) === null || _node$properties3 === void 0 ? void 0 : _node$properties3.value) === 'string') {
142
+ rawPositions = node.properties.value.split(' ').map(function (offset) {
143
+ return parseInt(offset);
144
+ });
145
+ }
146
+ if (index !== null && parent) {
147
+ parent.children.splice(index);
148
+ }
149
+ return EXIT;
150
+ }, true);
151
+ if (rawPositions.length < 2) {
152
+ return;
153
+ }
145
154
  visit(root, function (node, index, parent) {
146
155
  if (isCodeNode(node) || isMathNode(node)) {
147
156
  return SKIP;
148
157
  }
149
158
  if (node.type === 'text' && index !== null && parent) {
150
- var _node$position, _node$position$start, _node$position2, _node$position2$end;
151
- var startOffset = (_node$position = node.position) === null || _node$position === void 0 ? void 0 : (_node$position$start = _node$position.start) === null || _node$position$start === void 0 ? void 0 : _node$position$start.offset;
152
- var endOffset = (_node$position2 = node.position) === null || _node$position2 === void 0 ? void 0 : (_node$position2$end = _node$position2.end) === null || _node$position2$end === void 0 ? void 0 : _node$position2$end.offset;
153
- if (startOffset === undefined || endOffset === undefined) {
159
+ var _node$position;
160
+ var startOffset = (_node$position = node.position) === null || _node$position === void 0 ? void 0 : _node$position.start.offset;
161
+ if (startOffset === undefined) {
154
162
  return;
155
163
  }
156
164
  for (; i < rawPositions.length - 1 && rawPositions[i + 1] <= startOffset; i += 2);
157
- if (i < rawPositions.length - 1 && rawPositions[i] <= startOffset && rawPositions[i + 1] >= endOffset) {
165
+ if (i < rawPositions.length - 1 && rawPositions[i] <= startOffset) {
158
166
  var _parent$children;
159
167
  var result = parseMath(node.value);
160
168
  (_parent$children = parent.children).splice.apply(_parent$children, [index, 1].concat(_toConsumableArray(result)));
@@ -7,9 +7,9 @@ var rehypeRawPositions = function rehypeRawPositions() {
7
7
  return function (root) {
8
8
  var rawPositions = [];
9
9
  visit(root, 'raw', function (node) {
10
- var _node$position, _node$position$start, _node$position2, _node$position2$end;
11
- var startOffset = (_node$position = node.position) === null || _node$position === void 0 ? void 0 : (_node$position$start = _node$position.start) === null || _node$position$start === void 0 ? void 0 : _node$position$start.offset;
12
- var endOffset = (_node$position2 = node.position) === null || _node$position2 === void 0 ? void 0 : (_node$position2$end = _node$position2.end) === null || _node$position2$end === void 0 ? void 0 : _node$position2$end.offset;
10
+ var _node$position, _node$position2;
11
+ var startOffset = (_node$position = node.position) === null || _node$position === void 0 ? void 0 : _node$position.start.offset;
12
+ var endOffset = (_node$position2 = node.position) === null || _node$position2 === void 0 ? void 0 : _node$position2.end.offset;
13
13
  if (startOffset === undefined || endOffset === undefined) {
14
14
  return;
15
15
  }
@@ -19,9 +19,9 @@ var rehypeRawPositions = function rehypeRawPositions() {
19
19
  // 记录在新的 Hast 节点上,防止被 rehype-raw 删除
20
20
  root.children.push({
21
21
  type: 'element',
22
- tagName: 'div',
22
+ tagName: 'raw-positions',
23
23
  properties: {
24
- rawPositions: rawPositions.join(' ')
24
+ value: rawPositions.join(' ')
25
25
  },
26
26
  children: []
27
27
  });
@@ -3,7 +3,7 @@ import classNames from 'classnames';
3
3
  import React, { useEffect, useImperativeHandle, useMemo, useRef } from 'react';
4
4
  import Toolbar, { getDefaultToolbarItemMap, getDefaultToolbarItems } from "../components/Toolbar";
5
5
  import { LocaleProvider } from "../locales";
6
- import { getRange, useContainer } from "../utils/container";
6
+ import { createSelection, getRange, useContainer } from "../utils/container";
7
7
  import { prefix } from "../utils/global";
8
8
  import { composeHandlers, getDefaultKeyboardEventHandlers } from "../utils/handler";
9
9
  import { useHistory } from "../utils/history";
@@ -26,11 +26,14 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
26
26
  sourceCode = _useHistory.sourceCode,
27
27
  selection = _useHistory.selection,
28
28
  dispatch = _useHistory.dispatch;
29
+ var ignoreNext = useRef(false);
29
30
  var initialized = useRef(false);
30
31
 
31
32
  // 将文本同步到容器
32
33
  useEffect(function () {
33
- container.setText(sourceCode);
34
+ if (!ignoreNext.current) {
35
+ container.setText(sourceCode);
36
+ }
34
37
  if (initialized.current) {
35
38
  onChange === null || onChange === void 0 ? void 0 : onChange(sourceCode);
36
39
  }
@@ -38,16 +41,14 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
38
41
 
39
42
  // 将选区同步到容器
40
43
  useEffect(function () {
41
- if (initialized.current) {
44
+ if (!ignoreNext.current && initialized.current) {
42
45
  container.setSelection(selection);
43
46
  }
44
47
  }, [selection]);
45
48
  useEffect(function () {
49
+ ignoreNext.current = false;
46
50
  initialized.current = true;
47
- return function () {
48
- initialized.current = false;
49
- };
50
- }, []);
51
+ }, [sourceCode, selection]);
51
52
  var historyLatest = useRef(history);
52
53
  historyLatest.current = history;
53
54
  var sourceCodeLatest = useRef(sourceCode);
@@ -97,110 +98,41 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
97
98
  e.clipboardData.setData('text/plain', sourceCode.slice(startOffset, endOffset));
98
99
  if (e.type === 'cut') {
99
100
  dispatch({
100
- type: 'delete',
101
+ type: 'insert',
102
+ payload: {
103
+ text: ''
104
+ },
101
105
  selection: selection
102
106
  });
103
107
  }
104
108
  }
105
109
  break;
106
110
  }
107
- case 'paste':
108
- {
109
- e.preventDefault();
110
- if (e.clipboardData.types.includes('text/plain')) {
111
- dispatch({
112
- type: 'insert',
113
- payload: {
114
- text: e.clipboardData.getData('text/plain')
115
- },
116
- selection: selection
117
- });
118
- } else if (e.clipboardData.types.includes('Files')) {
119
- var _file = e.clipboardData.files[0];
120
- onInsertFile === null || onInsertFile === void 0 ? void 0 : onInsertFile(_file, {
121
- description: _file.name,
122
- image: _file.type.startsWith('image/')
123
- });
124
- }
125
- break;
126
- }
127
111
  }
128
112
  };
129
-
130
- /**
131
- * 开始组合时的选区
132
- */
133
- var compositionStartSelection = useRef();
134
-
135
- /**
136
- * 容器同步控制器
137
- */
138
- var syncController = useMemo(function () {
139
- var timer = 0;
140
-
141
- /**
142
- * 将最新的文本和选区同步到容器
143
- */
144
- var sync = function sync() {
145
- if (compositionStartSelection.current) {
146
- var _selection = getSelection();
147
- container.setText(sourceCodeLatest.current);
148
- container.setSelection(_selection);
149
- compositionStartSelection.current = undefined;
150
- }
151
- timer = 0;
152
- };
153
- return {
154
- /**
155
- * 在下个事件循环中同步
156
- */
157
- start: function start() {
158
- if (!timer) {
159
- timer = window.setTimeout(sync);
160
- }
161
- },
162
- /**
163
- * 立即执行下个事件循环中的同步
164
- */
165
- flush: function flush() {
166
- if (timer) {
167
- window.clearTimeout(timer);
168
- sync();
169
- }
170
- }
171
- };
172
- }, []);
173
113
  var compositionEventHandler = function compositionEventHandler(e) {
174
114
  switch (e.type) {
175
115
  case 'compositionend':
176
116
  {
177
- syncController.start();
178
- break;
179
- }
180
- case 'compositionstart':
181
- {
182
- syncController.flush();
183
- compositionStartSelection.current = getSelection();
117
+ var _selection = getSelection();
118
+ container.setText(sourceCodeLatest.current);
119
+ container.setSelection(_selection);
184
120
  break;
185
121
  }
186
122
  }
187
123
  };
188
124
  var dragEventHandler = function dragEventHandler(e) {
125
+ var selection = getSelection();
126
+ var _getRange2 = getRange(selection),
127
+ startOffset = _getRange2.startOffset,
128
+ endOffset = _getRange2.endOffset;
189
129
  switch (e.type) {
190
- case 'drop':
130
+ case 'dragstart':
191
131
  {
192
- var _selection2 = getSelection();
193
- window.setTimeout(function () {
194
- container.normalize();
195
- dispatch({
196
- type: 'set',
197
- payload: {
198
- sourceCode: container.getText(),
199
- selection: getSelection()
200
- },
201
- selection: _selection2
202
- });
203
- });
132
+ if (startOffset < endOffset) {
133
+ e.dataTransfer.clearData();
134
+ e.dataTransfer.setData('text/plain', sourceCode.slice(startOffset, endOffset));
135
+ }
204
136
  break;
205
137
  }
206
138
  }
@@ -214,26 +146,6 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
214
146
  }
215
147
  }
216
148
  };
217
- var formEventHandler = function formEventHandler(e) {
218
- switch (e.type) {
219
- case 'beforeinput':
220
- {
221
- e.preventDefault();
222
- if ('data' in e && typeof e.data === 'string') {
223
- var _compositionStartSele;
224
- dispatch({
225
- type: 'insert',
226
- payload: {
227
- text: e.data
228
- },
229
- selection: (_compositionStartSele = compositionStartSelection.current) !== null && _compositionStartSele !== void 0 ? _compositionStartSele : getSelection()
230
- });
231
- compositionStartSelection.current = undefined;
232
- }
233
- break;
234
- }
235
- }
236
- };
237
149
  var onInsertFileLatest = useRef(onInsertFile);
238
150
  onInsertFileLatest.current = onInsertFile;
239
151
  var options = useMemo(function () {
@@ -250,9 +162,7 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
250
162
  switch (e.type) {
251
163
  case 'keydown':
252
164
  {
253
- if (compositionStartSelection.current) {
254
- e.preventDefault();
255
- } else {
165
+ if (!e.nativeEvent.isComposing) {
256
166
  composeHandlers(options.keyboardEventHandlers)({
257
167
  history: history,
258
168
  sourceCode: sourceCode,
@@ -265,6 +175,174 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
265
175
  }
266
176
  }
267
177
  };
178
+ useEffect(function () {
179
+ var _containerRef$current;
180
+ var listener = function listener(e) {
181
+ switch (e.type) {
182
+ case 'beforeinput':
183
+ {
184
+ var targetRange = e.getTargetRanges()[0];
185
+ if (containerRef.current && targetRange) {
186
+ var startOffset = container.getOffset(containerRef.current, targetRange.startContainer, targetRange.startOffset);
187
+ var endOffset = container.getOffset(containerRef.current, targetRange.endContainer, targetRange.endOffset);
188
+ var _selection2 = createSelection(startOffset, endOffset);
189
+ switch (e.inputType) {
190
+ case 'insertText':
191
+ {
192
+ e.preventDefault();
193
+ if (e.data !== null) {
194
+ dispatch({
195
+ type: 'insert',
196
+ payload: {
197
+ text: e.data
198
+ },
199
+ selection: _selection2
200
+ });
201
+ }
202
+ break;
203
+ }
204
+ case 'insertReplacementText':
205
+ case 'insertFromDrop':
206
+ case 'insertFromPaste':
207
+ {
208
+ var _e$dataTransfer, _e$dataTransfer2;
209
+ e.preventDefault();
210
+ if ((_e$dataTransfer = e.dataTransfer) !== null && _e$dataTransfer !== void 0 && _e$dataTransfer.types.includes('text/plain')) {
211
+ dispatch({
212
+ type: 'insert',
213
+ payload: {
214
+ text: e.dataTransfer.getData('text/plain')
215
+ },
216
+ selection: _selection2
217
+ });
218
+ } else if ((_e$dataTransfer2 = e.dataTransfer) !== null && _e$dataTransfer2 !== void 0 && _e$dataTransfer2.types.includes('Files')) {
219
+ var _file = e.dataTransfer.files[0];
220
+ onInsertFile === null || onInsertFile === void 0 ? void 0 : onInsertFile(_file, {
221
+ description: _file.name,
222
+ image: _file.type.startsWith('image/')
223
+ });
224
+ }
225
+ break;
226
+ }
227
+ case 'insertLineBreak':
228
+ case 'insertParagraph':
229
+ {
230
+ e.preventDefault();
231
+ dispatch({
232
+ type: 'insert',
233
+ payload: {
234
+ text: '\n'
235
+ },
236
+ selection: _selection2
237
+ });
238
+ break;
239
+ }
240
+ case 'insertCompositionText':
241
+ case 'insertFromComposition':
242
+ {
243
+ if (e.data !== null) {
244
+ ignoreNext.current = true;
245
+ dispatch({
246
+ type: 'insert',
247
+ payload: {
248
+ text: e.data
249
+ },
250
+ selection: _selection2
251
+ });
252
+ }
253
+ break;
254
+ }
255
+ case 'deleteWordBackward':
256
+ case 'deleteWordForward':
257
+ case 'deleteContentBackward':
258
+ case 'deleteContentForward':
259
+ {
260
+ e.preventDefault();
261
+ if (startOffset < endOffset) {
262
+ dispatch({
263
+ type: 'insert',
264
+ payload: {
265
+ text: ''
266
+ },
267
+ selection: _selection2
268
+ });
269
+ } else if (e.inputType.endsWith('Backward')) {
270
+ if (startOffset) {
271
+ dispatch({
272
+ type: 'insert',
273
+ payload: {
274
+ text: ''
275
+ },
276
+ selection: createSelection(startOffset - 1, endOffset)
277
+ });
278
+ }
279
+ } else {
280
+ if (endOffset < sourceCodeLatest.current.length - 1) {
281
+ dispatch({
282
+ type: 'insert',
283
+ payload: {
284
+ text: ''
285
+ },
286
+ selection: createSelection(startOffset, endOffset + 1)
287
+ });
288
+ }
289
+ }
290
+ break;
291
+ }
292
+ case 'deleteByDrag':
293
+ case 'deleteCompositionText':
294
+ {
295
+ ignoreNext.current = true;
296
+ dispatch({
297
+ type: 'insert',
298
+ payload: {
299
+ text: ''
300
+ },
301
+ selection: _selection2
302
+ });
303
+ break;
304
+ }
305
+ case 'formatBold':
306
+ {
307
+ e.preventDefault();
308
+ dispatch({
309
+ type: 'toggle',
310
+ payload: {
311
+ type: 'strong'
312
+ },
313
+ selection: _selection2
314
+ });
315
+ break;
316
+ }
317
+ case 'formatItalic':
318
+ {
319
+ e.preventDefault();
320
+ dispatch({
321
+ type: 'toggle',
322
+ payload: {
323
+ type: 'emphasis'
324
+ },
325
+ selection: _selection2
326
+ });
327
+ break;
328
+ }
329
+ default:
330
+ {
331
+ e.preventDefault();
332
+ break;
333
+ }
334
+ }
335
+ }
336
+ break;
337
+ }
338
+ }
339
+ };
340
+ (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.addEventListener('beforeinput', listener);
341
+ return function () {
342
+ var _containerRef$current2;
343
+ return (_containerRef$current2 = containerRef.current) === null || _containerRef$current2 === void 0 ? void 0 : _containerRef$current2.removeEventListener('beforeinput', listener);
344
+ };
345
+ }, []);
268
346
  return /*#__PURE__*/React.createElement(LocaleProvider, {
269
347
  locale: locale
270
348
  }, /*#__PURE__*/React.createElement(Toolbar, {
@@ -280,15 +358,12 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
280
358
  height: height
281
359
  }),
282
360
  contentEditable: true,
283
- onBeforeInput: formEventHandler,
284
361
  onBlur: focusEventHandler,
285
362
  onCompositionEnd: compositionEventHandler,
286
- onCompositionStart: compositionEventHandler,
287
363
  onCopy: clipboardEventHandler,
288
364
  onCut: clipboardEventHandler,
289
- onDrop: dragEventHandler,
290
- onKeyDown: keyboardEventHandler,
291
- onPaste: clipboardEventHandler
365
+ onDragStart: dragEventHandler,
366
+ onKeyDown: keyboardEventHandler
292
367
  }));
293
368
  });
294
369
  if (process.env.NODE_ENV !== 'production') {
@@ -36,6 +36,10 @@ export interface XStarMdViewerProps {
36
36
  * 查看器的高度
37
37
  */
38
38
  height?: React.CSSProperties['height'];
39
+ /**
40
+ * 内置主题
41
+ */
42
+ theme?: string;
39
43
  /**
40
44
  * 文本
41
45
  */