@mbs-dev/react-editor 1.5.0 → 1.7.0
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 +48 -1
- package/package.json +1 -1
- package/src/Editor.tsx +60 -17
package/dist/Editor.js
CHANGED
|
@@ -94,6 +94,7 @@ var uploaderConfig = function (apiUrl, imageUrl) { return ({
|
|
|
94
94
|
var fn = this.jodit;
|
|
95
95
|
if (((_a = e === null || e === void 0 ? void 0 : e.data) === null || _a === void 0 ? void 0 : _a.files) && e.data.files.length) {
|
|
96
96
|
e.data.files.forEach(function (filename) {
|
|
97
|
+
var _a, _b, _c, _d;
|
|
97
98
|
var src = imageUrl ? "".concat(imageUrl, "/").concat(filename) : filename;
|
|
98
99
|
if (isImageByExtension(filename, _this.imagesExtensions || ['jpg', 'png', 'jpeg', 'gif', 'webp'])) {
|
|
99
100
|
var tagName = 'img';
|
|
@@ -102,6 +103,23 @@ var uploaderConfig = function (apiUrl, imageUrl) { return ({
|
|
|
102
103
|
fn.s.insertImage(elm, null, fn.o.imageDefaultWidth);
|
|
103
104
|
}
|
|
104
105
|
else {
|
|
106
|
+
if ((_a = fn === null || fn === void 0 ? void 0 : fn.s) === null || _a === void 0 ? void 0 : _a.focus)
|
|
107
|
+
fn.s.focus();
|
|
108
|
+
var savedRange = fn && fn.__mbs_lastRange;
|
|
109
|
+
if (savedRange && ((_b = fn === null || fn === void 0 ? void 0 : fn.s) === null || _b === void 0 ? void 0 : _b.selectRange)) {
|
|
110
|
+
try {
|
|
111
|
+
fn.s.selectRange(savedRange, true);
|
|
112
|
+
}
|
|
113
|
+
catch (_e) {
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else if ((_c = fn === null || fn === void 0 ? void 0 : fn.s) === null || _c === void 0 ? void 0 : _c.restore) {
|
|
117
|
+
try {
|
|
118
|
+
fn.s.restore();
|
|
119
|
+
}
|
|
120
|
+
catch (_f) {
|
|
121
|
+
}
|
|
122
|
+
}
|
|
105
123
|
var tagName = 'a';
|
|
106
124
|
var elm = fn.createInside.element(tagName);
|
|
107
125
|
elm.setAttribute('href', src);
|
|
@@ -109,6 +127,13 @@ var uploaderConfig = function (apiUrl, imageUrl) { return ({
|
|
|
109
127
|
elm.setAttribute('rel', 'noopener noreferrer');
|
|
110
128
|
elm.textContent = getDisplayNameFromPath(filename);
|
|
111
129
|
fn.s.insertNode(elm);
|
|
130
|
+
if ((_d = fn === null || fn === void 0 ? void 0 : fn.s) === null || _d === void 0 ? void 0 : _d.setCursorAfter) {
|
|
131
|
+
try {
|
|
132
|
+
fn.s.setCursorAfter(elm);
|
|
133
|
+
}
|
|
134
|
+
catch (_g) {
|
|
135
|
+
}
|
|
136
|
+
}
|
|
112
137
|
}
|
|
113
138
|
});
|
|
114
139
|
}
|
|
@@ -138,7 +163,8 @@ var uploaderConfig = function (apiUrl, imageUrl) { return ({
|
|
|
138
163
|
}); };
|
|
139
164
|
exports.uploaderConfig = uploaderConfig;
|
|
140
165
|
var config = function (_a) {
|
|
141
|
-
var _b
|
|
166
|
+
var _b;
|
|
167
|
+
var _c = _a === void 0 ? {} : _a, includeUploader = _c.includeUploader, apiUrl = _c.apiUrl, imageUrl = _c.imageUrl, onDeleteImage = _c.onDeleteImage;
|
|
142
168
|
var base = {
|
|
143
169
|
readonly: false,
|
|
144
170
|
placeholder: 'Start typing...',
|
|
@@ -188,9 +214,30 @@ var config = function (_a) {
|
|
|
188
214
|
if (includeUploader) {
|
|
189
215
|
base.uploader = (0, exports.uploaderConfig)(apiUrl, imageUrl);
|
|
190
216
|
}
|
|
217
|
+
base.events = __assign(__assign({}, (base.events || {})), { afterInit: function (editor) {
|
|
218
|
+
var saveRange = function () {
|
|
219
|
+
var _a;
|
|
220
|
+
try {
|
|
221
|
+
if ((_a = editor === null || editor === void 0 ? void 0 : editor.s) === null || _a === void 0 ? void 0 : _a.range) {
|
|
222
|
+
editor.__mbs_lastRange = editor.s.range.cloneRange();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (_b) {
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
saveRange();
|
|
229
|
+
editor.events.on('mouseup', saveRange);
|
|
230
|
+
editor.events.on('keyup', saveRange);
|
|
231
|
+
editor.events.on('focus', saveRange);
|
|
232
|
+
editor.events.on('change', saveRange);
|
|
233
|
+
} });
|
|
191
234
|
if (onDeleteImage) {
|
|
235
|
+
var prevAfterInit_1 = (_b = base.events) === null || _b === void 0 ? void 0 : _b.afterInit;
|
|
192
236
|
base.events = __assign(__assign({}, (base.events || {})), { afterInit: function (editor) {
|
|
193
237
|
var _this = this;
|
|
238
|
+
if (typeof prevAfterInit_1 === 'function') {
|
|
239
|
+
prevAfterInit_1(editor);
|
|
240
|
+
}
|
|
194
241
|
var extractImageSrcs = function (html) {
|
|
195
242
|
var container = document.createElement('div');
|
|
196
243
|
container.innerHTML = html || '';
|
package/package.json
CHANGED
package/src/Editor.tsx
CHANGED
|
@@ -19,18 +19,16 @@ const isImageByExtension = (filename: string, imageExts: string[]): boolean => {
|
|
|
19
19
|
return !!ext && imageExts.includes(ext);
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
//
|
|
22
|
+
// display name without extension and without last "-..."
|
|
23
23
|
// Example: recu-202600004-2-69956651a3b98099024323.pdf -> recu-202600004-2
|
|
24
24
|
const getDisplayNameFromPath = (filename: string): string => {
|
|
25
25
|
const clean = (filename || '').split('?')[0]?.split('#')[0] ?? '';
|
|
26
26
|
const last = clean.split('/').pop();
|
|
27
27
|
const base = last ? decodeURIComponent(last) : filename;
|
|
28
28
|
|
|
29
|
-
// remove extension
|
|
30
29
|
const dotIndex = base.lastIndexOf('.');
|
|
31
30
|
const noExt = dotIndex > 0 ? base.slice(0, dotIndex) : base;
|
|
32
31
|
|
|
33
|
-
// remove last "-..." suffix
|
|
34
32
|
const dashIndex = noExt.lastIndexOf('-');
|
|
35
33
|
if (dashIndex > 0) {
|
|
36
34
|
return noExt.slice(0, dashIndex);
|
|
@@ -43,10 +41,7 @@ const getDisplayNameFromPath = (filename: string): string => {
|
|
|
43
41
|
* Uploader configuration for Jodit
|
|
44
42
|
* Handles image upload + insertion in the editor
|
|
45
43
|
*/
|
|
46
|
-
export const uploaderConfig = (
|
|
47
|
-
apiUrl?: string,
|
|
48
|
-
imageUrl?: string
|
|
49
|
-
) => ({
|
|
44
|
+
export const uploaderConfig = (apiUrl?: string, imageUrl?: string) => ({
|
|
50
45
|
imagesExtensions: ['jpg', 'png', 'jpeg', 'gif', 'webp'],
|
|
51
46
|
filesVariableName(t: number): string {
|
|
52
47
|
return 'files[' + t + ']';
|
|
@@ -65,20 +60,47 @@ export const uploaderConfig = (
|
|
|
65
60
|
e.data.files.forEach((filename: string) => {
|
|
66
61
|
const src = imageUrl ? `${imageUrl}/${filename}` : filename;
|
|
67
62
|
|
|
68
|
-
//
|
|
63
|
+
// If it's an image => insert <img>, otherwise insert <a href="...">
|
|
69
64
|
if (isImageByExtension(filename, this.imagesExtensions || ['jpg', 'png', 'jpeg', 'gif', 'webp'])) {
|
|
70
65
|
const tagName = 'img';
|
|
71
66
|
const elm = fn.createInside.element(tagName);
|
|
72
67
|
elm.setAttribute('src', src);
|
|
73
68
|
fn.s.insertImage(elm as HTMLImageElement, null, fn.o.imageDefaultWidth);
|
|
74
69
|
} else {
|
|
70
|
+
// ✅ FIX: restore the last known caret/selection inside the editor (works in table cells)
|
|
71
|
+
if (fn?.s?.focus) fn.s.focus();
|
|
72
|
+
|
|
73
|
+
const savedRange = fn && (fn as any).__mbs_lastRange;
|
|
74
|
+
if (savedRange && fn?.s?.selectRange) {
|
|
75
|
+
try {
|
|
76
|
+
fn.s.selectRange(savedRange, true);
|
|
77
|
+
} catch {
|
|
78
|
+
// ignore
|
|
79
|
+
}
|
|
80
|
+
} else if (fn?.s?.restore) {
|
|
81
|
+
try {
|
|
82
|
+
fn.s.restore();
|
|
83
|
+
} catch {
|
|
84
|
+
// ignore
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
const tagName = 'a';
|
|
76
89
|
const elm = fn.createInside.element(tagName);
|
|
77
90
|
elm.setAttribute('href', src);
|
|
78
91
|
elm.setAttribute('target', '_blank');
|
|
79
92
|
elm.setAttribute('rel', 'noopener noreferrer');
|
|
80
93
|
elm.textContent = getDisplayNameFromPath(filename);
|
|
94
|
+
|
|
81
95
|
fn.s.insertNode(elm);
|
|
96
|
+
|
|
97
|
+
if (fn?.s?.setCursorAfter) {
|
|
98
|
+
try {
|
|
99
|
+
fn.s.setCursorAfter(elm);
|
|
100
|
+
} catch {
|
|
101
|
+
// ignore
|
|
102
|
+
}
|
|
103
|
+
}
|
|
82
104
|
}
|
|
83
105
|
});
|
|
84
106
|
}
|
|
@@ -119,9 +141,6 @@ type ConfigParams = {
|
|
|
119
141
|
onDeleteImage?: (imageUrl: string) => void | Promise<void>;
|
|
120
142
|
};
|
|
121
143
|
|
|
122
|
-
/**
|
|
123
|
-
* Build Jodit config for ReactEditor
|
|
124
|
-
*/
|
|
125
144
|
export const config = ({
|
|
126
145
|
includeUploader,
|
|
127
146
|
apiUrl,
|
|
@@ -180,14 +199,40 @@ export const config = ({
|
|
|
180
199
|
base.uploader = uploaderConfig(apiUrl, imageUrl);
|
|
181
200
|
}
|
|
182
201
|
|
|
202
|
+
// ✅ Always keep last caret/selection (needed for file uploads to insert in the right cell)
|
|
203
|
+
base.events = {
|
|
204
|
+
...(base.events || {}),
|
|
205
|
+
afterInit(editor: any) {
|
|
206
|
+
const saveRange = () => {
|
|
207
|
+
try {
|
|
208
|
+
if (editor?.s?.range) {
|
|
209
|
+
(editor as any).__mbs_lastRange = editor.s.range.cloneRange();
|
|
210
|
+
}
|
|
211
|
+
} catch {
|
|
212
|
+
// ignore
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// initial + common interactions
|
|
217
|
+
saveRange();
|
|
218
|
+
editor.events.on('mouseup', saveRange);
|
|
219
|
+
editor.events.on('keyup', saveRange);
|
|
220
|
+
editor.events.on('focus', saveRange);
|
|
221
|
+
editor.events.on('change', saveRange);
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
|
|
183
225
|
if (onDeleteImage) {
|
|
226
|
+
const prevAfterInit = base.events?.afterInit;
|
|
227
|
+
|
|
184
228
|
base.events = {
|
|
185
229
|
...(base.events || {}),
|
|
186
|
-
/**
|
|
187
|
-
* We use the value diff to detect removed <img> src.
|
|
188
|
-
* This avoids false calls during upload (where DOM nodes can be replaced).
|
|
189
|
-
*/
|
|
190
230
|
afterInit(editor: any) {
|
|
231
|
+
// keep existing selection saver
|
|
232
|
+
if (typeof prevAfterInit === 'function') {
|
|
233
|
+
prevAfterInit(editor);
|
|
234
|
+
}
|
|
235
|
+
|
|
191
236
|
const extractImageSrcs = (html: string): Set<string> => {
|
|
192
237
|
const container = document.createElement('div');
|
|
193
238
|
container.innerHTML = html || '';
|
|
@@ -207,10 +252,8 @@ export const config = ({
|
|
|
207
252
|
const currentValue: string = editor.value || '';
|
|
208
253
|
const currentSrcs = extractImageSrcs(currentValue);
|
|
209
254
|
|
|
210
|
-
// src present before, not present now -> deleted
|
|
211
255
|
prevSrcs.forEach((src) => {
|
|
212
256
|
if (!currentSrcs.has(src)) {
|
|
213
|
-
// If imageUrl is defined, you can filter to only your own assets
|
|
214
257
|
if (!imageUrl || src.startsWith(imageUrl)) {
|
|
215
258
|
void onDeleteImage(src);
|
|
216
259
|
}
|