x-star-editor 0.3.4 → 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]);
@@ -78,7 +78,7 @@ var Item = function Item(_ref) {
78
78
  return /*#__PURE__*/React.createElement("div", {
79
79
  className: classNames("".concat(prefix, "item-container"))
80
80
  }, /*#__PURE__*/React.createElement("div", {
81
- 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)),
82
82
  onClick: function onClick() {
83
83
  if (!disabled) {
84
84
  setTooltipOpen(false);
@@ -315,6 +315,7 @@ export var getDefaultToolbarItemMap = function getDefaultToolbarItemMap(locale,
315
315
  tooltip: t('image'),
316
316
  popoverRender: function popoverRender(exec, close) {
317
317
  return /*#__PURE__*/React.createElement(FileInput, {
318
+ image: true,
318
319
  onCancel: close,
319
320
  onOk: function onOk(data) {
320
321
  if (data.type === 'file') {
@@ -114,7 +114,7 @@
114
114
  background-color: rgba(230, 247, 255, 0.7);
115
115
  cursor: not-allowed;
116
116
  }
117
- .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 {
118
118
  background-color: #cbedfd;
119
119
  }
120
120
  .x-star-editor-toolbar .x-star-editor-item-container .x-star-editor-item .x-star-editor-icon {
@@ -179,7 +179,7 @@
179
179
  .x-star-editor-editor.x-star-editor-fullscreen {
180
180
  position: fixed;
181
181
  inset: 0;
182
- z-index: 1000;
182
+ z-index: 9999;
183
183
  }
184
184
  .x-star-editor-editor.x-star-editor-fullscreen > .x-star-editor-md-editor,
185
185
  .x-star-editor-editor.x-star-editor-fullscreen > .x-star-editor-md-viewer {
@@ -237,10 +237,10 @@
237
237
  background-color: #c1def1;
238
238
  }
239
239
  .x-star-editor-md-editor > :first-child {
240
- margin-top: 1em;
240
+ margin-top: 0.5em;
241
241
  }
242
242
  .x-star-editor-md-editor > :last-child {
243
- margin-bottom: 1em;
243
+ margin-bottom: 0.5em;
244
244
  }
245
245
 
246
246
  .x-star-editor-md-viewer {
@@ -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,52 +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
- return function () {
335
- return document.removeEventListener('selectionchange', listener);
336
- };
337
- }, []);
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
+ };
338
334
  return useRef({
339
335
  getText: getText,
340
336
  setText: setText,
341
337
  getOffset: getOffset,
342
338
  getSelection: getSelection,
343
- setSelection: setSelection
339
+ setSelection: setSelection,
340
+ normalizeSelection: normalizeSelection
344
341
  }).current;
345
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
  };
@@ -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
  */
@@ -5,6 +5,7 @@ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
5
5
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
6
6
  import classNames from 'classnames';
7
7
  import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
8
+ import { createKeybindingsHandler } from 'tinykeys';
8
9
  import SvgEditOnly from "../icons/EditOnly";
9
10
  import SvgEnterFullscreen from "../icons/EnterFullscreen";
10
11
  import SvgExitFullscreen from "../icons/ExitFullscreen";
@@ -42,6 +43,7 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
42
43
  locale = _ref2.locale,
43
44
  _ref2$initialValue = _ref2.initialValue,
44
45
  initialValue = _ref2$initialValue === void 0 ? '' : _ref2$initialValue,
46
+ readOnly = _ref2.readOnly,
45
47
  enableWebWorker = _ref2.enableWebWorker,
46
48
  editorProps = _ref2.editorProps,
47
49
  viewerProps = _ref2.viewerProps,
@@ -64,6 +66,9 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
64
66
  var _editorRef$current, _viewerRef$current;
65
67
  var editor = (_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 ? void 0 : _editorRef$current.getEditorContainer();
66
68
  var viewer = (_viewerRef$current = viewerRef.current) === null || _viewerRef$current === void 0 ? void 0 : _viewerRef$current.getViewerContainer();
69
+ if (!editor || !viewer) {
70
+ return;
71
+ }
67
72
 
68
73
  /**
69
74
  * 获取两个父元素的子元素到各自父元素顶部的距离
@@ -122,7 +127,7 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
122
127
  var listener = function listener(e) {
123
128
  var ignore = ignoreNext;
124
129
  ignoreNext = false;
125
- if (ignore || !editor || !viewer) {
130
+ if (ignore) {
126
131
  return;
127
132
  }
128
133
  var _ref3 = e.currentTarget === editor ? [editor, viewer] : [viewer, editor],
@@ -135,7 +140,7 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
135
140
  toOffsetTops = _getOffsetTops2[1];
136
141
  for (var i = 0; i < fromOffsetTops.length - 1; i++) {
137
142
  if (fromOffsetTops[i] <= fromElement.scrollTop && fromElement.scrollTop < fromOffsetTops[i + 1]) {
138
- 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]);
139
144
  if (toElement.scrollTop !== scrollTop) {
140
145
  ignoreNext = true;
141
146
  toElement.scrollTop = scrollTop;
@@ -144,11 +149,21 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
144
149
  }
145
150
  }
146
151
  };
147
- editor === null || editor === void 0 ? void 0 : editor.addEventListener('scroll', listener);
148
- viewer === null || viewer === void 0 ? void 0 : viewer.addEventListener('scroll', listener);
152
+ var observer = new MutationObserver(function () {
153
+ return listener({
154
+ currentTarget: editor
155
+ });
156
+ });
157
+ editor.addEventListener('scroll', listener);
158
+ viewer.addEventListener('scroll', listener);
159
+ observer.observe(viewer, {
160
+ subtree: true,
161
+ childList: true
162
+ });
149
163
  return function () {
150
- editor === null || editor === void 0 ? void 0 : editor.removeEventListener('scroll', listener);
151
- viewer === null || viewer === void 0 ? void 0 : viewer.removeEventListener('scroll', listener);
164
+ editor.removeEventListener('scroll', listener);
165
+ viewer.removeEventListener('scroll', listener);
166
+ observer.disconnect();
152
167
  };
153
168
  }, []);
154
169
  var _useState = useState(initialValue),
@@ -202,13 +217,21 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
202
217
  }].concat(_toConsumableArray((_editorProps$plugins = editorProps === null || editorProps === void 0 ? void 0 : editorProps.plugins) !== null && _editorProps$plugins !== void 0 ? _editorProps$plugins : []));
203
218
  }, [locale, editOnly, viewOnly, fullscreen, editorProps === null || editorProps === void 0 ? void 0 : editorProps.plugins]);
204
219
 
205
- // 防止出现两个滚动条
220
+ // 防止出现两个滚动条,按下 Esc 退出全屏
206
221
  useEffect(function () {
207
222
  if (fullscreen) {
208
223
  var overflow = document.body.style.overflow;
224
+ var listener = createKeybindingsHandler({
225
+ Escape: function Escape(e) {
226
+ e.stopPropagation();
227
+ setFullscreen(false);
228
+ }
229
+ });
209
230
  document.body.style.overflow = 'hidden';
231
+ document.addEventListener('keydown', listener);
210
232
  return function () {
211
233
  document.body.style.overflow = overflow;
234
+ document.removeEventListener('keydown', listener);
212
235
  };
213
236
  }
214
237
  }, [fullscreen]);
@@ -223,6 +246,7 @@ var XStarEditor = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
223
246
  height: height,
224
247
  locale: locale,
225
248
  initialValue: initialValue,
249
+ readOnly: viewOnly || readOnly,
226
250
  plugins: editorPlugins,
227
251
  onChange: function onChange(value) {
228
252
  setValue(value);
@@ -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) {
@@ -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.4",
3
+ "version": "0.3.5",
4
4
  "description": "An editor developed by turingstar",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",