@mbs-dev/react-editor 1.0.6 → 1.0.8
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/dist/Editor.js +80 -18
- package/package.json +1 -1
- package/src/Editor.tsx +82 -48
- package/src/Editor.types.ts +3 -1
- package/types/Editor.d.ts +15 -9
- package/types/Editor.types.d.ts +1 -0
package/dist/Editor.js
CHANGED
|
@@ -10,6 +10,42 @@ var __assign = (this && this.__assign) || function () {
|
|
|
10
10
|
};
|
|
11
11
|
return __assign.apply(this, arguments);
|
|
12
12
|
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
13
49
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
50
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
51
|
};
|
|
@@ -36,41 +72,43 @@ var uploaderConfig = function (apiUrl, imageUrl) { return ({
|
|
|
36
72
|
isSuccess: function (e) {
|
|
37
73
|
var _a;
|
|
38
74
|
var fn = this.jodit;
|
|
39
|
-
if (((_a = e.data) === null || _a === void 0 ? void 0 : _a.files) && e.data.files.length) {
|
|
75
|
+
if (((_a = e === null || e === void 0 ? void 0 : e.data) === null || _a === void 0 ? void 0 : _a.files) && e.data.files.length) {
|
|
40
76
|
var tagName_1 = 'img';
|
|
41
77
|
e.data.files.forEach(function (filename) {
|
|
42
78
|
var elm = fn.createInside.element(tagName_1);
|
|
43
|
-
|
|
79
|
+
var src = imageUrl ? "".concat(imageUrl, "/").concat(filename) : filename;
|
|
80
|
+
elm.setAttribute('src', src);
|
|
44
81
|
fn.s.insertImage(elm, null, fn.o.imageDefaultWidth);
|
|
45
82
|
});
|
|
46
83
|
}
|
|
47
|
-
return e.success;
|
|
84
|
+
return !!(e === null || e === void 0 ? void 0 : e.success);
|
|
48
85
|
},
|
|
49
86
|
getMessage: function (e) {
|
|
50
87
|
var _a;
|
|
51
|
-
return ((_a = e.data) === null || _a === void 0 ? void 0 : _a.messages) && Array.isArray(e.data.messages)
|
|
88
|
+
return ((_a = e === null || e === void 0 ? void 0 : e.data) === null || _a === void 0 ? void 0 : _a.messages) && Array.isArray(e.data.messages)
|
|
52
89
|
? e.data.messages.join('')
|
|
53
90
|
: '';
|
|
54
91
|
},
|
|
55
92
|
process: function (resp) {
|
|
56
93
|
var files = [];
|
|
57
|
-
files.unshift(resp.data);
|
|
94
|
+
files.unshift(resp === null || resp === void 0 ? void 0 : resp.data);
|
|
58
95
|
return {
|
|
59
|
-
files: resp.data,
|
|
60
|
-
error: resp.msg,
|
|
61
|
-
msg: resp.msg,
|
|
96
|
+
files: resp === null || resp === void 0 ? void 0 : resp.data,
|
|
97
|
+
error: resp === null || resp === void 0 ? void 0 : resp.msg,
|
|
98
|
+
msg: resp === null || resp === void 0 ? void 0 : resp.msg,
|
|
62
99
|
};
|
|
63
100
|
},
|
|
64
101
|
error: function (e) {
|
|
65
102
|
this.j.e.fire('errorMessage', e.message, 'error', 4000);
|
|
66
103
|
},
|
|
67
104
|
defaultHandlerError: function (e) {
|
|
68
|
-
this.j.e.fire('errorMessage', e.message);
|
|
105
|
+
this.j.e.fire('errorMessage', (e === null || e === void 0 ? void 0 : e.message) || 'Upload error');
|
|
69
106
|
},
|
|
70
107
|
}); };
|
|
71
108
|
exports.uploaderConfig = uploaderConfig;
|
|
72
|
-
var config = function (
|
|
73
|
-
var
|
|
109
|
+
var config = function (_a) {
|
|
110
|
+
var _b = _a === void 0 ? {} : _a, includeUploader = _b.includeUploader, apiUrl = _b.apiUrl, imageUrl = _b.imageUrl, onDeleteImage = _b.onDeleteImage;
|
|
111
|
+
var base = {
|
|
74
112
|
readonly: false,
|
|
75
113
|
placeholder: 'Start typing...',
|
|
76
114
|
toolbarAdaptive: false,
|
|
@@ -116,17 +154,41 @@ var config = function (includeUploader, apiUrl, imageUrl, onDeleteImage) {
|
|
|
116
154
|
],
|
|
117
155
|
};
|
|
118
156
|
if (includeUploader) {
|
|
119
|
-
|
|
157
|
+
base.uploader = (0, exports.uploaderConfig)(apiUrl, imageUrl);
|
|
120
158
|
}
|
|
121
159
|
if (onDeleteImage) {
|
|
122
|
-
|
|
123
|
-
var
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
160
|
+
base.events = __assign(__assign({}, (base.events || {})), { afterInit: function (editor) {
|
|
161
|
+
var _this = this;
|
|
162
|
+
var extractImageSrcs = function (html) {
|
|
163
|
+
var container = document.createElement('div');
|
|
164
|
+
container.innerHTML = html || '';
|
|
165
|
+
var imgs = Array.from(container.getElementsByTagName('img'));
|
|
166
|
+
return new Set(imgs
|
|
167
|
+
.map(function (img) { return img.getAttribute('src') || ''; })
|
|
168
|
+
.filter(function (src) { return !!src; }));
|
|
169
|
+
};
|
|
170
|
+
var prevValue = editor.value || '';
|
|
171
|
+
var prevSrcs = extractImageSrcs(prevValue);
|
|
172
|
+
editor.events.on('change', function () { return __awaiter(_this, void 0, void 0, function () {
|
|
173
|
+
var currentValue, currentSrcs;
|
|
174
|
+
return __generator(this, function (_a) {
|
|
175
|
+
currentValue = editor.value || '';
|
|
176
|
+
currentSrcs = extractImageSrcs(currentValue);
|
|
177
|
+
prevSrcs.forEach(function (src) {
|
|
178
|
+
if (!currentSrcs.has(src)) {
|
|
179
|
+
if (!imageUrl || src.startsWith(imageUrl)) {
|
|
180
|
+
void onDeleteImage(src);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
prevValue = currentValue;
|
|
185
|
+
prevSrcs = currentSrcs;
|
|
186
|
+
return [2];
|
|
187
|
+
});
|
|
188
|
+
}); });
|
|
127
189
|
} });
|
|
128
190
|
}
|
|
129
|
-
return
|
|
191
|
+
return base;
|
|
130
192
|
};
|
|
131
193
|
exports.config = config;
|
|
132
194
|
exports.default = ReactEditor;
|
package/package.json
CHANGED
package/src/Editor.tsx
CHANGED
|
@@ -18,73 +18,80 @@ const ReactEditor: React.FC<EditorProps> = ({ onChange, onBlur, value, config })
|
|
|
18
18
|
* Handles image upload + insertion in the editor
|
|
19
19
|
*/
|
|
20
20
|
export const uploaderConfig = (
|
|
21
|
-
apiUrl
|
|
22
|
-
imageUrl
|
|
21
|
+
apiUrl?: string,
|
|
22
|
+
imageUrl?: string
|
|
23
23
|
) => ({
|
|
24
24
|
imagesExtensions: ['jpg', 'png', 'jpeg', 'gif', 'webp'],
|
|
25
|
-
filesVariableName
|
|
25
|
+
filesVariableName(t: number): string {
|
|
26
26
|
return 'files[' + t + ']';
|
|
27
27
|
},
|
|
28
28
|
url: apiUrl,
|
|
29
29
|
withCredentials: false,
|
|
30
30
|
format: 'json',
|
|
31
31
|
method: 'POST',
|
|
32
|
-
prepareData
|
|
32
|
+
prepareData(formdata: FormData): FormData {
|
|
33
33
|
return formdata;
|
|
34
34
|
},
|
|
35
|
-
isSuccess
|
|
35
|
+
isSuccess(this: any, e: any): boolean {
|
|
36
36
|
const fn = this.jodit;
|
|
37
37
|
|
|
38
|
-
if (e
|
|
38
|
+
if (e?.data?.files && e.data.files.length) {
|
|
39
39
|
const tagName = 'img';
|
|
40
|
+
|
|
40
41
|
e.data.files.forEach((filename: string) => {
|
|
41
42
|
const elm = fn.createInside.element(tagName);
|
|
42
|
-
|
|
43
|
+
const src = imageUrl ? `${imageUrl}/${filename}` : filename;
|
|
44
|
+
elm.setAttribute('src', src);
|
|
43
45
|
fn.s.insertImage(elm as HTMLImageElement, null, fn.o.imageDefaultWidth);
|
|
44
46
|
});
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
return e
|
|
49
|
+
return !!e?.success;
|
|
48
50
|
},
|
|
49
|
-
getMessage
|
|
50
|
-
return e
|
|
51
|
+
getMessage(e: any): string {
|
|
52
|
+
return e?.data?.messages && Array.isArray(e.data.messages)
|
|
51
53
|
? e.data.messages.join('')
|
|
52
54
|
: '';
|
|
53
55
|
},
|
|
54
|
-
process
|
|
55
|
-
|
|
56
|
-
files.unshift(resp
|
|
56
|
+
process(resp: any): { files: any[]; error: string; msg: string } {
|
|
57
|
+
const files: any[] = [];
|
|
58
|
+
files.unshift(resp?.data);
|
|
59
|
+
|
|
57
60
|
return {
|
|
58
|
-
files: resp
|
|
59
|
-
error: resp
|
|
60
|
-
msg: resp
|
|
61
|
+
files: resp?.data,
|
|
62
|
+
error: resp?.msg,
|
|
63
|
+
msg: resp?.msg,
|
|
61
64
|
};
|
|
62
65
|
},
|
|
63
|
-
|
|
64
|
-
error: function (this: any, e: Error): void {
|
|
66
|
+
error(this: any, e: Error): void {
|
|
65
67
|
this.j.e.fire('errorMessage', e.message, 'error', 4000);
|
|
66
68
|
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
this.j.e.fire('errorMessage', e.message);
|
|
69
|
+
defaultHandlerError(this: any, e: any): void {
|
|
70
|
+
this.j.e.fire('errorMessage', e?.message || 'Upload error');
|
|
70
71
|
},
|
|
71
72
|
});
|
|
72
73
|
|
|
74
|
+
type ConfigParams = {
|
|
75
|
+
includeUploader?: boolean;
|
|
76
|
+
apiUrl?: string;
|
|
77
|
+
imageUrl?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Called when an image is really removed from the editor content.
|
|
80
|
+
* You receive the image src URL and can call your API to delete it on server.
|
|
81
|
+
*/
|
|
82
|
+
onDeleteImage?: (imageUrl: string) => void | Promise<void>;
|
|
83
|
+
};
|
|
84
|
+
|
|
73
85
|
/**
|
|
74
|
-
*
|
|
75
|
-
* - includeUploader: enable/disable upload integration
|
|
76
|
-
* - apiUrl: upload endpoint
|
|
77
|
-
* - imageUrl: base URL for stored images
|
|
78
|
-
* - onDeleteImage: callback called when an image is removed from the editor
|
|
79
|
-
* receives the image src URL so you can call your API to delete it on server
|
|
86
|
+
* Build Jodit config for ReactEditor
|
|
80
87
|
*/
|
|
81
|
-
export const config = (
|
|
82
|
-
includeUploader
|
|
83
|
-
apiUrl
|
|
84
|
-
imageUrl
|
|
85
|
-
onDeleteImage
|
|
86
|
-
) => {
|
|
87
|
-
const
|
|
88
|
+
export const config = ({
|
|
89
|
+
includeUploader,
|
|
90
|
+
apiUrl,
|
|
91
|
+
imageUrl,
|
|
92
|
+
onDeleteImage,
|
|
93
|
+
}: ConfigParams = {}) => {
|
|
94
|
+
const base: any = {
|
|
88
95
|
readonly: false,
|
|
89
96
|
placeholder: 'Start typing...',
|
|
90
97
|
toolbarAdaptive: false,
|
|
@@ -131,28 +138,55 @@ export const config = (
|
|
|
131
138
|
],
|
|
132
139
|
};
|
|
133
140
|
|
|
134
|
-
// Attach uploader config if requested
|
|
135
141
|
if (includeUploader) {
|
|
136
|
-
|
|
142
|
+
base.uploader = uploaderConfig(apiUrl, imageUrl);
|
|
137
143
|
}
|
|
138
144
|
|
|
139
|
-
// Handle delete image: when an image is removed in the editor,
|
|
140
|
-
// we call the provided callback with the image src URL.
|
|
141
145
|
if (onDeleteImage) {
|
|
142
|
-
|
|
143
|
-
...(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
base.events = {
|
|
147
|
+
...(base.events || {}),
|
|
148
|
+
/**
|
|
149
|
+
* We use the value diff to detect removed <img> src.
|
|
150
|
+
* This avoids false calls during upload (where DOM nodes can be replaced).
|
|
151
|
+
*/
|
|
152
|
+
afterInit(editor: any) {
|
|
153
|
+
const extractImageSrcs = (html: string): Set<string> => {
|
|
154
|
+
const container = document.createElement('div');
|
|
155
|
+
container.innerHTML = html || '';
|
|
156
|
+
const imgs = Array.from(container.getElementsByTagName('img')) as HTMLImageElement[];
|
|
157
|
+
|
|
158
|
+
return new Set(
|
|
159
|
+
imgs
|
|
160
|
+
.map((img) => img.getAttribute('src') || '')
|
|
161
|
+
.filter((src) => !!src)
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
let prevValue: string = editor.value || '';
|
|
166
|
+
let prevSrcs: Set<string> = extractImageSrcs(prevValue);
|
|
167
|
+
|
|
168
|
+
editor.events.on('change', async () => {
|
|
169
|
+
const currentValue: string = editor.value || '';
|
|
170
|
+
const currentSrcs = extractImageSrcs(currentValue);
|
|
171
|
+
|
|
172
|
+
// src present before, not present now -> deleted
|
|
173
|
+
prevSrcs.forEach((src) => {
|
|
174
|
+
if (!currentSrcs.has(src)) {
|
|
175
|
+
// If imageUrl is defined, you can filter to only your own assets
|
|
176
|
+
if (!imageUrl || src.startsWith(imageUrl)) {
|
|
177
|
+
void onDeleteImage(src);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
prevValue = currentValue;
|
|
183
|
+
prevSrcs = currentSrcs;
|
|
184
|
+
});
|
|
151
185
|
},
|
|
152
186
|
};
|
|
153
187
|
}
|
|
154
188
|
|
|
155
|
-
return
|
|
189
|
+
return base;
|
|
156
190
|
};
|
|
157
191
|
|
|
158
192
|
export default ReactEditor;
|
package/src/Editor.types.ts
CHANGED
package/types/Editor.d.ts
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { EditorProps } from './Editor.types';
|
|
3
3
|
declare const ReactEditor: React.FC<EditorProps>;
|
|
4
|
-
export declare const uploaderConfig: (apiUrl
|
|
4
|
+
export declare const uploaderConfig: (apiUrl?: string, imageUrl?: string) => {
|
|
5
5
|
imagesExtensions: string[];
|
|
6
|
-
filesVariableName
|
|
6
|
+
filesVariableName(t: number): string;
|
|
7
7
|
url: string | undefined;
|
|
8
8
|
withCredentials: boolean;
|
|
9
9
|
format: string;
|
|
10
10
|
method: string;
|
|
11
|
-
prepareData
|
|
12
|
-
isSuccess
|
|
13
|
-
getMessage
|
|
14
|
-
process
|
|
11
|
+
prepareData(formdata: FormData): FormData;
|
|
12
|
+
isSuccess(this: any, e: any): boolean;
|
|
13
|
+
getMessage(e: any): string;
|
|
14
|
+
process(resp: any): {
|
|
15
15
|
files: any[];
|
|
16
16
|
error: string;
|
|
17
17
|
msg: string;
|
|
18
18
|
};
|
|
19
|
-
error
|
|
20
|
-
defaultHandlerError
|
|
19
|
+
error(this: any, e: Error): void;
|
|
20
|
+
defaultHandlerError(this: any, e: any): void;
|
|
21
21
|
};
|
|
22
|
-
|
|
22
|
+
type ConfigParams = {
|
|
23
|
+
includeUploader?: boolean;
|
|
24
|
+
apiUrl?: string;
|
|
25
|
+
imageUrl?: string;
|
|
26
|
+
onDeleteImage?: (imageUrl: string) => void | Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
export declare const config: ({ includeUploader, apiUrl, imageUrl, onDeleteImage, }?: ConfigParams) => any;
|
|
23
29
|
export default ReactEditor;
|
package/types/Editor.types.d.ts
CHANGED