rn-rich-text-editor 0.0.16 → 0.0.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rn-rich-text-editor",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "A rich text editor component for React Native",
5
5
  "keywords": [
6
6
  "react-native",
package/src/Editor.js CHANGED
@@ -3,7 +3,7 @@ import { WebView } from 'react-native-webview';
3
3
  import { actions } from './actions';
4
4
  import { messages } from './messages';
5
5
  import { Keyboard, Platform, StyleSheet, TextInput, View, Linking } from 'react-native';
6
- import { createHTML } from './editor/createHTML';
6
+ import { createHTML, createReadOnlyHTML } from './editor/createHTML';
7
7
 
8
8
  const PlatformIOS = Platform.OS === 'ios';
9
9
 
@@ -18,6 +18,7 @@ export default class Editor extends Component {
18
18
  initialContentHTML: '',
19
19
  initialFocus: false,
20
20
  disabled: false,
21
+ readOnly: false,
21
22
  useContainer: true,
22
23
  pasteAsPlainText: false,
23
24
  autoCapitalize: 'off',
@@ -69,39 +70,49 @@ export default class Editor extends Component {
69
70
  initialHeight,
70
71
  initialFocus,
71
72
  disabled,
73
+ readOnly,
72
74
  styleWithCSS,
73
75
  useCharacter,
74
76
  defaultHttps,
77
+ initialContentHTML,
75
78
  } = props;
79
+ const contentForReadOnly = initialContentHTML || (typeof html === 'string' ? html : (html && html.html)) || '';
76
80
  that.state = {
77
81
  html: {
78
- html:
79
- html ||
80
- createHTML({
81
- backgroundColor,
82
- color,
83
- caretColor,
84
- placeholderColor,
85
- initialCSSText,
86
- cssText,
87
- contentCSSText,
88
- pasteAsPlainText,
89
- pasteListener: !!onPaste,
90
- keyUpListener: !!onKeyUp,
91
- keyDownListener: !!onKeyDown,
92
- inputListener: !!onInput,
93
- enterKeyHint,
94
- autoCapitalize,
95
- autoCorrect,
96
- initialFocus: initialFocus && !disabled,
97
- defaultParagraphSeparator,
98
- firstFocusEnd,
99
- useContainer,
100
- styleWithCSS,
101
- useCharacter,
102
- defaultHttps,
103
-
104
- }),
82
+ html: readOnly
83
+ ? createReadOnlyHTML({
84
+ content: contentForReadOnly,
85
+ backgroundColor,
86
+ color,
87
+ initialCSSText,
88
+ cssText,
89
+ contentCSSText,
90
+ })
91
+ : (html ||
92
+ createHTML({
93
+ backgroundColor,
94
+ color,
95
+ caretColor,
96
+ placeholderColor,
97
+ initialCSSText,
98
+ cssText,
99
+ contentCSSText,
100
+ pasteAsPlainText,
101
+ pasteListener: !!onPaste,
102
+ keyUpListener: !!onKeyUp,
103
+ keyDownListener: !!onKeyDown,
104
+ inputListener: !!onInput,
105
+ enterKeyHint,
106
+ autoCapitalize,
107
+ autoCorrect,
108
+ initialFocus: initialFocus && !disabled,
109
+ defaultParagraphSeparator,
110
+ firstFocusEnd,
111
+ useContainer,
112
+ styleWithCSS,
113
+ useCharacter,
114
+ defaultHttps,
115
+ })),
105
116
  },
106
117
  keyboardHeight: 0,
107
118
  height: initialHeight > 0 ? initialHeight : DEFAULT_EDITOR_HEIGHT,
@@ -240,7 +251,27 @@ export default class Editor extends Component {
240
251
  }
241
252
 
242
253
  componentDidUpdate(prevProps, prevState, snapshot) {
243
- const { editorStyle, disabled, placeholder } = this.props;
254
+ const { editorStyle, disabled, placeholder, readOnly, initialContentHTML, html } = this.props;
255
+ if (readOnly) {
256
+ const contentChanged = initialContentHTML !== prevProps.initialContentHTML || html !== prevProps.html;
257
+ if (prevProps.readOnly !== readOnly || contentChanged || prevProps.editorStyle !== editorStyle) {
258
+ const contentForReadOnly = initialContentHTML || (typeof html === 'string' ? html : (html?.html)) || '';
259
+ const { backgroundColor, color, initialCSSText, cssText, contentCSSText } = editorStyle || {};
260
+ this.setState({
261
+ html: {
262
+ html: createReadOnlyHTML({
263
+ content: contentForReadOnly,
264
+ backgroundColor,
265
+ color,
266
+ initialCSSText,
267
+ cssText,
268
+ contentCSSText,
269
+ }),
270
+ },
271
+ });
272
+ }
273
+ return;
274
+ }
244
275
  if (prevProps.editorStyle !== editorStyle) {
245
276
  editorStyle && this.setContentStyle(editorStyle);
246
277
  }
@@ -258,16 +289,16 @@ export default class Editor extends Component {
258
289
 
259
290
  renderWebView() {
260
291
  let that = this;
261
- const { html, editorStyle, useContainer, style, onLink, dataDetectorTypes, ...rest } = that.props;
292
+ const { html, editorStyle, useContainer, style, onLink, dataDetectorTypes, readOnly, ...rest } = that.props;
262
293
  const { html: viewHTML } = that.state;
263
294
  return (
264
295
  <>
265
296
  <WebView
266
297
  useWebKit={true}
267
298
  scrollEnabled={false}
268
- hideKeyboardAccessoryView={true}
299
+ hideKeyboardAccessoryView={!readOnly}
269
300
  keyboardDisplayRequiresUserAction={false}
270
- nestedScrollEnabled={!useContainer}
301
+ nestedScrollEnabled={readOnly || !useContainer}
271
302
  style={[styles.webview, style]}
272
303
  {...rest}
273
304
  ref={that.setRef}
@@ -288,7 +319,7 @@ export default class Editor extends Component {
288
319
  return true;
289
320
  }}
290
321
  />
291
- {Platform.OS === 'android' && <TextInput ref={ref => (that._input = ref)} style={styles._input} />}
322
+ {!readOnly && Platform.OS === 'android' && <TextInput ref={ref => (that._input = ref)} style={styles._input} />}
292
323
  </>
293
324
  );
294
325
  }
@@ -452,7 +483,11 @@ export default class Editor extends Component {
452
483
 
453
484
  init() {
454
485
  let that = this;
455
- const { initialFocus, initialContentHTML, placeholder, editorInitializedCallback, disabled } = that.props;
486
+ const { initialFocus, initialContentHTML, placeholder, editorInitializedCallback, disabled, readOnly } = that.props;
487
+ if (readOnly) {
488
+ editorInitializedCallback();
489
+ return;
490
+ }
456
491
  initialContentHTML && that.setContentHTML(initialContentHTML);
457
492
  placeholder && that.setPlaceholder(placeholder);
458
493
  that.setDisable(disabled);
package/src/Toolbar.js CHANGED
@@ -63,6 +63,7 @@ export default class Toolbar extends Component {
63
63
  static defaultProps = {
64
64
  actions: defaultActions,
65
65
  disabled: false,
66
+ readOnly: false,
66
67
  iconTint: '#71787F',
67
68
  iconSize: 20,
68
69
  iconGap: 16,
@@ -373,7 +374,10 @@ export default class Toolbar extends Component {
373
374
  }
374
375
 
375
376
  render() {
376
- const { style, disabled, children, flatContainerStyle, horizontal = true } = this.props;
377
+ const { style, disabled, readOnly, children, flatContainerStyle, horizontal = true } = this.props;
378
+ if (readOnly) {
379
+ return null;
380
+ }
377
381
  const vStyle = [styles.barContainer, style, disabled && this._getButtonDisabledStyle()];
378
382
  return (
379
383
  <View style={vStyle}>
@@ -755,5 +755,69 @@ function createHTML(options = {}) {
755
755
  `;
756
756
  }
757
757
 
758
+ /**
759
+ * Escapes a string for safe embedding inside a JavaScript double-quoted string in HTML.
760
+ */
761
+ function escapeForScript(s) {
762
+ if (s == null) return '';
763
+ return String(s)
764
+ .replace(/\\/g, '\\\\')
765
+ .replace(/"/g, '\\"')
766
+ .replace(/\u2028/g, '\\u2028')
767
+ .replace(/\u2029/g, '\\u2029')
768
+ .replace(/<\/script/gi, '<\\/script');
769
+ }
770
+
771
+ /**
772
+ * Creates minimal HTML for read-only display: content in a fixed-height scrollable box.
773
+ * No editor, no toolbar. Content is escaped and embedded in the template.
774
+ */
775
+ function createReadOnlyHTML(options = {}) {
776
+ const {
777
+ content = '',
778
+ backgroundColor = '#FFF',
779
+ color = '#000033',
780
+ contentCSSText = '',
781
+ cssText = '',
782
+ initialCSSText = '',
783
+ } = options;
784
+ const escapedContent = escapeForScript(content);
785
+ return `
786
+ <!DOCTYPE html>
787
+ <html>
788
+ <head>
789
+ <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
790
+ <style>
791
+ ${initialCSSText}
792
+ * { outline: 0; -webkit-tap-highlight-color: rgba(0,0,0,0); box-sizing: border-box; }
793
+ html, body { margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; font-size: 1em; height: 100%; }
794
+ body { background-color: ${backgroundColor}; color: ${color}; }
795
+ .readonly-container {
796
+ height: 100%;
797
+ overflow-y: auto;
798
+ -webkit-overflow-scrolling: touch;
799
+ padding: 10px;
800
+ ${contentCSSText}
801
+ }
802
+ </style>
803
+ ${getContentCSS()}
804
+ <style>${cssText}</style>
805
+ </head>
806
+ <body>
807
+ <div id="readonly-content" class="readonly-container"></div>
808
+ <script>
809
+ (function() {
810
+ var content = "${escapedContent}";
811
+ var el = document.getElementById('readonly-content');
812
+ if (el) {
813
+ el.innerHTML = content;
814
+ }
815
+ })();
816
+ </script>
817
+ </body>
818
+ </html>
819
+ `;
820
+ }
821
+
758
822
  const HTML = createHTML();
759
- export { HTML, createHTML };
823
+ export { HTML, createHTML, createReadOnlyHTML };
package/src/index.d.ts CHANGED
@@ -39,6 +39,7 @@ export interface EditorProps {
39
39
  initialContentHTML?: string;
40
40
  initialFocus?: boolean;
41
41
  disabled?: boolean;
42
+ readOnly?: boolean;
42
43
  useContainer?: boolean;
43
44
  pasteAsPlainText?: boolean;
44
45
  autoCapitalize?: string;
@@ -69,6 +70,7 @@ export interface ToolbarProps {
69
70
  getEditor?: () => EditorRef | null;
70
71
  actions?: string[];
71
72
  disabled?: boolean;
73
+ readOnly?: boolean;
72
74
  iconTint?: string;
73
75
  selectedIconTint?: string;
74
76
  disabledIconTint?: string;