@mbs-dev/react-editor 1.0.6 → 1.0.7

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 CHANGED
@@ -1,14 +1,28 @@
1
1
  "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
2
+ var __read = (this && this.__read) || function (o, n) {
3
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
4
+ if (!m) return o;
5
+ var i = m.call(o), r, ar = [], e;
6
+ try {
7
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
8
+ }
9
+ catch (error) { e = { error: error }; }
10
+ finally {
11
+ try {
12
+ if (r && !r.done && (m = i["return"])) m.call(i);
8
13
  }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
14
+ finally { if (e) throw e.error; }
15
+ }
16
+ return ar;
17
+ };
18
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
19
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
20
+ if (ar || !(i in from)) {
21
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
22
+ ar[i] = from[i];
23
+ }
24
+ }
25
+ return to.concat(ar || Array.prototype.slice.call(from));
12
26
  };
13
27
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
28
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -34,9 +48,8 @@ var uploaderConfig = function (apiUrl, imageUrl) { return ({
34
48
  return formdata;
35
49
  },
36
50
  isSuccess: function (e) {
37
- var _a;
38
51
  var fn = this.jodit;
39
- if (((_a = e.data) === null || _a === void 0 ? void 0 : _a.files) && e.data.files.length) {
52
+ if (e.data.files && e.data.files.length) {
40
53
  var tagName_1 = 'img';
41
54
  e.data.files.forEach(function (filename) {
42
55
  var elm = fn.createInside.element(tagName_1);
@@ -47,8 +60,7 @@ var uploaderConfig = function (apiUrl, imageUrl) { return ({
47
60
  return e.success;
48
61
  },
49
62
  getMessage: function (e) {
50
- var _a;
51
- return ((_a = e.data) === null || _a === void 0 ? void 0 : _a.messages) && Array.isArray(e.data.messages)
63
+ return e.data.messages && Array.isArray(e.data.messages)
52
64
  ? e.data.messages.join('')
53
65
  : '';
54
66
  },
@@ -69,8 +81,9 @@ var uploaderConfig = function (apiUrl, imageUrl) { return ({
69
81
  },
70
82
  }); };
71
83
  exports.uploaderConfig = uploaderConfig;
72
- var config = function (includeUploader, apiUrl, imageUrl, onDeleteImage) {
73
- var baseConfig = {
84
+ var config = function (_a) {
85
+ var _b = _a === void 0 ? {} : _a, includeUploader = _b.includeUploader, apiUrl = _b.apiUrl, imageUrl = _b.imageUrl, onDeleteImage = _b.onDeleteImage;
86
+ return ({
74
87
  readonly: false,
75
88
  placeholder: 'Start typing...',
76
89
  toolbarAdaptive: false,
@@ -114,19 +127,40 @@ var config = function (includeUploader, apiUrl, imageUrl, onDeleteImage) {
114
127
  'table',
115
128
  'fullsize',
116
129
  ],
117
- };
118
- if (includeUploader) {
119
- baseConfig.uploader = (0, exports.uploaderConfig)(apiUrl, imageUrl);
120
- }
121
- if (onDeleteImage) {
122
- baseConfig.events = __assign(__assign({}, (baseConfig.events || {})), { afterRemoveImage: function (image) {
123
- var src = image.getAttribute('src');
124
- if (src) {
125
- onDeleteImage(src);
126
- }
127
- } });
128
- }
129
- return baseConfig;
130
+ uploader: includeUploader ? (0, exports.uploaderConfig)(apiUrl, imageUrl) : undefined,
131
+ events: onDeleteImage
132
+ ? {
133
+ afterInit: function (editor) {
134
+ var root = editor.editor;
135
+ var observer = new MutationObserver(function (mutations) {
136
+ mutations.forEach(function (mutation) {
137
+ mutation.removedNodes.forEach(function (node) {
138
+ if (node.nodeType !== Node.ELEMENT_NODE)
139
+ return;
140
+ var el = node;
141
+ var imgs = __spreadArray(__spreadArray([], __read((el.matches('img') ? [el] : [])), false), __read(Array.from(el.querySelectorAll('img'))), false);
142
+ imgs.forEach(function (img) {
143
+ var src = img.getAttribute('src');
144
+ if (!src)
145
+ return;
146
+ if (imageUrl && !src.startsWith(imageUrl))
147
+ return;
148
+ onDeleteImage(src);
149
+ });
150
+ });
151
+ });
152
+ });
153
+ observer.observe(root, {
154
+ childList: true,
155
+ subtree: true,
156
+ });
157
+ editor.events.on('beforeDestruct', function () {
158
+ observer.disconnect();
159
+ });
160
+ },
161
+ }
162
+ : undefined,
163
+ });
130
164
  };
131
165
  exports.config = config;
132
166
  exports.default = ReactEditor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mbs-dev/react-editor",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "react editor",
5
5
  "main": "dist/index.js",
6
6
  "types": "types/index.d.ts",
package/src/Editor.tsx CHANGED
@@ -1,6 +1,7 @@
1
+ // Editor.tsx
1
2
  import React from 'react';
2
3
  import JoditEditor from 'jodit-react';
3
- import { EditorProps } from './Editor.types';
4
+ import { EditorProps, OnDeleteImage } from './Editor.types';
4
5
 
5
6
  const ReactEditor: React.FC<EditorProps> = ({ onChange, onBlur, value, config }) => {
6
7
  return (
@@ -13,30 +14,24 @@ const ReactEditor: React.FC<EditorProps> = ({ onChange, onBlur, value, config })
13
14
  );
14
15
  };
15
16
 
16
- /**
17
- * Uploader configuration for Jodit
18
- * Handles image upload + insertion in the editor
19
- */
20
- export const uploaderConfig = (
21
- apiUrl: string | undefined,
22
- imageUrl: string | undefined
23
- ) => ({
17
+ export const uploaderConfig = (apiUrl?: string, imageUrl?: string) => ({
24
18
  imagesExtensions: ['jpg', 'png', 'jpeg', 'gif', 'webp'],
25
- filesVariableName: function (t: number): string {
19
+ filesVariableName(t: number): string {
26
20
  return 'files[' + t + ']';
27
21
  },
28
22
  url: apiUrl,
29
23
  withCredentials: false,
30
24
  format: 'json',
31
25
  method: 'POST',
32
- prepareData: function (formdata: FormData): FormData {
26
+ prepareData(formdata: FormData): FormData {
33
27
  return formdata;
34
28
  },
35
- isSuccess: function (this: any, e: any): boolean {
29
+ isSuccess(this: any, e: any): boolean {
36
30
  const fn = this.jodit;
37
31
 
38
- if (e.data?.files && e.data.files.length) {
32
+ if (e.data.files && e.data.files.length) {
39
33
  const tagName = 'img';
34
+
40
35
  e.data.files.forEach((filename: string) => {
41
36
  const elm = fn.createInside.element(tagName);
42
37
  elm.setAttribute('src', `${imageUrl}/${filename}`);
@@ -46,113 +41,132 @@ export const uploaderConfig = (
46
41
 
47
42
  return e.success;
48
43
  },
49
- getMessage: function (e: any): string {
50
- return e.data?.messages && Array.isArray(e.data.messages)
44
+ getMessage(e: any): string {
45
+ return e.data.messages && Array.isArray(e.data.messages)
51
46
  ? e.data.messages.join('')
52
47
  : '';
53
48
  },
54
- process: function (resp: any): { files: any[]; error: string; msg: string } {
55
- let files: any[] = [];
49
+ process(resp: any): { files: any[]; error: string; msg: string } {
50
+ const files: any[] = [];
56
51
  files.unshift(resp.data);
52
+
57
53
  return {
58
54
  files: resp.data,
59
55
  error: resp.msg,
60
56
  msg: resp.msg,
61
57
  };
62
58
  },
63
-
64
- error: function (this: any, e: Error): void {
59
+ error(this: any, e: Error): void {
65
60
  this.j.e.fire('errorMessage', e.message, 'error', 4000);
66
61
  },
67
-
68
- defaultHandlerError: function (this: any, e: any): void {
62
+ defaultHandlerError(this: any, e: any): void {
69
63
  this.j.e.fire('errorMessage', e.message);
70
64
  },
71
65
  });
72
66
 
73
- /**
74
- * Global editor config
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
80
- */
81
- export const config = (
82
- includeUploader?: boolean,
83
- apiUrl?: string,
84
- imageUrl?: string,
85
- onDeleteImage?: (imageUrl: string) => void
86
- ) => {
87
- const baseConfig: any = {
88
- readonly: false,
89
- placeholder: 'Start typing...',
90
- toolbarAdaptive: false,
91
- useSearch: false,
92
- language: 'en',
93
- allowResizeX: false,
94
- allowResizeY: false,
95
- height: 400,
96
- enableDragAndDropFileToEditor: true,
97
- showCharsCounter: true,
98
- showWordsCounter: true,
99
- showXPathInStatusbar: false,
100
-
101
- buttons: [
102
- 'source',
103
- '|',
104
- 'bold',
105
- 'italic',
106
- 'underline',
107
- '|',
108
- 'ul',
109
- 'ol',
110
- '|',
111
- 'image',
112
- '|',
113
- 'video',
114
- '|',
115
- 'link',
116
- '|',
117
- 'undo',
118
- 'redo',
119
- '|',
120
- 'hr',
121
- '|',
122
- 'eraser',
123
- '|',
124
- 'font',
125
- 'fontsize',
126
- 'paragraph',
127
- 'brush',
128
- '|',
129
- 'table',
130
- 'fullsize',
131
- ],
132
- };
133
-
134
- // Attach uploader config if requested
135
- if (includeUploader) {
136
- baseConfig.uploader = uploaderConfig(apiUrl, imageUrl);
137
- }
138
-
139
- // Handle delete image: when an image is removed in the editor,
140
- // we call the provided callback with the image src URL.
141
- if (onDeleteImage) {
142
- baseConfig.events = {
143
- ...(baseConfig.events || {}),
144
- // Depending on Jodit version, this event name may vary; this
145
- // handler is intended to be called when an image is removed.
146
- afterRemoveImage: function (this: any, image: HTMLImageElement) {
147
- const src = image.getAttribute('src');
148
- if (src) {
149
- onDeleteImage(src);
150
- }
151
- },
152
- };
153
- }
154
-
155
- return baseConfig;
67
+ type ConfigParams = {
68
+ includeUploader?: boolean;
69
+ apiUrl?: string;
70
+ imageUrl?: string;
71
+ onDeleteImage?: OnDeleteImage;
156
72
  };
157
73
 
74
+ export const config = ({
75
+ includeUploader,
76
+ apiUrl,
77
+ imageUrl,
78
+ onDeleteImage,
79
+ }: ConfigParams = {}) => ({
80
+ readonly: false,
81
+ placeholder: 'Start typing...',
82
+ toolbarAdaptive: false,
83
+ useSearch: false,
84
+ language: 'en',
85
+ allowResizeX: false,
86
+ allowResizeY: false,
87
+ height: 400,
88
+ enableDragAndDropFileToEditor: true,
89
+ showCharsCounter: true,
90
+ showWordsCounter: true,
91
+ showXPathInStatusbar: false,
92
+
93
+ buttons: [
94
+ 'source',
95
+ '|',
96
+ 'bold',
97
+ 'italic',
98
+ 'underline',
99
+ '|',
100
+ 'ul',
101
+ 'ol',
102
+ '|',
103
+ 'image',
104
+ '|',
105
+ 'video',
106
+ '|',
107
+ 'link',
108
+ '|',
109
+ 'undo',
110
+ 'redo',
111
+ '|',
112
+ 'hr',
113
+ '|',
114
+ 'eraser',
115
+ '|',
116
+ 'font',
117
+ 'fontsize',
118
+ 'paragraph',
119
+ 'brush',
120
+ '|',
121
+ 'table',
122
+ 'fullsize',
123
+ ],
124
+
125
+ uploader: includeUploader ? uploaderConfig(apiUrl, imageUrl) : undefined,
126
+
127
+ // 👇 This part ensures delete callback runs when image is removed
128
+ events: onDeleteImage
129
+ ? {
130
+ afterInit(editor: any) {
131
+ const root: HTMLElement = editor.editor;
132
+
133
+ const observer = new MutationObserver((mutations) => {
134
+ mutations.forEach((mutation) => {
135
+ mutation.removedNodes.forEach((node) => {
136
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
137
+
138
+ const el = node as HTMLElement;
139
+
140
+ const imgs: HTMLImageElement[] = [
141
+ ...(el.matches('img') ? [el as HTMLImageElement] : []),
142
+ ...Array.from(el.querySelectorAll('img')),
143
+ ];
144
+
145
+ imgs.forEach((img) => {
146
+ const src = img.getAttribute('src');
147
+ if (!src) return;
148
+
149
+ // Optionally restrict to your image base URL
150
+ if (imageUrl && !src.startsWith(imageUrl)) return;
151
+
152
+ onDeleteImage(src);
153
+ });
154
+ });
155
+ });
156
+ });
157
+
158
+ observer.observe(root, {
159
+ childList: true,
160
+ subtree: true,
161
+ });
162
+
163
+ // Cleanup when editor is destroyed
164
+ editor.events.on('beforeDestruct', () => {
165
+ observer.disconnect();
166
+ });
167
+ },
168
+ }
169
+ : undefined,
170
+ });
171
+
158
172
  export default ReactEditor;
@@ -7,4 +7,6 @@ export interface EditorProps {
7
7
  imageUrl?: string;
8
8
  config?: any
9
9
  // ref?: any;
10
- }
10
+ }
11
+
12
+ export type OnDeleteImage = (src: string) => void | Promise<void>;
package/types/Editor.d.ts CHANGED
@@ -1,23 +1,64 @@
1
1
  import React from 'react';
2
- import { EditorProps } from './Editor.types';
2
+ import { EditorProps, OnDeleteImage } from './Editor.types';
3
3
  declare const ReactEditor: React.FC<EditorProps>;
4
- export declare const uploaderConfig: (apiUrl: string | undefined, imageUrl: string | undefined) => {
4
+ export declare const uploaderConfig: (apiUrl?: string, imageUrl?: string) => {
5
5
  imagesExtensions: string[];
6
- filesVariableName: (t: number) => string;
6
+ filesVariableName(t: number): string;
7
7
  url: string | undefined;
8
8
  withCredentials: boolean;
9
9
  format: string;
10
10
  method: string;
11
- prepareData: (formdata: FormData) => FormData;
12
- isSuccess: (this: any, e: any) => boolean;
13
- getMessage: (e: any) => string;
14
- process: (resp: any) => {
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: (this: any, e: Error) => void;
20
- defaultHandlerError: (this: any, e: any) => void;
19
+ error(this: any, e: Error): void;
20
+ defaultHandlerError(this: any, e: any): void;
21
+ };
22
+ type ConfigParams = {
23
+ includeUploader?: boolean;
24
+ apiUrl?: string;
25
+ imageUrl?: string;
26
+ onDeleteImage?: OnDeleteImage;
27
+ };
28
+ export declare const config: ({ includeUploader, apiUrl, imageUrl, onDeleteImage, }?: ConfigParams) => {
29
+ readonly: boolean;
30
+ placeholder: string;
31
+ toolbarAdaptive: boolean;
32
+ useSearch: boolean;
33
+ language: string;
34
+ allowResizeX: boolean;
35
+ allowResizeY: boolean;
36
+ height: number;
37
+ enableDragAndDropFileToEditor: boolean;
38
+ showCharsCounter: boolean;
39
+ showWordsCounter: boolean;
40
+ showXPathInStatusbar: boolean;
41
+ buttons: string[];
42
+ uploader: {
43
+ imagesExtensions: string[];
44
+ filesVariableName(t: number): string;
45
+ url: string | undefined;
46
+ withCredentials: boolean;
47
+ format: string;
48
+ method: string;
49
+ prepareData(formdata: FormData): FormData;
50
+ isSuccess(this: any, e: any): boolean;
51
+ getMessage(e: any): string;
52
+ process(resp: any): {
53
+ files: any[];
54
+ error: string;
55
+ msg: string;
56
+ };
57
+ error(this: any, e: Error): void;
58
+ defaultHandlerError(this: any, e: any): void;
59
+ } | undefined;
60
+ events: {
61
+ afterInit(editor: any): void;
62
+ } | undefined;
21
63
  };
22
- export declare const config: (includeUploader?: boolean, apiUrl?: string, imageUrl?: string, onDeleteImage?: ((imageUrl: string) => void) | undefined) => any;
23
64
  export default ReactEditor;
@@ -7,3 +7,4 @@ export interface EditorProps {
7
7
  imageUrl?: string;
8
8
  config?: any;
9
9
  }
10
+ export type OnDeleteImage = (src: string) => void | Promise<void>;