truerte-react 0.0.3
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/CHANGELOG.md +58 -0
- package/LICENSE.txt +22 -0
- package/README.md +54 -0
- package/lib/cjs/main/ts/EventNames.d.ts +8 -0
- package/lib/cjs/main/ts/EventNames.js +87 -0
- package/lib/cjs/main/ts/Events.d.ts +94 -0
- package/lib/cjs/main/ts/Events.js +2 -0
- package/lib/cjs/main/ts/ScriptLoader2.d.ts +10 -0
- package/lib/cjs/main/ts/ScriptLoader2.js +138 -0
- package/lib/cjs/main/ts/TrueRTE.d.ts +3 -0
- package/lib/cjs/main/ts/TrueRTE.js +8 -0
- package/lib/cjs/main/ts/Utils.d.ts +18 -0
- package/lib/cjs/main/ts/Utils.js +53 -0
- package/lib/cjs/main/ts/Uuid.d.ts +6 -0
- package/lib/cjs/main/ts/Uuid.js +16 -0
- package/lib/cjs/main/ts/components/Editor.d.ts +171 -0
- package/lib/cjs/main/ts/components/Editor.js +358 -0
- package/lib/cjs/main/ts/index.d.ts +2 -0
- package/lib/cjs/main/ts/index.js +5 -0
- package/lib/es2015/main/ts/EventNames.d.ts +8 -0
- package/lib/es2015/main/ts/EventNames.js +84 -0
- package/lib/es2015/main/ts/Events.d.ts +94 -0
- package/lib/es2015/main/ts/Events.js +1 -0
- package/lib/es2015/main/ts/ScriptLoader2.d.ts +10 -0
- package/lib/es2015/main/ts/ScriptLoader2.js +135 -0
- package/lib/es2015/main/ts/TrueRTE.d.ts +3 -0
- package/lib/es2015/main/ts/TrueRTE.js +5 -0
- package/lib/es2015/main/ts/Utils.d.ts +18 -0
- package/lib/es2015/main/ts/Utils.js +43 -0
- package/lib/es2015/main/ts/Uuid.d.ts +6 -0
- package/lib/es2015/main/ts/Uuid.js +12 -0
- package/lib/es2015/main/ts/components/Editor.d.ts +171 -0
- package/lib/es2015/main/ts/components/Editor.js +354 -0
- package/lib/es2015/main/ts/index.d.ts +2 -0
- package/lib/es2015/main/ts/index.js +2 -0
- package/package.json +63 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official TrueRTE React component
|
|
3
|
+
* Copyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.
|
|
4
|
+
* Copyright (c) 2024 TrueRTE contributors
|
|
5
|
+
* Licensed under the MIT license (https://github.com/truerte/truerte-react/blob/main/LICENSE.TXT)
|
|
6
|
+
*/
|
|
7
|
+
import * as React from 'react';
|
|
8
|
+
import { IEvents } from '../Events';
|
|
9
|
+
import type { Editor as TrueRTEEditor, TrueRTE } from 'truerte';
|
|
10
|
+
type OmitStringIndexSignature<T> = {
|
|
11
|
+
[K in keyof T as string extends K ? never : K]: T[K];
|
|
12
|
+
};
|
|
13
|
+
interface DoNotUse<T extends string> {
|
|
14
|
+
__brand: T;
|
|
15
|
+
}
|
|
16
|
+
type OmittedInitProps = 'selector' | 'target' | 'readonly';
|
|
17
|
+
type EditorOptions = Parameters<TrueRTE['init']>[0];
|
|
18
|
+
export type InitOptions = Omit<OmitStringIndexSignature<EditorOptions>, OmittedInitProps> & {
|
|
19
|
+
selector?: DoNotUse<'selector prop is handled internally by the component'>;
|
|
20
|
+
target?: DoNotUse<'target prop is handled internally by the component'>;
|
|
21
|
+
readonly?: DoNotUse<'readonly prop is overridden by the component, use the `disabled` prop instead'>;
|
|
22
|
+
} & {
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
export type Version = `${'1'}${'' | `.${number}` | `.${number}.${number}`}`;
|
|
26
|
+
export type EditorInstanceRef = React.Ref<TrueRTEEditor | null>;
|
|
27
|
+
export interface IProps {
|
|
28
|
+
/**
|
|
29
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#id React Tech Ref - id}
|
|
30
|
+
* @description The ID of the element to render the editor into.
|
|
31
|
+
*/
|
|
32
|
+
id: string;
|
|
33
|
+
/**
|
|
34
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#inline React Tech Ref - inline}
|
|
35
|
+
* @description Whether the editor should be rendered inline. Equivalent to the `inline` option in TrueRTE.
|
|
36
|
+
*/
|
|
37
|
+
inline: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#initialvalue React Tech Ref - initialValue}
|
|
40
|
+
* @description The initial HTML content of the editor.
|
|
41
|
+
*
|
|
42
|
+
* IMPORTANT: Ensure that this is **not** updated by `onEditorChange` or the editor will be unusable.
|
|
43
|
+
*/
|
|
44
|
+
initialValue: string;
|
|
45
|
+
/**
|
|
46
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#oneditorchange React Tech Ref - onEditorChange}
|
|
47
|
+
* @description Used to store the state of the editor outside the component. Typically used for controlled components.
|
|
48
|
+
* @param a The current HTML content of the editor.
|
|
49
|
+
* @param editor The TrueRTE editor instance.
|
|
50
|
+
* @returns void
|
|
51
|
+
*/
|
|
52
|
+
onEditorChange: (a: string, editor: TrueRTEEditor) => void;
|
|
53
|
+
/**
|
|
54
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#value React Tech Ref - value}
|
|
55
|
+
* @description The current HTML content of the editor. Typically used for controlled components.
|
|
56
|
+
*/
|
|
57
|
+
value: string;
|
|
58
|
+
/**
|
|
59
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#init React Tech Ref - init}
|
|
60
|
+
* @description Additional settings passed to `truerte.init()` when initializing the editor.
|
|
61
|
+
*/
|
|
62
|
+
init: InitOptions;
|
|
63
|
+
/**
|
|
64
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#tagname React Tech Ref - tagName}
|
|
65
|
+
* @description The tag name of the element to render the editor into. Only valid when `inline` is `true`.
|
|
66
|
+
*/
|
|
67
|
+
tagName: string;
|
|
68
|
+
/**
|
|
69
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#tabIndex React Tech Ref - tabIndex}
|
|
70
|
+
* @description The tab index of the element that the editor wraps.
|
|
71
|
+
*/
|
|
72
|
+
tabIndex: number;
|
|
73
|
+
/**
|
|
74
|
+
* @description The TrueRTE version to use when loading from jsDelivr CDN. By default, version 1
|
|
75
|
+
* (that is, the latest minor and patch release of the major version 1) will be used.
|
|
76
|
+
* For more info about the possible version formats, see the {@link https://www.jsdelivr.com/documentation#id-npm jsDelivr documentation}.
|
|
77
|
+
*/
|
|
78
|
+
cdnVersion: Version;
|
|
79
|
+
/**
|
|
80
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#plugins React Tech Ref - plugins}
|
|
81
|
+
* @description The plugins to load into the editor. Equivalent to the `plugins` option in TrueRTE.
|
|
82
|
+
*/
|
|
83
|
+
plugins: NonNullable<EditorOptions['plugins']>;
|
|
84
|
+
/**
|
|
85
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#toolbar React Tech Ref - toolbar}
|
|
86
|
+
* @description The toolbar to load into the editor. Equivalent to the `toolbar` option in TrueRTE.
|
|
87
|
+
*/
|
|
88
|
+
toolbar: NonNullable<EditorOptions['toolbar']>;
|
|
89
|
+
/**
|
|
90
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#disabled React Tech Ref - disabled}
|
|
91
|
+
* @description Whether the editor should be "disabled" (read-only).
|
|
92
|
+
*/
|
|
93
|
+
disabled: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#textareaname React Tech Ref - textareaName}
|
|
96
|
+
* @description Set the `name` attribute of the `textarea` element used for the editor in forms. Only valid in iframe mode.
|
|
97
|
+
*/
|
|
98
|
+
textareaName: string;
|
|
99
|
+
/**
|
|
100
|
+
* @description Optional external ref that receives the underlying TrueRTE editor instance.
|
|
101
|
+
* Use this for imperative control from outside the component.
|
|
102
|
+
*/
|
|
103
|
+
editorRef: EditorInstanceRef;
|
|
104
|
+
/**
|
|
105
|
+
* @description The URL of the TrueRTE script to lazy load.
|
|
106
|
+
*/
|
|
107
|
+
truerteScriptSrc: string;
|
|
108
|
+
/**
|
|
109
|
+
* @description When true, automatically configures `icons: 'truerte-lucide'`
|
|
110
|
+
* unless you explicitly provide `init.icons` or `init.icons_url`.
|
|
111
|
+
*/
|
|
112
|
+
useLucideIcons: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#rollback React Tech Ref - rollback}
|
|
115
|
+
* @description The number of milliseconds to wait before reverting to the previous value when the editor's content changes.
|
|
116
|
+
*/
|
|
117
|
+
rollback: number | false;
|
|
118
|
+
/**
|
|
119
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/#scriptloading React Tech Ref - scriptLoading}
|
|
120
|
+
* @description Options for how the TrueRTE script should be loaded.
|
|
121
|
+
* @property async Whether the script should be loaded with the `async` attribute.
|
|
122
|
+
* @property defer Whether the script should be loaded with the `defer` attribute.
|
|
123
|
+
* @property delay The number of milliseconds to wait before loading the script.
|
|
124
|
+
*/
|
|
125
|
+
scriptLoading: {
|
|
126
|
+
async?: boolean;
|
|
127
|
+
defer?: boolean;
|
|
128
|
+
delay?: number;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export interface IAllProps extends Partial<IProps>, Partial<IEvents> {
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/ TrueRTE React Technical Reference}
|
|
135
|
+
*/
|
|
136
|
+
export declare class Editor extends React.Component<IAllProps> {
|
|
137
|
+
static defaultProps: Partial<IAllProps>;
|
|
138
|
+
editor?: TrueRTEEditor;
|
|
139
|
+
private id;
|
|
140
|
+
private elementRef;
|
|
141
|
+
private inline;
|
|
142
|
+
private currentContent?;
|
|
143
|
+
private boundHandlers;
|
|
144
|
+
private rollbackTimer;
|
|
145
|
+
private valueCursor;
|
|
146
|
+
constructor(props: Partial<IAllProps>);
|
|
147
|
+
private get view();
|
|
148
|
+
componentDidUpdate(prevProps: Partial<IAllProps>): void;
|
|
149
|
+
componentDidMount(): void;
|
|
150
|
+
componentWillUnmount(): void;
|
|
151
|
+
render(): React.ReactElement<{
|
|
152
|
+
ref: React.RefObject<HTMLElement | null>;
|
|
153
|
+
id: string;
|
|
154
|
+
tabIndex: number | undefined;
|
|
155
|
+
}, string | React.JSXElementConstructor<any>>;
|
|
156
|
+
getEditor(): TrueRTEEditor | undefined;
|
|
157
|
+
private beforeInputEvent;
|
|
158
|
+
private renderInline;
|
|
159
|
+
private renderIframe;
|
|
160
|
+
private getScriptSources;
|
|
161
|
+
private getInitialValue;
|
|
162
|
+
private bindHandlers;
|
|
163
|
+
private rollbackChange;
|
|
164
|
+
private handleBeforeInput;
|
|
165
|
+
private handleBeforeInputSpecial;
|
|
166
|
+
private handleEditorChange;
|
|
167
|
+
private handleEditorChangeSpecial;
|
|
168
|
+
private assignEditorRef;
|
|
169
|
+
private initialise;
|
|
170
|
+
}
|
|
171
|
+
export {};
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Editor = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Official TrueRTE React component
|
|
6
|
+
* Copyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.
|
|
7
|
+
* Copyright (c) 2024 TrueRTE contributors
|
|
8
|
+
* Licensed under the MIT license (https://github.com/truerte/truerte-react/blob/main/LICENSE.TXT)
|
|
9
|
+
*/
|
|
10
|
+
const React = require("react");
|
|
11
|
+
const ScriptLoader2_1 = require("../ScriptLoader2");
|
|
12
|
+
const TrueRTE_1 = require("../TrueRTE");
|
|
13
|
+
const Utils_1 = require("../Utils");
|
|
14
|
+
const Uuid_1 = require("../Uuid");
|
|
15
|
+
const changeEvents = 'change keyup compositionend setcontent CommentChange';
|
|
16
|
+
/**
|
|
17
|
+
* @see {@link https://www.truerte.org/docs/truerte/1/react-ref/ TrueRTE React Technical Reference}
|
|
18
|
+
*/
|
|
19
|
+
class Editor extends React.Component {
|
|
20
|
+
constructor(props) {
|
|
21
|
+
var _a, _b, _c;
|
|
22
|
+
super(props);
|
|
23
|
+
this.rollbackTimer = undefined;
|
|
24
|
+
this.valueCursor = undefined;
|
|
25
|
+
this.rollbackChange = () => {
|
|
26
|
+
const editor = this.editor;
|
|
27
|
+
const value = this.props.value;
|
|
28
|
+
if (editor && value && value !== this.currentContent) {
|
|
29
|
+
editor.undoManager.ignore(() => {
|
|
30
|
+
editor.setContent(value);
|
|
31
|
+
// only restore cursor on inline editors when they are focused
|
|
32
|
+
// as otherwise it will cause a focus grab
|
|
33
|
+
if (this.valueCursor && (!this.inline || editor.hasFocus())) {
|
|
34
|
+
try {
|
|
35
|
+
editor.selection.moveToBookmark(this.valueCursor);
|
|
36
|
+
}
|
|
37
|
+
catch (e) { /* ignore */ }
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
this.rollbackTimer = undefined;
|
|
42
|
+
};
|
|
43
|
+
this.handleBeforeInput = (_evt) => {
|
|
44
|
+
if (this.props.value !== undefined && this.props.value === this.currentContent && this.editor) {
|
|
45
|
+
if (!this.inline || this.editor.hasFocus()) {
|
|
46
|
+
try {
|
|
47
|
+
// getBookmark throws exceptions when the editor has not been focused
|
|
48
|
+
// possibly only in inline mode but I'm not taking chances
|
|
49
|
+
this.valueCursor = this.editor.selection.getBookmark(3);
|
|
50
|
+
}
|
|
51
|
+
catch (e) { /* ignore */ }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
this.handleBeforeInputSpecial = (evt) => {
|
|
56
|
+
if (evt.key === 'Enter' || evt.key === 'Backspace' || evt.key === 'Delete') {
|
|
57
|
+
this.handleBeforeInput(evt);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
this.handleEditorChange = (_evt) => {
|
|
61
|
+
const editor = this.editor;
|
|
62
|
+
if (editor && editor.initialized) {
|
|
63
|
+
const newContent = editor.getContent();
|
|
64
|
+
if (this.props.value !== undefined && this.props.value !== newContent && this.props.rollback !== false) {
|
|
65
|
+
// start a timer and revert to the value if not applied in time
|
|
66
|
+
if (!this.rollbackTimer) {
|
|
67
|
+
this.rollbackTimer = window.setTimeout(this.rollbackChange, typeof this.props.rollback === 'number' ? this.props.rollback : 200);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (newContent !== this.currentContent) {
|
|
71
|
+
this.currentContent = newContent;
|
|
72
|
+
if ((0, Utils_1.isFunction)(this.props.onEditorChange)) {
|
|
73
|
+
this.props.onEditorChange(newContent, editor);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
this.handleEditorChangeSpecial = (evt) => {
|
|
79
|
+
if (evt.key === 'Backspace' || evt.key === 'Delete') {
|
|
80
|
+
this.handleEditorChange(evt);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
this.assignEditorRef = (editorRef, editor) => {
|
|
84
|
+
if (editorRef === undefined || editorRef === null) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (typeof editorRef === 'function') {
|
|
88
|
+
editorRef(editor);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
editorRef.current = editor;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
this.initialise = (attempts = 0) => {
|
|
95
|
+
var _a, _b, _c, _d, _e, _f;
|
|
96
|
+
const target = this.elementRef.current;
|
|
97
|
+
if (!target) {
|
|
98
|
+
return; // Editor has been unmounted
|
|
99
|
+
}
|
|
100
|
+
if (!target.isConnected) {
|
|
101
|
+
// this is probably someone trying to help by rendering us offscreen
|
|
102
|
+
// but we can't do that because the editor iframe must be in the document
|
|
103
|
+
// in order to have state
|
|
104
|
+
// TODO: how will this do when we use web component?
|
|
105
|
+
if (attempts === 0) {
|
|
106
|
+
// we probably just need to wait for the current events to be processed
|
|
107
|
+
setTimeout(() => this.initialise(1), 1);
|
|
108
|
+
}
|
|
109
|
+
else if (attempts < 100) {
|
|
110
|
+
// wait for ten seconds, polling every tenth of a second
|
|
111
|
+
setTimeout(() => this.initialise(attempts + 1), 100);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// give up, at this point it seems that more polling is unlikely to help
|
|
115
|
+
throw new Error('truerte can only be initialised when in a document');
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const truerte = (0, TrueRTE_1.getTrueRTE)(this.view);
|
|
120
|
+
if (!truerte) {
|
|
121
|
+
throw new Error('truerte should have been loaded into global scope');
|
|
122
|
+
}
|
|
123
|
+
const resolvedPlugins = (0, Utils_1.mergePlugins)((_a = this.props.init) === null || _a === void 0 ? void 0 : _a.plugins, this.props.plugins);
|
|
124
|
+
const hasExplicitIconConfig = ((_b = this.props.init) === null || _b === void 0 ? void 0 : _b.icons) !== undefined || ((_c = this.props.init) === null || _c === void 0 ? void 0 : _c.icons_url) !== undefined;
|
|
125
|
+
const resolvedIconPack = this.props.useLucideIcons === true && !hasExplicitIconConfig
|
|
126
|
+
? 'truerte-lucide'
|
|
127
|
+
: (_d = this.props.init) === null || _d === void 0 ? void 0 : _d.icons;
|
|
128
|
+
const finalInit = Object.assign(Object.assign({}, this.props.init), { selector: undefined, target, readonly: this.props.disabled, inline: this.inline, plugins: resolvedPlugins, icons: resolvedIconPack, toolbar: (_e = this.props.toolbar) !== null && _e !== void 0 ? _e : (_f = this.props.init) === null || _f === void 0 ? void 0 : _f.toolbar, setup: (editor) => {
|
|
129
|
+
this.editor = editor;
|
|
130
|
+
this.assignEditorRef(this.props.editorRef, editor);
|
|
131
|
+
this.bindHandlers({});
|
|
132
|
+
// When running in inline mode the editor gets the initial value
|
|
133
|
+
// from the innerHTML of the element it is initialized on.
|
|
134
|
+
// However we don't want to take on the responsibility of sanitizing
|
|
135
|
+
// to remove XSS in the react integration so we have a chicken and egg
|
|
136
|
+
// problem... We avoid it by sneaking in a set content before the first
|
|
137
|
+
// "official" setContent and using TrueRTE to do the sanitization.
|
|
138
|
+
if (this.inline && !(0, Utils_1.isTextareaOrInput)(target)) {
|
|
139
|
+
editor.once('PostRender', (_evt) => {
|
|
140
|
+
editor.setContent(this.getInitialValue(), { no_events: true });
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (this.props.init && (0, Utils_1.isFunction)(this.props.init.setup)) {
|
|
144
|
+
this.props.init.setup(editor);
|
|
145
|
+
}
|
|
146
|
+
}, init_instance_callback: (editor) => {
|
|
147
|
+
var _a, _b;
|
|
148
|
+
// check for changes that happened since truerte.init() was called
|
|
149
|
+
const initialValue = this.getInitialValue();
|
|
150
|
+
this.currentContent = (_a = this.currentContent) !== null && _a !== void 0 ? _a : editor.getContent();
|
|
151
|
+
if (this.currentContent !== initialValue) {
|
|
152
|
+
this.currentContent = initialValue;
|
|
153
|
+
// same as resetContent in TrueRTE 5
|
|
154
|
+
editor.setContent(initialValue);
|
|
155
|
+
editor.undoManager.clear();
|
|
156
|
+
editor.undoManager.add();
|
|
157
|
+
editor.setDirty(false);
|
|
158
|
+
}
|
|
159
|
+
const disabled = (_b = this.props.disabled) !== null && _b !== void 0 ? _b : false;
|
|
160
|
+
(0, Utils_1.setMode)(this.editor, disabled ? 'readonly' : 'design');
|
|
161
|
+
// ensure existing init_instance_callback is called
|
|
162
|
+
if (this.props.init && (0, Utils_1.isFunction)(this.props.init.init_instance_callback)) {
|
|
163
|
+
this.props.init.init_instance_callback(editor);
|
|
164
|
+
}
|
|
165
|
+
} });
|
|
166
|
+
if (!this.inline) {
|
|
167
|
+
target.style.visibility = '';
|
|
168
|
+
}
|
|
169
|
+
if ((0, Utils_1.isTextareaOrInput)(target)) {
|
|
170
|
+
target.value = this.getInitialValue();
|
|
171
|
+
}
|
|
172
|
+
truerte.init(finalInit);
|
|
173
|
+
};
|
|
174
|
+
this.id = this.props.id || (0, Uuid_1.uuid)('truerte-react');
|
|
175
|
+
this.elementRef = React.createRef();
|
|
176
|
+
this.inline = (_c = (_a = this.props.inline) !== null && _a !== void 0 ? _a : (_b = this.props.init) === null || _b === void 0 ? void 0 : _b.inline) !== null && _c !== void 0 ? _c : false;
|
|
177
|
+
this.boundHandlers = {};
|
|
178
|
+
}
|
|
179
|
+
get view() {
|
|
180
|
+
var _a, _b;
|
|
181
|
+
return (_b = (_a = this.elementRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window;
|
|
182
|
+
}
|
|
183
|
+
componentDidUpdate(prevProps) {
|
|
184
|
+
var _a, _b, _c;
|
|
185
|
+
if (this.props.editorRef !== prevProps.editorRef) {
|
|
186
|
+
this.assignEditorRef(prevProps.editorRef, null);
|
|
187
|
+
this.assignEditorRef(this.props.editorRef, (_a = this.editor) !== null && _a !== void 0 ? _a : null);
|
|
188
|
+
}
|
|
189
|
+
if (this.rollbackTimer) {
|
|
190
|
+
clearTimeout(this.rollbackTimer);
|
|
191
|
+
this.rollbackTimer = undefined;
|
|
192
|
+
}
|
|
193
|
+
if (this.editor) {
|
|
194
|
+
this.bindHandlers(prevProps);
|
|
195
|
+
if (this.editor.initialized) {
|
|
196
|
+
this.currentContent = (_b = this.currentContent) !== null && _b !== void 0 ? _b : this.editor.getContent();
|
|
197
|
+
if (typeof this.props.initialValue === 'string' && this.props.initialValue !== prevProps.initialValue) {
|
|
198
|
+
// same as resetContent in TinyMCE 5 – TODO what does this mean for TrueRTE?
|
|
199
|
+
this.editor.setContent(this.props.initialValue);
|
|
200
|
+
this.editor.undoManager.clear();
|
|
201
|
+
this.editor.undoManager.add();
|
|
202
|
+
this.editor.setDirty(false);
|
|
203
|
+
}
|
|
204
|
+
else if (typeof this.props.value === 'string' && this.props.value !== this.currentContent) {
|
|
205
|
+
const localEditor = this.editor;
|
|
206
|
+
localEditor.undoManager.transact(() => {
|
|
207
|
+
// inline editors grab focus when restoring selection
|
|
208
|
+
// so we don't try to keep their selection unless they are currently focused
|
|
209
|
+
let cursor;
|
|
210
|
+
if (!this.inline || localEditor.hasFocus()) {
|
|
211
|
+
try {
|
|
212
|
+
// getBookmark throws exceptions when the editor has not been focused
|
|
213
|
+
// possibly only in inline mode but I'm not taking chances
|
|
214
|
+
cursor = localEditor.selection.getBookmark(3);
|
|
215
|
+
}
|
|
216
|
+
catch (e) { /* ignore */ }
|
|
217
|
+
}
|
|
218
|
+
const valueCursor = this.valueCursor;
|
|
219
|
+
localEditor.setContent(this.props.value);
|
|
220
|
+
if (!this.inline || localEditor.hasFocus()) {
|
|
221
|
+
for (const bookmark of [cursor, valueCursor]) {
|
|
222
|
+
if (bookmark) {
|
|
223
|
+
try {
|
|
224
|
+
localEditor.selection.moveToBookmark(bookmark);
|
|
225
|
+
this.valueCursor = bookmark;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
catch (e) { /* ignore */ }
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (this.props.disabled !== prevProps.disabled) {
|
|
235
|
+
const disabled = (_c = this.props.disabled) !== null && _c !== void 0 ? _c : false;
|
|
236
|
+
(0, Utils_1.setMode)(this.editor, disabled ? 'readonly' : 'design');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
componentDidMount() {
|
|
242
|
+
var _a, _b, _c, _d, _e;
|
|
243
|
+
if ((0, TrueRTE_1.getTrueRTE)(this.view) !== null) {
|
|
244
|
+
this.initialise();
|
|
245
|
+
}
|
|
246
|
+
else if (Array.isArray(this.props.truerteScriptSrc) && this.props.truerteScriptSrc.length === 0) {
|
|
247
|
+
(_b = (_a = this.props).onScriptsLoadError) === null || _b === void 0 ? void 0 : _b.call(_a, new Error('No `truerte` global is present but the `truerteScriptSrc` prop was an empty array.'));
|
|
248
|
+
}
|
|
249
|
+
else if ((_c = this.elementRef.current) === null || _c === void 0 ? void 0 : _c.ownerDocument) {
|
|
250
|
+
const successHandler = () => {
|
|
251
|
+
var _a, _b;
|
|
252
|
+
(_b = (_a = this.props).onScriptsLoad) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
253
|
+
this.initialise();
|
|
254
|
+
};
|
|
255
|
+
const errorHandler = (err) => {
|
|
256
|
+
var _a, _b;
|
|
257
|
+
(_b = (_a = this.props).onScriptsLoadError) === null || _b === void 0 ? void 0 : _b.call(_a, err);
|
|
258
|
+
};
|
|
259
|
+
ScriptLoader2_1.ScriptLoader.loadList(this.elementRef.current.ownerDocument, this.getScriptSources(), (_e = (_d = this.props.scriptLoading) === null || _d === void 0 ? void 0 : _d.delay) !== null && _e !== void 0 ? _e : 0, successHandler, errorHandler);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
componentWillUnmount() {
|
|
263
|
+
const editor = this.editor;
|
|
264
|
+
if (editor) {
|
|
265
|
+
editor.off(changeEvents, this.handleEditorChange);
|
|
266
|
+
editor.off(this.beforeInputEvent(), this.handleBeforeInput);
|
|
267
|
+
editor.off('keypress', this.handleEditorChangeSpecial);
|
|
268
|
+
editor.off('keydown', this.handleBeforeInputSpecial);
|
|
269
|
+
editor.off('NewBlock', this.handleEditorChange);
|
|
270
|
+
Object.keys(this.boundHandlers).forEach((eventName) => {
|
|
271
|
+
editor.off(eventName, this.boundHandlers[eventName]);
|
|
272
|
+
});
|
|
273
|
+
this.boundHandlers = {};
|
|
274
|
+
editor.remove();
|
|
275
|
+
this.editor = undefined;
|
|
276
|
+
}
|
|
277
|
+
this.assignEditorRef(this.props.editorRef, null);
|
|
278
|
+
}
|
|
279
|
+
render() {
|
|
280
|
+
return this.inline ? this.renderInline() : this.renderIframe();
|
|
281
|
+
}
|
|
282
|
+
getEditor() {
|
|
283
|
+
return this.editor;
|
|
284
|
+
}
|
|
285
|
+
beforeInputEvent() {
|
|
286
|
+
return (0, Utils_1.isBeforeInputEventAvailable)() ? 'beforeinput SelectionChange' : 'SelectionChange';
|
|
287
|
+
}
|
|
288
|
+
renderInline() {
|
|
289
|
+
const { tagName = 'div' } = this.props;
|
|
290
|
+
return React.createElement(tagName, {
|
|
291
|
+
ref: this.elementRef,
|
|
292
|
+
id: this.id,
|
|
293
|
+
tabIndex: this.props.tabIndex
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
renderIframe() {
|
|
297
|
+
return React.createElement('textarea', {
|
|
298
|
+
ref: this.elementRef,
|
|
299
|
+
style: { visibility: 'hidden' },
|
|
300
|
+
name: this.props.textareaName,
|
|
301
|
+
id: this.id,
|
|
302
|
+
tabIndex: this.props.tabIndex
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
getScriptSources() {
|
|
306
|
+
var _a, _b;
|
|
307
|
+
const async = (_a = this.props.scriptLoading) === null || _a === void 0 ? void 0 : _a.async;
|
|
308
|
+
const defer = (_b = this.props.scriptLoading) === null || _b === void 0 ? void 0 : _b.defer;
|
|
309
|
+
if (this.props.truerteScriptSrc !== undefined) {
|
|
310
|
+
if (typeof this.props.truerteScriptSrc === 'string') {
|
|
311
|
+
return [{ src: this.props.truerteScriptSrc, async, defer }];
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// fallback to jsDelivr CDN when the truerteScriptSrc is not specified
|
|
315
|
+
// `cdnVersion` is in `defaultProps`, so it's always defined.
|
|
316
|
+
const cdnLink = `https://cdn.jsdelivr.net/npm/truerte@${this.props.cdnVersion}/truerte.min.js`;
|
|
317
|
+
return [{ src: cdnLink, async, defer }];
|
|
318
|
+
}
|
|
319
|
+
getInitialValue() {
|
|
320
|
+
if (typeof this.props.initialValue === 'string') {
|
|
321
|
+
return this.props.initialValue;
|
|
322
|
+
}
|
|
323
|
+
else if (typeof this.props.value === 'string') {
|
|
324
|
+
return this.props.value;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
return '';
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
bindHandlers(prevProps) {
|
|
331
|
+
if (this.editor !== undefined) {
|
|
332
|
+
// typescript chokes trying to understand the type of the lookup function
|
|
333
|
+
(0, Utils_1.configHandlers)(this.editor, prevProps, this.props, this.boundHandlers, (key) => this.props[key]);
|
|
334
|
+
// check if we should monitor editor changes
|
|
335
|
+
const isValueControlled = (p) => p.onEditorChange !== undefined || p.value !== undefined;
|
|
336
|
+
const wasControlled = isValueControlled(prevProps);
|
|
337
|
+
const nowControlled = isValueControlled(this.props);
|
|
338
|
+
if (!wasControlled && nowControlled) {
|
|
339
|
+
this.editor.on(changeEvents, this.handleEditorChange);
|
|
340
|
+
this.editor.on(this.beforeInputEvent(), this.handleBeforeInput);
|
|
341
|
+
this.editor.on('keydown', this.handleBeforeInputSpecial);
|
|
342
|
+
this.editor.on('keyup', this.handleEditorChangeSpecial);
|
|
343
|
+
this.editor.on('NewBlock', this.handleEditorChange);
|
|
344
|
+
}
|
|
345
|
+
else if (wasControlled && !nowControlled) {
|
|
346
|
+
this.editor.off(changeEvents, this.handleEditorChange);
|
|
347
|
+
this.editor.off(this.beforeInputEvent(), this.handleBeforeInput);
|
|
348
|
+
this.editor.off('keydown', this.handleBeforeInputSpecial);
|
|
349
|
+
this.editor.off('keyup', this.handleEditorChangeSpecial);
|
|
350
|
+
this.editor.off('NewBlock', this.handleEditorChange);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
exports.Editor = Editor;
|
|
356
|
+
Editor.defaultProps = {
|
|
357
|
+
cdnVersion: '1',
|
|
358
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official TrueRTE React component
|
|
3
|
+
* Copyright (c) 2026 TrueRTE contributors
|
|
4
|
+
* Licensed under the MIT license (https://github.com/truerte/truerte-react/blob/main/LICENSE.TXT)
|
|
5
|
+
*/
|
|
6
|
+
export declare const nativeEvents: readonly ["BeforePaste", "Blur", "Click", "CompositionEnd", "CompositionStart", "CompositionUpdate", "ContextMenu", "Copy", "Cut", "Dblclick", "Drag", "DragDrop", "DragEnd", "DragGesture", "DragOver", "Drop", "Focus", "FocusIn", "FocusOut", "Input", "KeyDown", "KeyPress", "KeyUp", "MouseDown", "MouseEnter", "MouseLeave", "MouseMove", "MouseOut", "MouseOver", "MouseUp", "Paste", "SelectionChange"];
|
|
7
|
+
export declare const customEvents: readonly ["Activate", "AddUndo", "BeforeAddUndo", "BeforeExecCommand", "BeforeGetContent", "BeforeRenderUI", "BeforeSetContent", "Change", "ClearUndos", "CommentChange", "Deactivate", "Dirty", "ExecCommand", "GetContent", "Hide", "IconsLoadError", "Init", "LanguageLoadError", "LoadContent", "ModelLoadError", "NodeChange", "ObjectResizeStart", "ObjectResized", "ObjectSelected", "PluginLoadError", "PostProcess", "PostRender", "PreProcess", "ProgressState", "Redo", "Remove", "Reset", "ResizeEditor", "SaveContent", "SetAttrib", "SetContent", "Show", "SkinLoadError", "Submit", "ThemeLoadError", "Undo", "VisualAid"];
|
|
8
|
+
export declare const validEvents: readonly ["BeforePaste", "Blur", "Click", "CompositionEnd", "CompositionStart", "CompositionUpdate", "ContextMenu", "Copy", "Cut", "Dblclick", "Drag", "DragDrop", "DragEnd", "DragGesture", "DragOver", "Drop", "Focus", "FocusIn", "FocusOut", "Input", "KeyDown", "KeyPress", "KeyUp", "MouseDown", "MouseEnter", "MouseLeave", "MouseMove", "MouseOut", "MouseOver", "MouseUp", "Paste", "SelectionChange", "Activate", "AddUndo", "BeforeAddUndo", "BeforeExecCommand", "BeforeGetContent", "BeforeRenderUI", "BeforeSetContent", "Change", "ClearUndos", "CommentChange", "Deactivate", "Dirty", "ExecCommand", "GetContent", "Hide", "IconsLoadError", "Init", "LanguageLoadError", "LoadContent", "ModelLoadError", "NodeChange", "ObjectResizeStart", "ObjectResized", "ObjectSelected", "PluginLoadError", "PostProcess", "PostRender", "PreProcess", "ProgressState", "Redo", "Remove", "Reset", "ResizeEditor", "SaveContent", "SetAttrib", "SetContent", "Show", "SkinLoadError", "Submit", "ThemeLoadError", "Undo", "VisualAid"];
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official TrueRTE React component
|
|
3
|
+
* Copyright (c) 2026 TrueRTE contributors
|
|
4
|
+
* Licensed under the MIT license (https://github.com/truerte/truerte-react/blob/main/LICENSE.TXT)
|
|
5
|
+
*/
|
|
6
|
+
export const nativeEvents = [
|
|
7
|
+
'BeforePaste',
|
|
8
|
+
'Blur',
|
|
9
|
+
'Click',
|
|
10
|
+
'CompositionEnd',
|
|
11
|
+
'CompositionStart',
|
|
12
|
+
'CompositionUpdate',
|
|
13
|
+
'ContextMenu',
|
|
14
|
+
'Copy',
|
|
15
|
+
'Cut',
|
|
16
|
+
'Dblclick',
|
|
17
|
+
'Drag',
|
|
18
|
+
'DragDrop',
|
|
19
|
+
'DragEnd',
|
|
20
|
+
'DragGesture',
|
|
21
|
+
'DragOver',
|
|
22
|
+
'Drop',
|
|
23
|
+
'Focus',
|
|
24
|
+
'FocusIn',
|
|
25
|
+
'FocusOut',
|
|
26
|
+
'Input',
|
|
27
|
+
'KeyDown',
|
|
28
|
+
'KeyPress',
|
|
29
|
+
'KeyUp',
|
|
30
|
+
'MouseDown',
|
|
31
|
+
'MouseEnter',
|
|
32
|
+
'MouseLeave',
|
|
33
|
+
'MouseMove',
|
|
34
|
+
'MouseOut',
|
|
35
|
+
'MouseOver',
|
|
36
|
+
'MouseUp',
|
|
37
|
+
'Paste',
|
|
38
|
+
'SelectionChange',
|
|
39
|
+
];
|
|
40
|
+
export const customEvents = [
|
|
41
|
+
'Activate',
|
|
42
|
+
'AddUndo',
|
|
43
|
+
'BeforeAddUndo',
|
|
44
|
+
'BeforeExecCommand',
|
|
45
|
+
'BeforeGetContent',
|
|
46
|
+
'BeforeRenderUI',
|
|
47
|
+
'BeforeSetContent',
|
|
48
|
+
'Change',
|
|
49
|
+
'ClearUndos',
|
|
50
|
+
'CommentChange',
|
|
51
|
+
'Deactivate',
|
|
52
|
+
'Dirty',
|
|
53
|
+
'ExecCommand',
|
|
54
|
+
'GetContent',
|
|
55
|
+
'Hide',
|
|
56
|
+
'IconsLoadError',
|
|
57
|
+
'Init',
|
|
58
|
+
'LanguageLoadError',
|
|
59
|
+
'LoadContent',
|
|
60
|
+
'ModelLoadError',
|
|
61
|
+
'NodeChange',
|
|
62
|
+
'ObjectResizeStart',
|
|
63
|
+
'ObjectResized',
|
|
64
|
+
'ObjectSelected',
|
|
65
|
+
'PluginLoadError',
|
|
66
|
+
'PostProcess',
|
|
67
|
+
'PostRender',
|
|
68
|
+
'PreProcess',
|
|
69
|
+
'ProgressState',
|
|
70
|
+
'Redo',
|
|
71
|
+
'Remove',
|
|
72
|
+
'Reset',
|
|
73
|
+
'ResizeEditor',
|
|
74
|
+
'SaveContent',
|
|
75
|
+
'SetAttrib',
|
|
76
|
+
'SetContent',
|
|
77
|
+
'Show',
|
|
78
|
+
'SkinLoadError',
|
|
79
|
+
'Submit',
|
|
80
|
+
'ThemeLoadError',
|
|
81
|
+
'Undo',
|
|
82
|
+
'VisualAid',
|
|
83
|
+
];
|
|
84
|
+
export const validEvents = [...nativeEvents, ...customEvents];
|