x-star-editor 0.3.3 → 0.3.5

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,6 @@
1
1
  import React from 'react';
2
2
  interface FileInputProps {
3
+ image?: boolean;
3
4
  onCancel: () => void;
4
5
  onOk: (data: ({
5
6
  type: 'file';
@@ -11,5 +12,5 @@ interface FileInputProps {
11
12
  description: string;
12
13
  }) => void;
13
14
  }
14
- declare const FileInput: ({ onCancel, onOk }: FileInputProps) => React.JSX.Element;
15
+ declare const FileInput: ({ image, onCancel, onOk }: FileInputProps) => React.JSX.Element;
15
16
  export default FileInput;
@@ -6,7 +6,8 @@ import { useLocale } from "../locales";
6
6
  import { prefix } from "../utils/global";
7
7
  var FileInput = function FileInput(_ref) {
8
8
  var _file$name;
9
- var onCancel = _ref.onCancel,
9
+ var image = _ref.image,
10
+ onCancel = _ref.onCancel,
10
11
  onOk = _ref.onOk;
11
12
  var _useState = useState('file'),
12
13
  _useState2 = _slicedToArray(_useState, 2),
@@ -47,6 +48,7 @@ var FileInput = function FileInput(_ref) {
47
48
  }, /*#__PURE__*/React.createElement("input", {
48
49
  className: classNames("".concat(prefix, "hidden")),
49
50
  type: "file",
51
+ accept: image ? 'image/*' : undefined,
50
52
  onChange: function onChange(e) {
51
53
  var _e$target$files;
52
54
  return setFile((_e$target$files = e.target.files) === null || _e$target$files === void 0 ? void 0 : _e$target$files[0]);
@@ -5,10 +5,7 @@ import React, { useEffect, useRef, useState } from 'react';
5
5
  import SvgBlockquote from "../icons/Blockquote";
6
6
  import SvgCode from "../icons/Code";
7
7
  import SvgDelete from "../icons/Delete";
8
- import SvgEditOnly from "../icons/EditOnly";
9
8
  import SvgEmphasis from "../icons/Emphasis";
10
- import SvgEnterFullscreen from "../icons/EnterFullscreen";
11
- import SvgExitFullscreen from "../icons/ExitFullscreen";
12
9
  import SvgHeading from "../icons/Heading";
13
10
  import SvgHelp from "../icons/Help";
14
11
  import SvgImage from "../icons/Image";
@@ -27,7 +24,6 @@ import SvgToHTML from "../icons/ToHtml";
27
24
  import SvgToMarkdown from "../icons/ToMarkdown";
28
25
  import SvgUndo from "../icons/Undo";
29
26
  import SvgUnorderedList from "../icons/UnorderedList";
30
- import SvgViewOnly from "../icons/ViewOnly";
31
27
  import { getFormat } from "../locales";
32
28
  import { prefix } from "../utils/global";
33
29
  import { redoHandler, toggleHandler, undoHandler } from "../utils/handler";
@@ -70,8 +66,7 @@ var Item = function Item(_ref) {
70
66
  }
71
67
  };
72
68
  document.addEventListener('click', listener, {
73
- capture: true,
74
- passive: true
69
+ capture: true
75
70
  });
76
71
  return function () {
77
72
  return document.removeEventListener('click', listener, {
@@ -83,9 +78,10 @@ var Item = function Item(_ref) {
83
78
  return /*#__PURE__*/React.createElement("div", {
84
79
  className: classNames("".concat(prefix, "item-container"))
85
80
  }, /*#__PURE__*/React.createElement("div", {
86
- className: classNames("".concat(prefix, "item"), (_classNames = {}, _defineProperty(_classNames, "".concat(prefix, "disabled"), disabled), _defineProperty(_classNames, "".concat(prefix, "active"), tooltipOpen || popoverMount), _classNames)),
81
+ className: classNames("".concat(prefix, "item"), (_classNames = {}, _defineProperty(_classNames, "".concat(prefix, "disabled"), disabled), _defineProperty(_classNames, "".concat(prefix, "active"), popoverMount), _classNames)),
87
82
  onClick: function onClick() {
88
83
  if (!disabled) {
84
+ setTooltipOpen(false);
89
85
  setPopoverOpen(true);
90
86
  _onClick === null || _onClick === void 0 ? void 0 : _onClick();
91
87
  }
@@ -144,22 +140,42 @@ var headingOptions = [{
144
140
  depth: 6,
145
141
  fontSize: '0.9em'
146
142
  }];
147
- var mermaidOptions = [{
148
- label: 'Flowchart',
149
- value: "flowchart TD\n A[Start] --> B{Is it?}\n B -- Yes --> C[OK]\n C --> D[Rethink]\n D --> B\n B -- No ----> E[End]"
150
- }, {
143
+ var mermaidOptions = [
144
+ // {
145
+ // label: 'Flowchart',
146
+ // value: `flowchart TD
147
+ // A[Start] --> B{Is it?}
148
+ // B -- Yes --> C[OK]
149
+ // C --> D[Rethink]
150
+ // D --> B
151
+ // B -- No ----> E[End]`,
152
+ // },
153
+ {
151
154
  label: 'Sequence Diagram',
152
155
  value: "sequenceDiagram\n Alice->>John: Hello John, how are you?\n John-->>Alice: Great!\n Alice-)John: See you later!"
153
156
  }, {
154
157
  label: 'Class Diagram',
155
158
  value: "classDiagram\n class BankAccount\n BankAccount : +String owner\n BankAccount : +Bigdecimal balance\n BankAccount : +deposit(amount)\n BankAccount : +withdrawal(amount)"
156
- }, {
157
- label: 'State Diagram',
158
- value: "stateDiagram-v2\n [*] --> Still\n Still --> [*]\n\n Still --> Moving\n Moving --> Still\n Moving --> Crash\n Crash --> [*]"
159
- }, {
160
- label: 'Entity Relationship Diagram',
161
- value: "erDiagram\n CUSTOMER ||--o{ ORDER : places\n ORDER ||--|{ LINE-ITEM : contains\n CUSTOMER }|..|{ DELIVERY-ADDRESS : uses"
162
- }, {
159
+ },
160
+ // {
161
+ // label: 'State Diagram',
162
+ // value: `stateDiagram-v2
163
+ // [*] --> Still
164
+ // Still --> [*]
165
+
166
+ // Still --> Moving
167
+ // Moving --> Still
168
+ // Moving --> Crash
169
+ // Crash --> [*]`,
170
+ // },
171
+ // {
172
+ // label: 'Entity Relationship Diagram',
173
+ // value: `erDiagram
174
+ // CUSTOMER ||--o{ ORDER : places
175
+ // ORDER ||--|{ LINE-ITEM : contains
176
+ // CUSTOMER }|..|{ DELIVERY-ADDRESS : uses`,
177
+ // },
178
+ {
163
179
  label: 'User Journey',
164
180
  value: "journey\n title My working day\n section Go to work\n Make tea: 5: Me\n Go upstairs: 3: Me\n Do work: 1: Me, Cat\n section Go home\n Go downstairs: 5: Me\n Sit down: 5: Me"
165
181
  }, {
@@ -171,10 +187,25 @@ var mermaidOptions = [{
171
187
  }, {
172
188
  label: 'Quadrant Chart',
173
189
  value: "quadrantChart\n title Reach and engagement of campaigns\n x-axis Low Reach --> High Reach\n y-axis Low Engagement --> High Engagement\n quadrant-1 We should expand\n quadrant-2 Need to promote\n quadrant-3 Re-evaluate\n quadrant-4 May be improved\n Campaign A: [0.3, 0.6]\n Campaign B: [0.45, 0.23]\n Campaign C: [0.57, 0.69]\n Campaign D: [0.78, 0.34]\n Campaign E: [0.40, 0.34]\n Campaign F: [0.35, 0.78]"
174
- }, {
175
- label: 'Requirement Diagram',
176
- value: "requirementDiagram\n\n requirement test_req {\n id: 1\n text: the test text.\n risk: high\n verifymethod: test\n }\n\n element test_entity {\n type: simulation\n }\n\n test_entity - satisfies -> test_req"
177
- }, {
190
+ },
191
+ // {
192
+ // label: 'Requirement Diagram',
193
+ // value: `requirementDiagram
194
+
195
+ // requirement test_req {
196
+ // id: 1
197
+ // text: the test text.
198
+ // risk: high
199
+ // verifymethod: test
200
+ // }
201
+
202
+ // element test_entity {
203
+ // type: simulation
204
+ // }
205
+
206
+ // test_entity - satisfies -> test_req`,
207
+ // },
208
+ {
178
209
  label: 'Gitgraph (Git) Diagram',
179
210
  value: "gitGraph\n commit\n commit\n branch develop\n checkout develop\n commit\n commit\n checkout main\n merge develop\n commit\n commit"
180
211
  }, {
@@ -184,10 +215,7 @@ var mermaidOptions = [{
184
215
  label: 'Timeline',
185
216
  value: "timeline\n title History of Social Media Platform\n 2002 : LinkedIn\n 2004 : Facebook\n : Google\n 2005 : Youtube\n 2006 : Twitter"
186
217
  }];
187
- export var getDefaultToolbarItemMap = function getDefaultToolbarItemMap() {
188
- var _SvgEnterFullscreen, _t;
189
- var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
190
- var onInsertFile = arguments.length > 1 ? arguments[1] : undefined;
218
+ export var getDefaultToolbarItemMap = function getDefaultToolbarItemMap(locale, onInsertFile) {
191
219
  var t = getFormat(locale, 'toolbarItem');
192
220
  return {
193
221
  blockquote: {
@@ -237,12 +265,6 @@ export var getDefaultToolbarItemMap = function getDefaultToolbarItemMap() {
237
265
  }));
238
266
  }
239
267
  },
240
- editOnly: {
241
- children: /*#__PURE__*/React.createElement(SvgEditOnly, {
242
- className: classNames("".concat(prefix, "icon"))
243
- }),
244
- tooltip: t('editOnly')
245
- },
246
268
  emphasis: {
247
269
  children: /*#__PURE__*/React.createElement(SvgEmphasis, {
248
270
  className: classNames("".concat(prefix, "icon"))
@@ -254,14 +276,6 @@ export var getDefaultToolbarItemMap = function getDefaultToolbarItemMap() {
254
276
  }));
255
277
  }
256
278
  },
257
- fullscreen: {
258
- children: (_SvgEnterFullscreen = /*#__PURE__*/React.createElement(SvgEnterFullscreen, {
259
- className: classNames("".concat(prefix, "icon"))
260
- })) !== null && _SvgEnterFullscreen !== void 0 ? _SvgEnterFullscreen : /*#__PURE__*/React.createElement(SvgExitFullscreen, {
261
- className: classNames("".concat(prefix, "icon"))
262
- }),
263
- tooltip: (_t = t('enterFullscreen')) !== null && _t !== void 0 ? _t : t('exitFullscreen')
264
- },
265
279
  heading: {
266
280
  children: /*#__PURE__*/React.createElement(SvgHeading, {
267
281
  className: classNames("".concat(prefix, "icon"))
@@ -301,6 +315,7 @@ export var getDefaultToolbarItemMap = function getDefaultToolbarItemMap() {
301
315
  tooltip: t('image'),
302
316
  popoverRender: function popoverRender(exec, close) {
303
317
  return /*#__PURE__*/React.createElement(FileInput, {
318
+ image: true,
304
319
  onCancel: close,
305
320
  onOk: function onOk(data) {
306
321
  if (data.type === 'file') {
@@ -517,12 +532,6 @@ export var getDefaultToolbarItemMap = function getDefaultToolbarItemMap() {
517
532
  className: classNames("".concat(prefix, "icon"))
518
533
  }),
519
534
  tooltip: t('unorderedList')
520
- },
521
- viewOnly: {
522
- children: /*#__PURE__*/React.createElement(SvgViewOnly, {
523
- className: classNames("".concat(prefix, "icon"))
524
- }),
525
- tooltip: t('viewOnly')
526
535
  }
527
536
  };
528
537
  };
@@ -530,7 +539,7 @@ export var getDefaultToolbarItems = function getDefaultToolbarItems() {
530
539
  return [['heading', 'strong', 'emphasis', 'delete'], ['thematicBreak', 'blockquote' /** 'table' */], ['link', 'image', 'mermaid'], ['inlineCode', 'code', 'inlineMath', 'math'], ['undo', 'redo'],
531
540
  // ['orderedList', 'unorderedList', 'taskList'],
532
541
  ['toMarkdown', 'toHTML']
533
- // ['help', 'editOnly', 'viewOnly', 'fullscreen'],
542
+ // ['help'],
534
543
  ];
535
544
  };
536
545
 
@@ -85,7 +85,7 @@ export declare const useLocale: <T extends "fileInput" | "toolbarItem">(slice: T
85
85
  };
86
86
  })[T][U];
87
87
  };
88
- export declare const getFormat: <T extends "fileInput" | "toolbarItem">(locale: string, slice: T) => <U extends keyof MessageMap[T]>(key: U) => ({
88
+ export declare const getFormat: <T extends "fileInput" | "toolbarItem">(locale: string | undefined, slice: T) => <U extends keyof MessageMap[T]>(key: U) => ({
89
89
  readonly fileInput: {
90
90
  readonly cancel: "取消";
91
91
  readonly chooseFile: "选择文件";
@@ -30,8 +30,10 @@ export var useLocale = function useLocale(slice) {
30
30
  format: format
31
31
  };
32
32
  };
33
- export var getFormat = function getFormat(locale, slice) {
33
+ export var getFormat = function getFormat() {
34
34
  var _zh_CN$en_US$locale2;
35
+ var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
36
+ var slice = arguments.length > 1 ? arguments[1] : undefined;
35
37
  var message = ((_zh_CN$en_US$locale2 = {
36
38
  zh_CN: zh_CN,
37
39
  en_US: en_US
@@ -75,6 +75,8 @@
75
75
  }
76
76
 
77
77
  .x-star-editor-toolbar {
78
+ position: relative;
79
+ color: rgba(0, 0, 0, 0.88);
78
80
  padding: 0.5em 1em;
79
81
  display: flex;
80
82
  flex-wrap: wrap;
@@ -83,8 +85,14 @@
83
85
  border: 1px solid #dedede;
84
86
  border-top-left-radius: 0.3em;
85
87
  border-top-right-radius: 0.3em;
86
- position: relative;
87
- color: rgba(0, 0, 0, 0.88);
88
+ }
89
+ .x-star-editor-toolbar *,
90
+ .x-star-editor-toolbar ::before,
91
+ .x-star-editor-toolbar ::after {
92
+ box-sizing: border-box;
93
+ }
94
+ .x-star-editor-toolbar ::selection {
95
+ background-color: #c1def1;
88
96
  }
89
97
  .x-star-editor-toolbar .x-star-editor-item-container {
90
98
  position: relative;
@@ -106,12 +114,15 @@
106
114
  background-color: rgba(230, 247, 255, 0.7);
107
115
  cursor: not-allowed;
108
116
  }
109
- .x-star-editor-toolbar .x-star-editor-item-container .x-star-editor-item.x-star-editor-active {
117
+ .x-star-editor-toolbar .x-star-editor-item-container .x-star-editor-item:hover, .x-star-editor-toolbar .x-star-editor-item-container .x-star-editor-item.x-star-editor-active {
110
118
  background-color: #cbedfd;
111
119
  }
112
120
  .x-star-editor-toolbar .x-star-editor-item-container .x-star-editor-item .x-star-editor-icon {
113
121
  font-size: 1.75em;
114
122
  }
123
+ .x-star-editor-toolbar .x-star-editor-item-container .x-star-editor-item .x-star-editor-icon.x-star-editor-active {
124
+ color: #1890ff;
125
+ }
115
126
  .x-star-editor-toolbar .x-star-editor-item-container .x-star-editor-tooltip {
116
127
  position: absolute;
117
128
  top: 3em;
@@ -160,19 +171,20 @@
160
171
  margin: 0.4em;
161
172
  border: 1px solid #dedede;
162
173
  }
163
- .x-star-editor-toolbar *,
164
- .x-star-editor-toolbar ::before,
165
- .x-star-editor-toolbar ::after {
166
- box-sizing: border-box;
167
- }
168
- .x-star-editor-toolbar ::selection {
169
- background-color: #c1def1;
170
- }
171
174
 
172
175
  .x-star-editor-editor {
173
176
  display: flex;
174
177
  flex-wrap: wrap;
175
178
  }
179
+ .x-star-editor-editor.x-star-editor-fullscreen {
180
+ position: fixed;
181
+ inset: 0;
182
+ z-index: 9999;
183
+ }
184
+ .x-star-editor-editor.x-star-editor-fullscreen > .x-star-editor-md-editor,
185
+ .x-star-editor-editor.x-star-editor-fullscreen > .x-star-editor-md-viewer {
186
+ height: calc(100% - 3em) !important;
187
+ }
176
188
  .x-star-editor-editor > .x-star-editor-toolbar {
177
189
  width: 100%;
178
190
  }
@@ -182,34 +194,39 @@
182
194
  border-bottom-right-radius: 0;
183
195
  }
184
196
  .x-star-editor-editor > .x-star-editor-md-viewer {
197
+ border: 1px solid #dedede;
198
+ border-top: none;
199
+ border-bottom-right-radius: 0.3em;
200
+ border-bottom-left-radius: 0.3em;
185
201
  padding: 0 1em;
186
202
  flex: 1;
187
203
  background-color: #fff;
204
+ border-bottom-left-radius: 0;
205
+ overflow: auto;
206
+ }
207
+ .x-star-editor-editor > .x-star-editor-active {
188
208
  border: 1px solid #dedede;
189
209
  border-top: none;
190
210
  border-bottom-right-radius: 0.3em;
191
- overflow: auto;
211
+ border-bottom-left-radius: 0.3em;
212
+ }
213
+ .x-star-editor-editor > .x-star-editor-hidden {
214
+ display: none;
192
215
  }
193
216
 
194
217
  .x-star-editor-md-editor {
195
- padding: 0 1em;
196
- line-height: 1.6em;
197
- white-space: break-spaces;
198
- background-color: #fff;
218
+ position: relative;
219
+ color: rgba(0, 0, 0, 0.88);
199
220
  border: 1px solid #dedede;
200
221
  border-top: none;
201
222
  border-bottom-right-radius: 0.3em;
202
223
  border-bottom-left-radius: 0.3em;
224
+ padding: 0 1em;
225
+ line-height: 1.6em;
226
+ white-space: break-spaces;
227
+ background-color: #fff;
203
228
  outline: none;
204
229
  overflow: auto;
205
- position: relative;
206
- color: rgba(0, 0, 0, 0.88);
207
- }
208
- .x-star-editor-md-editor > :first-child {
209
- margin-top: 1em;
210
- }
211
- .x-star-editor-md-editor > :last-child {
212
- margin-bottom: 1em;
213
230
  }
214
231
  .x-star-editor-md-editor *,
215
232
  .x-star-editor-md-editor ::before,
@@ -219,6 +236,12 @@
219
236
  .x-star-editor-md-editor ::selection {
220
237
  background-color: #c1def1;
221
238
  }
239
+ .x-star-editor-md-editor > :first-child {
240
+ margin-top: 0.5em;
241
+ }
242
+ .x-star-editor-md-editor > :last-child {
243
+ margin-bottom: 0.5em;
244
+ }
222
245
 
223
246
  .x-star-editor-md-viewer {
224
247
  position: relative;
@@ -41,4 +41,5 @@ export declare const useContainer: (ref: React.RefObject<HTMLDivElement>) => {
41
41
  getOffset: (root: Node, targetNode: Node, targetOffset: number) => number;
42
42
  getSelection: () => ContainerSelection;
43
43
  setSelection: ({ anchorOffset, focusOffset }: ContainerSelection) => void;
44
+ normalizeSelection: () => void;
44
45
  };
@@ -1,5 +1,5 @@
1
1
  import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper";
2
- import { useEffect, useRef } from 'react';
2
+ import { useRef } from 'react';
3
3
  import { editorRender } from "./markdown";
4
4
 
5
5
  /**
@@ -294,57 +294,49 @@ export var useContainer = function useContainer(ref) {
294
294
  range.setEnd(focus.node, focus.offset);
295
295
  var rangeRect = range.getBoundingClientRect();
296
296
  var containerRect = ref.current.getBoundingClientRect();
297
- var topOffset = rangeRect.top - containerRect.top;
298
- var bottomOffset = rangeRect.bottom - containerRect.bottom;
297
+ var topOffset = Math.round(rangeRect.top - containerRect.top);
298
+ var bottomOffset = Math.round(rangeRect.bottom - containerRect.bottom);
299
299
 
300
300
  // 将光标滚动到视口内
301
- if (topOffset < 0) {
302
- ref.current.scrollTop += topOffset;
303
- } else if (bottomOffset > 0) {
304
- ref.current.scrollTop += bottomOffset;
301
+ if (topOffset < 1) {
302
+ ref.current.scrollTop += topOffset - 1;
303
+ } else if (bottomOffset > -1) {
304
+ ref.current.scrollTop += bottomOffset + 1;
305
305
  }
306
306
  };
307
307
 
308
- // 确保光标不在零宽空格后
309
- useEffect(function () {
310
- var listener = function listener() {
311
- var _ref$current, _anchorNode$nodeValue, _focusNode$nodeValue;
312
- var selection = window.getSelection();
313
- if (!selection || !selection.anchorNode || !selection.focusNode || !((_ref$current = ref.current) !== null && _ref$current !== void 0 && _ref$current.contains(selection.anchorNode))) {
314
- return;
315
- }
316
- var update = false;
317
- var anchorNode = selection.anchorNode,
318
- anchorOffset = selection.anchorOffset,
319
- focusNode = selection.focusNode,
320
- focusOffset = selection.focusOffset;
321
- if (anchorNode.nodeType === Node.TEXT_NODE && ((_anchorNode$nodeValue = anchorNode.nodeValue) === null || _anchorNode$nodeValue === void 0 ? void 0 : _anchorNode$nodeValue[anchorOffset - 1]) === "\u200B") {
322
- update = true;
323
- anchorOffset--;
324
- }
325
- if (focusNode.nodeType === Node.TEXT_NODE && ((_focusNode$nodeValue = focusNode.nodeValue) === null || _focusNode$nodeValue === void 0 ? void 0 : _focusNode$nodeValue[focusOffset - 1]) === "\u200B") {
326
- update = true;
327
- focusOffset--;
328
- }
329
- if (update) {
330
- selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
331
- }
332
- };
333
- document.addEventListener('selectionchange', listener, {
334
- capture: true,
335
- passive: true
336
- });
337
- return function () {
338
- return document.removeEventListener('selectionchange', listener, {
339
- capture: true
340
- });
341
- };
342
- }, []);
308
+ /**
309
+ * 标准化容器选区,确保光标不在零宽空格后
310
+ */
311
+ var normalizeSelection = function normalizeSelection() {
312
+ var _anchorNode$nodeValue, _focusNode$nodeValue;
313
+ var selection = window.getSelection();
314
+ if (!selection || !selection.anchorNode || !selection.focusNode || !ref.current) {
315
+ return;
316
+ }
317
+ var update = false;
318
+ var anchorNode = selection.anchorNode,
319
+ anchorOffset = selection.anchorOffset,
320
+ focusNode = selection.focusNode,
321
+ focusOffset = selection.focusOffset;
322
+ if (anchorNode.nodeType === Node.TEXT_NODE && ((_anchorNode$nodeValue = anchorNode.nodeValue) === null || _anchorNode$nodeValue === void 0 ? void 0 : _anchorNode$nodeValue[anchorOffset - 1]) === "\u200B") {
323
+ update = true;
324
+ anchorOffset--;
325
+ }
326
+ if (focusNode.nodeType === Node.TEXT_NODE && ((_focusNode$nodeValue = focusNode.nodeValue) === null || _focusNode$nodeValue === void 0 ? void 0 : _focusNode$nodeValue[focusOffset - 1]) === "\u200B") {
327
+ update = true;
328
+ focusOffset--;
329
+ }
330
+ if (update) {
331
+ selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
332
+ }
333
+ };
343
334
  return useRef({
344
335
  getText: getText,
345
336
  setText: setText,
346
337
  getOffset: getOffset,
347
338
  getSelection: getSelection,
348
- setSelection: setSelection
339
+ setSelection: setSelection,
340
+ normalizeSelection: normalizeSelection
349
341
  }).current;
350
342
  };
@@ -66,7 +66,7 @@ export interface SelectAction {
66
66
  selection: ContainerSelection;
67
67
  }
68
68
  type HistoryAction = UndoAction | RedoAction | BatchAction | SelectAction;
69
- export declare const useHistory: (initialSourceCode: string) => {
69
+ export declare const useHistory: (initialSourceCode: string, readOnly?: boolean) => {
70
70
  history: History;
71
71
  sourceCode: string;
72
72
  selection: ContainerSelection;
@@ -1,7 +1,7 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
2
2
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
3
3
  import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
4
- import { useReducer } from 'react';
4
+ import { useReducer, useRef } from 'react';
5
5
  import { createSelection, getRange } from "./container";
6
6
 
7
7
  /**
@@ -268,7 +268,7 @@ var historyReducer = function historyReducer(_ref2, action) {
268
268
  }
269
269
  }
270
270
  };
271
- export var useHistory = function useHistory(initialSourceCode) {
271
+ export var useHistory = function useHistory(initialSourceCode, readOnly) {
272
272
  var _useReducer = useReducer(historyReducer, initialSourceCode, function (text) {
273
273
  var action = {
274
274
  type: 'insert',
@@ -292,10 +292,16 @@ export var useHistory = function useHistory(initialSourceCode) {
292
292
  _useReducer2 = _slicedToArray(_useReducer, 2),
293
293
  history = _useReducer2[0],
294
294
  dispatch = _useReducer2[1];
295
+ var readOnlyLatest = useRef(readOnly);
296
+ readOnlyLatest.current = readOnly;
295
297
  return {
296
298
  history: history,
297
299
  sourceCode: history.states[history.index].sourceCode,
298
300
  selection: history.selection,
299
- dispatch: dispatch
301
+ dispatch: useRef(function (action) {
302
+ if (!readOnlyLatest.current) {
303
+ dispatch(action);
304
+ }
305
+ }).current
300
306
  };
301
307
  };
@@ -3,6 +3,7 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
3
3
  import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime";
4
4
  import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
5
5
  import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper";
6
+ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
6
7
  import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
7
8
  import { toText as hastToText } from 'hast-util-to-text';
8
9
  import { Fragment, jsx, jsxs } from 'react/jsx-runtime';
@@ -31,6 +32,20 @@ import rehypeRawPositions from "./rehype/rehype-raw-positions";
31
32
  */
32
33
  var toMdastProcessor = unified().use(remarkParse).use(remarkGfm).use(remarkMath).freeze();
33
34
 
35
+ /**
36
+ * 带缓存的解析函数
37
+ *
38
+ * @param sourceCode Markdown 文本
39
+ * @returns Mdast 树
40
+ */
41
+ var cachedParse = function () {
42
+ var cache = {};
43
+ return function (sourceCode) {
44
+ var _cache$sourceCode;
45
+ return (_cache$sourceCode = cache[sourceCode]) !== null && _cache$sourceCode !== void 0 ? _cache$sourceCode : (cache = _defineProperty({}, sourceCode, toMdastProcessor.parse(sourceCode)))[sourceCode];
46
+ };
47
+ }();
48
+
34
49
  /**
35
50
  * 将 Markdown 文本映射到 DOM 树,第一层为 `<div>` 块元素,第二层为 `<div>` 行元素,之后均为 `<span>` 元素
36
51
  *
@@ -228,7 +243,7 @@ export var editorRender = function editorRender(sourceCode) {
228
243
  }
229
244
  addText(sourceCode.slice(scanOffset, parentEndOffset), true);
230
245
  };
231
- getChildren(toMdastProcessor.parse(sourceCode).children, sourceCode.length);
246
+ getChildren(cachedParse(sourceCode).children, sourceCode.length);
232
247
  if (scanOffset <= sourceCode.length) {
233
248
  // 添加最后一行文本
234
249
  block.append(createLine(sourceCode.slice(scanOffset)));
@@ -274,7 +289,7 @@ export var preViewerRender = /*#__PURE__*/function () {
274
289
  while (1) switch (_context.prev = _context.next) {
275
290
  case 0:
276
291
  _context.next = 2;
277
- return toHastProcessor.run(toMdastProcessor.parse(sourceCode));
292
+ return toHastProcessor.run(JSON.parse(JSON.stringify(cachedParse(sourceCode))));
278
293
  case 2:
279
294
  return _context.abrupt("return", _context.sent);
280
295
  case 3:
@@ -25,6 +25,10 @@ export interface XStarEditorProps {
25
25
  * 初始文本
26
26
  */
27
27
  initialValue?: string;
28
+ /**
29
+ * 是否只读
30
+ */
31
+ readOnly?: XStarMdEditorProps['readOnly'];
28
32
  /**
29
33
  * 是否启用 Web Worker
30
34
  */
@@ -32,7 +36,7 @@ export interface XStarEditorProps {
32
36
  /**
33
37
  * 编辑器属性
34
38
  */
35
- editorProps?: Omit<XStarMdEditorProps, 'height' | 'locale' | 'initialValue' | 'onChange' | 'onInsertFile'>;
39
+ editorProps?: Omit<XStarMdEditorProps, 'height' | 'locale' | 'initialValue' | 'readOnly' | 'onChange' | 'onInsertFile'>;
36
40
  /**
37
41
  * 查看器属性
38
42
  */
@@ -1,8 +1,16 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
3
+ import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
2
4
  import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
3
5
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
4
6
  import classNames from 'classnames';
5
- import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
7
+ import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
8
+ import { createKeybindingsHandler } from 'tinykeys';
9
+ import SvgEditOnly from "../icons/EditOnly";
10
+ import SvgEnterFullscreen from "../icons/EnterFullscreen";
11
+ import SvgExitFullscreen from "../icons/ExitFullscreen";
12
+ import SvgViewOnly from "../icons/ViewOnly";
13
+ import { getFormat } from "../locales";
6
14
  import { prefix } from "../utils/global";
7
15
  import XStarMdEditor, { useMdEditorRef } from "../x-star-md-editor";
8
16
  import XStarMdViewer, { useMdViewerRef } from "../x-star-md-viewer";
@@ -28,12 +36,14 @@ var ViewerRenderWrapper = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
28
36
  }, children);
29
37
  });
30
38
  var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
39
+ var _classNames4, _classNames5, _classNames6;
31
40
  var className = _ref2.className,
32
41
  style = _ref2.style,
33
42
  height = _ref2.height,
34
43
  locale = _ref2.locale,
35
44
  _ref2$initialValue = _ref2.initialValue,
36
45
  initialValue = _ref2$initialValue === void 0 ? '' : _ref2$initialValue,
46
+ readOnly = _ref2.readOnly,
37
47
  enableWebWorker = _ref2.enableWebWorker,
38
48
  editorProps = _ref2.editorProps,
39
49
  viewerProps = _ref2.viewerProps,
@@ -56,6 +66,9 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
56
66
  var _editorRef$current, _viewerRef$current;
57
67
  var editor = (_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 ? void 0 : _editorRef$current.getEditorContainer();
58
68
  var viewer = (_viewerRef$current = viewerRef.current) === null || _viewerRef$current === void 0 ? void 0 : _viewerRef$current.getViewerContainer();
69
+ if (!editor || !viewer) {
70
+ return;
71
+ }
59
72
 
60
73
  /**
61
74
  * 获取两个父元素的子元素到各自父元素顶部的距离
@@ -114,7 +127,7 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
114
127
  var listener = function listener(e) {
115
128
  var ignore = ignoreNext;
116
129
  ignoreNext = false;
117
- if (ignore || !editor || !viewer) {
130
+ if (ignore) {
118
131
  return;
119
132
  }
120
133
  var _ref3 = e.currentTarget === editor ? [editor, viewer] : [viewer, editor],
@@ -127,7 +140,7 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
127
140
  toOffsetTops = _getOffsetTops2[1];
128
141
  for (var i = 0; i < fromOffsetTops.length - 1; i++) {
129
142
  if (fromOffsetTops[i] <= fromElement.scrollTop && fromElement.scrollTop < fromOffsetTops[i + 1]) {
130
- var scrollTop = (fromElement.scrollTop - fromOffsetTops[i]) / (fromOffsetTops[i + 1] - fromOffsetTops[i]) * (toOffsetTops[i + 1] - toOffsetTops[i]) + toOffsetTops[i];
143
+ var scrollTop = Math.round((fromElement.scrollTop - fromOffsetTops[i]) / (fromOffsetTops[i + 1] - fromOffsetTops[i]) * (toOffsetTops[i + 1] - toOffsetTops[i]) + toOffsetTops[i]);
131
144
  if (toElement.scrollTop !== scrollTop) {
132
145
  ignoreNext = true;
133
146
  toElement.scrollTop = scrollTop;
@@ -136,37 +149,105 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
136
149
  }
137
150
  }
138
151
  };
139
- editor === null || editor === void 0 ? void 0 : editor.addEventListener('scroll', listener, {
140
- capture: true,
141
- passive: true
152
+ var observer = new MutationObserver(function () {
153
+ return listener({
154
+ currentTarget: editor
155
+ });
142
156
  });
143
- viewer === null || viewer === void 0 ? void 0 : viewer.addEventListener('scroll', listener, {
144
- capture: true,
145
- passive: true
157
+ editor.addEventListener('scroll', listener);
158
+ viewer.addEventListener('scroll', listener);
159
+ observer.observe(viewer, {
160
+ subtree: true,
161
+ childList: true
146
162
  });
147
163
  return function () {
148
- editor === null || editor === void 0 ? void 0 : editor.removeEventListener('scroll', listener, {
149
- capture: true
150
- });
151
- viewer === null || viewer === void 0 ? void 0 : viewer.removeEventListener('scroll', listener, {
152
- capture: true
153
- });
164
+ editor.removeEventListener('scroll', listener);
165
+ viewer.removeEventListener('scroll', listener);
166
+ observer.disconnect();
154
167
  };
155
168
  }, []);
156
169
  var _useState = useState(initialValue),
157
170
  _useState2 = _slicedToArray(_useState, 2),
158
171
  value = _useState2[0],
159
172
  setValue = _useState2[1];
173
+ var _useState3 = useState(['editor', 'viewer']),
174
+ _useState4 = _slicedToArray(_useState3, 2),
175
+ layout = _useState4[0],
176
+ setLayout = _useState4[1];
177
+ var editOnly = layout.includes('editor') && !layout.includes('viewer');
178
+ var viewOnly = !layout.includes('editor') && layout.includes('viewer');
179
+ var _useState5 = useState(false),
180
+ _useState6 = _slicedToArray(_useState5, 2),
181
+ fullscreen = _useState6[0],
182
+ setFullscreen = _useState6[1];
183
+ var editorPlugins = useMemo(function () {
184
+ var _editorProps$plugins;
185
+ return [function (ctx) {
186
+ var t = getFormat(locale, 'toolbarItem');
187
+ ctx.toolbarItemMap.editOnly = {
188
+ children: /*#__PURE__*/React.createElement(SvgEditOnly, {
189
+ className: classNames("".concat(prefix, "icon"), _defineProperty({}, "".concat(prefix, "active"), editOnly))
190
+ }),
191
+ tooltip: t('editOnly'),
192
+ onClick: function onClick() {
193
+ return setLayout(editOnly ? ['editor', 'viewer'] : ['editor']);
194
+ }
195
+ };
196
+ ctx.toolbarItemMap.viewOnly = {
197
+ children: /*#__PURE__*/React.createElement(SvgViewOnly, {
198
+ className: classNames("".concat(prefix, "icon"), _defineProperty({}, "".concat(prefix, "active"), viewOnly))
199
+ }),
200
+ tooltip: t('viewOnly'),
201
+ onClick: function onClick() {
202
+ return setLayout(viewOnly ? ['editor', 'viewer'] : ['viewer']);
203
+ }
204
+ };
205
+ ctx.toolbarItemMap.fullscreen = {
206
+ children: fullscreen ? /*#__PURE__*/React.createElement(SvgExitFullscreen, {
207
+ className: classNames("".concat(prefix, "icon"))
208
+ }) : /*#__PURE__*/React.createElement(SvgEnterFullscreen, {
209
+ className: classNames("".concat(prefix, "icon"))
210
+ }),
211
+ tooltip: fullscreen ? t('exitFullscreen') : t('enterFullscreen'),
212
+ onClick: function onClick() {
213
+ return setFullscreen(!fullscreen);
214
+ }
215
+ };
216
+ ctx.toolbarItems.push(['editOnly', 'viewOnly', 'fullscreen']);
217
+ }].concat(_toConsumableArray((_editorProps$plugins = editorProps === null || editorProps === void 0 ? void 0 : editorProps.plugins) !== null && _editorProps$plugins !== void 0 ? _editorProps$plugins : []));
218
+ }, [locale, editOnly, viewOnly, fullscreen, editorProps === null || editorProps === void 0 ? void 0 : editorProps.plugins]);
219
+
220
+ // 防止出现两个滚动条,按下 Esc 退出全屏
221
+ useEffect(function () {
222
+ if (fullscreen) {
223
+ var overflow = document.body.style.overflow;
224
+ var listener = createKeybindingsHandler({
225
+ Escape: function Escape(e) {
226
+ e.stopPropagation();
227
+ setFullscreen(false);
228
+ }
229
+ });
230
+ document.body.style.overflow = 'hidden';
231
+ document.addEventListener('keydown', listener);
232
+ return function () {
233
+ document.body.style.overflow = overflow;
234
+ document.removeEventListener('keydown', listener);
235
+ };
236
+ }
237
+ }, [fullscreen]);
160
238
  return /*#__PURE__*/React.createElement("div", {
161
239
  ref: containerRef,
162
- className: classNames("".concat(prefix, "editor"), className),
240
+ className: classNames("".concat(prefix, "editor"), _defineProperty({}, "".concat(prefix, "fullscreen"), fullscreen), className),
163
241
  style: style
164
242
  }, /*#__PURE__*/React.createElement(XStarMdEditor, _extends({
165
243
  ref: editorRef
166
244
  }, editorProps, {
245
+ className: classNames((_classNames4 = {}, _defineProperty(_classNames4, "".concat(prefix, "active"), editOnly), _defineProperty(_classNames4, "".concat(prefix, "hidden"), viewOnly), _classNames4), editorProps === null || editorProps === void 0 ? void 0 : editorProps.className),
167
246
  height: height,
168
247
  locale: locale,
169
248
  initialValue: initialValue,
249
+ readOnly: viewOnly || readOnly,
250
+ plugins: editorPlugins,
170
251
  onChange: function onChange(value) {
171
252
  setValue(value);
172
253
  _onChange === null || _onChange === void 0 ? void 0 : _onChange(value);
@@ -174,12 +255,13 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
174
255
  onInsertFile: onInsertFile
175
256
  })), viewerRender ? /*#__PURE__*/React.createElement(ViewerRenderWrapper, {
176
257
  ref: viewerRef,
177
- className: viewerProps === null || viewerProps === void 0 ? void 0 : viewerProps.className,
258
+ className: classNames((_classNames5 = {}, _defineProperty(_classNames5, "".concat(prefix, "active"), viewOnly), _defineProperty(_classNames5, "".concat(prefix, "hidden"), editOnly), _classNames5), viewerProps === null || viewerProps === void 0 ? void 0 : viewerProps.className),
178
259
  style: viewerProps === null || viewerProps === void 0 ? void 0 : viewerProps.style,
179
260
  height: height
180
261
  }, viewerRender(value)) : /*#__PURE__*/React.createElement(XStarMdViewer, _extends({
181
262
  ref: viewerRef
182
263
  }, viewerProps, {
264
+ className: classNames((_classNames6 = {}, _defineProperty(_classNames6, "".concat(prefix, "active"), viewOnly), _defineProperty(_classNames6, "".concat(prefix, "hidden"), editOnly), _classNames6), viewerProps === null || viewerProps === void 0 ? void 0 : viewerProps.className),
183
265
  height: height,
184
266
  value: value,
185
267
  enableWebWorker: enableWebWorker
@@ -51,6 +51,10 @@ export interface XStarMdEditorProps {
51
51
  * 初始文本
52
52
  */
53
53
  initialValue?: string;
54
+ /**
55
+ * 是否只读
56
+ */
57
+ readOnly?: boolean;
54
58
  /**
55
59
  * 插件
56
60
  */
@@ -16,12 +16,13 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
16
16
  locale = _ref.locale,
17
17
  _ref$initialValue = _ref.initialValue,
18
18
  initialValue = _ref$initialValue === void 0 ? '' : _ref$initialValue,
19
+ readOnly = _ref.readOnly,
19
20
  plugins = _ref.plugins,
20
21
  onChange = _ref.onChange,
21
22
  onInsertFile = _ref.onInsertFile;
22
23
  var containerRef = useRef(null);
23
24
  var container = useContainer(containerRef);
24
- var _useHistory = useHistory(initialValue),
25
+ var _useHistory = useHistory(initialValue, readOnly),
25
26
  history = _useHistory.history,
26
27
  sourceCode = _useHistory.sourceCode,
27
28
  selection = _useHistory.selection,
@@ -124,6 +125,11 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
124
125
  });
125
126
  break;
126
127
  }
128
+ case 'compositionstart':
129
+ {
130
+ container.normalizeSelection();
131
+ break;
132
+ }
127
133
  }
128
134
  };
129
135
  var dragEventHandler = function dragEventHandler(e) {
@@ -186,7 +192,7 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
186
192
  var _containerRef$current;
187
193
  var listener = function listener(e) {
188
194
  var targetRange = e.getTargetRanges()[0];
189
- if (!containerRef.current || !targetRange) {
195
+ if (!targetRange || !containerRef.current) {
190
196
  return;
191
197
  }
192
198
  var startOffset = container.getOffset(containerRef.current, targetRange.startContainer, targetRange.startOffset);
@@ -369,6 +375,7 @@ var XStarMdEditor = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
369
375
  contentEditable: true,
370
376
  onBlur: focusEventHandler,
371
377
  onCompositionEnd: compositionEventHandler,
378
+ onCompositionStart: compositionEventHandler,
372
379
  onCopy: clipboardEventHandler,
373
380
  onCut: clipboardEventHandler,
374
381
  onDragStart: dragEventHandler,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x-star-editor",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "An editor developed by turingstar",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",