rich-html-editor 0.2.1
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/LICENSE +21 -0
- package/README.md +263 -0
- package/dist/chunk-ZDGUOGND.mjs +441 -0
- package/dist/chunk-ZDGUOGND.mjs.map +1 -0
- package/dist/index.d.mts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +1896 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1439 -0
- package/dist/index.mjs.map +1 -0
- package/dist/state-CZIMHTJ3.mjs +25 -0
- package/dist/state-CZIMHTJ3.mjs.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
// src/core/events.ts
|
|
2
|
+
var EditorEventEmitter = class {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
5
|
+
}
|
|
6
|
+
on(type, handler) {
|
|
7
|
+
if (!this.listeners.has(type)) {
|
|
8
|
+
this.listeners.set(type, /* @__PURE__ */ new Set());
|
|
9
|
+
}
|
|
10
|
+
this.listeners.get(type).add(handler);
|
|
11
|
+
return () => this.off(type, handler);
|
|
12
|
+
}
|
|
13
|
+
once(type, handler) {
|
|
14
|
+
const unsubscribe = this.on(type, (event) => {
|
|
15
|
+
handler(event);
|
|
16
|
+
unsubscribe();
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
off(type, handler) {
|
|
20
|
+
const handlers = this.listeners.get(type);
|
|
21
|
+
if (handlers) {
|
|
22
|
+
handlers.delete(handler);
|
|
23
|
+
if (handlers.size === 0) this.listeners.delete(type);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
emit(event) {
|
|
27
|
+
const handlers = this.listeners.get(event.type);
|
|
28
|
+
if (handlers) {
|
|
29
|
+
handlers.forEach((handler) => {
|
|
30
|
+
try {
|
|
31
|
+
handler(event);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(
|
|
34
|
+
`[rich-html-editor] Error in event handler for ${event.type}:`,
|
|
35
|
+
error
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
removeAllListeners(type) {
|
|
42
|
+
if (type) this.listeners.delete(type);
|
|
43
|
+
else this.listeners.clear();
|
|
44
|
+
}
|
|
45
|
+
listenerCount(type) {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
return (_b = (_a = this.listeners.get(type)) == null ? void 0 : _a.size) != null ? _b : 0;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var editorEventEmitter = new EditorEventEmitter();
|
|
51
|
+
function getEditorEventEmitter() {
|
|
52
|
+
return editorEventEmitter;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/core/constants.ts
|
|
56
|
+
var TOOLBAR_ID = "editor-toolbar";
|
|
57
|
+
var STYLE_ID = "editor-styles";
|
|
58
|
+
var CLASS_EDITABLE = "editor-editable-element";
|
|
59
|
+
var CLASS_ACTIVE = "editor-active-element";
|
|
60
|
+
var DEFAULT_MAX_STACK = 60;
|
|
61
|
+
var TOOLBAR_BG = "#f8fafc";
|
|
62
|
+
var TOOLBAR_BORDER = "#e5e7eb";
|
|
63
|
+
var BUTTON_BORDER = "#d1d5db";
|
|
64
|
+
var BUTTON_ACTIVE_BG = "#e0e7ff";
|
|
65
|
+
var BUTTON_BG = "#fff";
|
|
66
|
+
var BUTTON_COLOR = "#222";
|
|
67
|
+
var INFO_COLOR = "#888";
|
|
68
|
+
var HOVER_OUTLINE = "#2563eb";
|
|
69
|
+
var ACTIVE_OUTLINE = "#16a34a";
|
|
70
|
+
var LABEL_BOLD = "<b>B</b>";
|
|
71
|
+
var LABEL_ITALIC = "<i>I</i>";
|
|
72
|
+
var LABEL_UNDERLINE = "<u>U</u>";
|
|
73
|
+
var LABEL_STRIKETHROUGH = "<s>S</s>";
|
|
74
|
+
var LABEL_UNDO = "\u21BA";
|
|
75
|
+
var LABEL_REDO = "\u21BB";
|
|
76
|
+
var LABEL_LINK = "\u{1F517}";
|
|
77
|
+
var LABEL_ALIGN_LEFT = `
|
|
78
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
79
|
+
<rect x="1" y="2" width="10" height="2" rx="0.5" fill="currentColor" />
|
|
80
|
+
<rect x="1" y="6" width="14" height="2" rx="0.5" fill="currentColor" />
|
|
81
|
+
<rect x="1" y="10" width="10" height="2" rx="0.5" fill="currentColor" />
|
|
82
|
+
<rect x="1" y="14" width="14" height="2" rx="0.5" fill="currentColor" />
|
|
83
|
+
</svg>
|
|
84
|
+
`;
|
|
85
|
+
var LABEL_ALIGN_CENTER = `
|
|
86
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
87
|
+
<rect x="3" y="2" width="10" height="2" rx="0.5" fill="currentColor" />
|
|
88
|
+
<rect x="1" y="6" width="14" height="2" rx="0.5" fill="currentColor" />
|
|
89
|
+
<rect x="3" y="10" width="10" height="2" rx="0.5" fill="currentColor" />
|
|
90
|
+
<rect x="1" y="14" width="14" height="2" rx="0.5" fill="currentColor" />
|
|
91
|
+
</svg>
|
|
92
|
+
`;
|
|
93
|
+
var LABEL_ALIGN_RIGHT = `
|
|
94
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
95
|
+
<rect x="5" y="2" width="10" height="2" rx="0.5" fill="currentColor" />
|
|
96
|
+
<rect x="1" y="6" width="14" height="2" rx="0.5" fill="currentColor" />
|
|
97
|
+
<rect x="5" y="10" width="10" height="2" rx="0.5" fill="currentColor" />
|
|
98
|
+
<rect x="1" y="14" width="14" height="2" rx="0.5" fill="currentColor" />
|
|
99
|
+
</svg>
|
|
100
|
+
`;
|
|
101
|
+
var FONT_OPTIONS = [
|
|
102
|
+
{ label: "Arial", value: "Arial" },
|
|
103
|
+
{ label: "Helvetica", value: "Helvetica, Arial, sans-serif" },
|
|
104
|
+
{ label: "Verdana", value: "Verdana, Geneva, sans-serif" },
|
|
105
|
+
{ label: "Tahoma", value: "Tahoma, Geneva, sans-serif" },
|
|
106
|
+
{ label: "Trebuchet MS", value: "Trebuchet MS, Helvetica, sans-serif" },
|
|
107
|
+
{ label: "Georgia", value: "Georgia, serif" },
|
|
108
|
+
{ label: "Times New Roman", value: "Times New Roman, Times, serif" },
|
|
109
|
+
{ label: "Palatino", value: "Palatino, 'Palatino Linotype', serif" },
|
|
110
|
+
{ label: "Garamond", value: "Garamond, serif" },
|
|
111
|
+
{ label: "Book Antiqua", value: "'Book Antiqua', Palatino, serif" },
|
|
112
|
+
{ label: "Courier New", value: "'Courier New', Courier, monospace" },
|
|
113
|
+
{ label: "Lucida Console", value: "'Lucida Console', Monaco, monospace" },
|
|
114
|
+
{ label: "Impact", value: "Impact, Charcoal, sans-serif" },
|
|
115
|
+
{ label: "Comic Sans MS", value: "'Comic Sans MS', 'Comic Sans', cursive" },
|
|
116
|
+
{ label: "Segoe UI", value: "'Segoe UI', Tahoma, Geneva, sans-serif" },
|
|
117
|
+
{
|
|
118
|
+
label: "Roboto",
|
|
119
|
+
value: "Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif"
|
|
120
|
+
},
|
|
121
|
+
{ label: "Open Sans", value: "'Open Sans', Arial, sans-serif" },
|
|
122
|
+
{ label: "Lato", value: "Lato, 'Helvetica Neue', Arial, sans-serif" },
|
|
123
|
+
{ label: "Montserrat", value: "Montserrat, Arial, sans-serif" },
|
|
124
|
+
{ label: "Source Sans Pro", value: "'Source Sans Pro', Arial, sans-serif" },
|
|
125
|
+
{ label: "Fira Sans", value: "'Fira Sans', Arial, sans-serif" },
|
|
126
|
+
{ label: "Ubuntu", value: "Ubuntu, Arial, sans-serif" },
|
|
127
|
+
{ label: "Noto Sans", value: "'Noto Sans', Arial, sans-serif" },
|
|
128
|
+
{ label: "Droid Sans", value: "'Droid Sans', Arial, sans-serif" },
|
|
129
|
+
{
|
|
130
|
+
label: "Helvetica Neue",
|
|
131
|
+
value: "'Helvetica Neue', Helvetica, Arial, sans-serif"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
label: "System UI",
|
|
135
|
+
value: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
136
|
+
}
|
|
137
|
+
];
|
|
138
|
+
var SIZE_OPTIONS = [
|
|
139
|
+
{ label: "8", value: "8" },
|
|
140
|
+
{ label: "9", value: "9" },
|
|
141
|
+
{ label: "10", value: "10" },
|
|
142
|
+
{ label: "11", value: "11" },
|
|
143
|
+
{ label: "12", value: "12" },
|
|
144
|
+
{ label: "14", value: "14" },
|
|
145
|
+
{ label: "16", value: "16" },
|
|
146
|
+
{ label: "18", value: "18" },
|
|
147
|
+
{ label: "20", value: "20" },
|
|
148
|
+
{ label: "22", value: "22" },
|
|
149
|
+
{ label: "24", value: "24" },
|
|
150
|
+
{ label: "26", value: "26" },
|
|
151
|
+
{ label: "28", value: "28" },
|
|
152
|
+
{ label: "36", value: "36" },
|
|
153
|
+
{ label: "48", value: "48" },
|
|
154
|
+
{ label: "72", value: "72" }
|
|
155
|
+
];
|
|
156
|
+
var FORMAT_OPTIONS = [
|
|
157
|
+
{ label: "Heading 1", value: "h1" },
|
|
158
|
+
{ label: "Heading 2", value: "h2" },
|
|
159
|
+
{ label: "Heading 3", value: "h3" },
|
|
160
|
+
{ label: "Heading 4", value: "h4" },
|
|
161
|
+
{ label: "Heading 5", value: "h5" },
|
|
162
|
+
{ label: "Heading 6", value: "h6" }
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
// src/utils/sanitize.ts
|
|
166
|
+
import createDOMPurify from "dompurify";
|
|
167
|
+
function sanitizeHtml(html, ctx) {
|
|
168
|
+
if (!html) return "";
|
|
169
|
+
let win = null;
|
|
170
|
+
if (ctx && ctx.defaultView) {
|
|
171
|
+
win = ctx.defaultView;
|
|
172
|
+
} else if (ctx && ctx.document) {
|
|
173
|
+
win = ctx;
|
|
174
|
+
} else if (typeof window !== "undefined") {
|
|
175
|
+
win = window;
|
|
176
|
+
}
|
|
177
|
+
if (win) {
|
|
178
|
+
try {
|
|
179
|
+
const DOMPurify = createDOMPurify(win);
|
|
180
|
+
try {
|
|
181
|
+
DOMPurify.addHook("uponSanitizeAttribute", (node, data) => {
|
|
182
|
+
try {
|
|
183
|
+
if (data && data.attrName && data.attrName.startsWith("data-")) {
|
|
184
|
+
data.keepAttr = true;
|
|
185
|
+
}
|
|
186
|
+
} catch (e) {
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
} catch (e) {
|
|
190
|
+
}
|
|
191
|
+
return DOMPurify.sanitize(html, {
|
|
192
|
+
// Use sensible defaults: allow common formatting tags but strip scripts
|
|
193
|
+
ALLOWED_TAGS: [
|
|
194
|
+
"a",
|
|
195
|
+
"b",
|
|
196
|
+
"i",
|
|
197
|
+
"em",
|
|
198
|
+
"strong",
|
|
199
|
+
"u",
|
|
200
|
+
"p",
|
|
201
|
+
"div",
|
|
202
|
+
"span",
|
|
203
|
+
// Common semantic elements: preserve document structure so undo/redo
|
|
204
|
+
// does not flatten header/section/nav into plain content.
|
|
205
|
+
"header",
|
|
206
|
+
"nav",
|
|
207
|
+
"section",
|
|
208
|
+
"main",
|
|
209
|
+
"footer",
|
|
210
|
+
"article",
|
|
211
|
+
"aside",
|
|
212
|
+
"figure",
|
|
213
|
+
"figcaption",
|
|
214
|
+
"time",
|
|
215
|
+
// Interactive / form elements that may appear in content
|
|
216
|
+
"button",
|
|
217
|
+
"input",
|
|
218
|
+
"label",
|
|
219
|
+
"select",
|
|
220
|
+
"option",
|
|
221
|
+
"textarea",
|
|
222
|
+
"details",
|
|
223
|
+
"summary",
|
|
224
|
+
// Allow <style> tags so user/content-provided CSS is preserved
|
|
225
|
+
// when taking snapshots and during undo/redo operations.
|
|
226
|
+
// DOMPurify will still sanitize the contents of style blocks.
|
|
227
|
+
"style",
|
|
228
|
+
// Preserve linked stylesheets so page/editor styling isn't lost
|
|
229
|
+
"link",
|
|
230
|
+
"ul",
|
|
231
|
+
"ol",
|
|
232
|
+
"li",
|
|
233
|
+
"br",
|
|
234
|
+
"hr",
|
|
235
|
+
"blockquote",
|
|
236
|
+
"pre",
|
|
237
|
+
"code",
|
|
238
|
+
"h1",
|
|
239
|
+
"h2",
|
|
240
|
+
"h3",
|
|
241
|
+
"h4",
|
|
242
|
+
"h5",
|
|
243
|
+
"h6",
|
|
244
|
+
"img"
|
|
245
|
+
],
|
|
246
|
+
ALLOWED_ATTR: [
|
|
247
|
+
"href",
|
|
248
|
+
"title",
|
|
249
|
+
"alt",
|
|
250
|
+
"src",
|
|
251
|
+
"class",
|
|
252
|
+
"style",
|
|
253
|
+
// Attributes used by <link> tags
|
|
254
|
+
"rel",
|
|
255
|
+
"type",
|
|
256
|
+
"media"
|
|
257
|
+
],
|
|
258
|
+
// Also allow `id` attributes so element ids survive sanitization.
|
|
259
|
+
ADD_ATTR: ["id"]
|
|
260
|
+
});
|
|
261
|
+
} catch (e) {
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return html.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "").replace(/on[a-z]+=\"[^"]*\"/gi, "");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/core/state.ts
|
|
268
|
+
var _doc = null;
|
|
269
|
+
var _undoStack = [];
|
|
270
|
+
var _redoStack = [];
|
|
271
|
+
var _currentEditable = null;
|
|
272
|
+
var _maxStackSize = DEFAULT_MAX_STACK;
|
|
273
|
+
function _setDoc(doc) {
|
|
274
|
+
_doc = doc;
|
|
275
|
+
}
|
|
276
|
+
function _getDoc() {
|
|
277
|
+
return _doc;
|
|
278
|
+
}
|
|
279
|
+
function _setUndoStack(stack) {
|
|
280
|
+
_undoStack = stack;
|
|
281
|
+
editorEventEmitter.emit({
|
|
282
|
+
type: "undoStateChanged",
|
|
283
|
+
timestamp: Date.now(),
|
|
284
|
+
data: { canUndo: _undoStack.length > 1 }
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function _getUndoStack() {
|
|
288
|
+
return _undoStack;
|
|
289
|
+
}
|
|
290
|
+
function _setRedoStack(stack) {
|
|
291
|
+
_redoStack = stack;
|
|
292
|
+
editorEventEmitter.emit({
|
|
293
|
+
type: "redoStateChanged",
|
|
294
|
+
timestamp: Date.now(),
|
|
295
|
+
data: { canRedo: _redoStack.length > 0 }
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function _getRedoStack() {
|
|
299
|
+
return _redoStack;
|
|
300
|
+
}
|
|
301
|
+
function _setCurrentEditable(el) {
|
|
302
|
+
_currentEditable = el;
|
|
303
|
+
editorEventEmitter.emit({
|
|
304
|
+
type: "selectionChanged",
|
|
305
|
+
timestamp: Date.now(),
|
|
306
|
+
data: { element: el == null ? void 0 : el.tagName }
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function _getCurrentEditable() {
|
|
310
|
+
return _currentEditable;
|
|
311
|
+
}
|
|
312
|
+
function pushStandaloneSnapshot(clearRedo = true) {
|
|
313
|
+
if (!_doc) return;
|
|
314
|
+
const clone = _doc.documentElement.cloneNode(true);
|
|
315
|
+
const toolbarNode = clone.querySelector(`#${TOOLBAR_ID}`);
|
|
316
|
+
if (toolbarNode && toolbarNode.parentNode)
|
|
317
|
+
toolbarNode.parentNode.removeChild(toolbarNode);
|
|
318
|
+
const styleNode = clone.querySelector(`#${STYLE_ID}`);
|
|
319
|
+
if (styleNode && styleNode.parentNode)
|
|
320
|
+
styleNode.parentNode.removeChild(styleNode);
|
|
321
|
+
try {
|
|
322
|
+
const editableNodes = clone.querySelectorAll(
|
|
323
|
+
"[contenteditable], ." + CLASS_EDITABLE + ", ." + CLASS_ACTIVE
|
|
324
|
+
);
|
|
325
|
+
editableNodes.forEach((el) => {
|
|
326
|
+
try {
|
|
327
|
+
if (el instanceof Element) {
|
|
328
|
+
if (el.hasAttribute("contenteditable"))
|
|
329
|
+
el.removeAttribute("contenteditable");
|
|
330
|
+
if (el.hasAttribute("tabindex")) el.removeAttribute("tabindex");
|
|
331
|
+
el.classList.remove(CLASS_EDITABLE, CLASS_ACTIVE);
|
|
332
|
+
}
|
|
333
|
+
} catch (e) {
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
} catch (e) {
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
const scripts = Array.from(
|
|
340
|
+
clone.querySelectorAll("script")
|
|
341
|
+
);
|
|
342
|
+
scripts.forEach((s) => {
|
|
343
|
+
var _a;
|
|
344
|
+
try {
|
|
345
|
+
const code = s.textContent || "";
|
|
346
|
+
const attrs = {};
|
|
347
|
+
Array.from(s.attributes).forEach((a) => attrs[a.name] = a.value);
|
|
348
|
+
const placeholder = clone.ownerDocument.createElement("span");
|
|
349
|
+
try {
|
|
350
|
+
const safe = typeof btoa !== "undefined" ? btoa(unescape(encodeURIComponent(code))) : encodeURIComponent(code);
|
|
351
|
+
placeholder.setAttribute("data-rhe-script", safe);
|
|
352
|
+
} catch (e) {
|
|
353
|
+
placeholder.setAttribute("data-rhe-script", encodeURIComponent(code));
|
|
354
|
+
}
|
|
355
|
+
if (Object.keys(attrs).length) {
|
|
356
|
+
placeholder.setAttribute(
|
|
357
|
+
"data-rhe-script-attrs",
|
|
358
|
+
encodeURIComponent(JSON.stringify(attrs))
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
const parentMarker = s.closest("[data-rhe-id]");
|
|
362
|
+
if (parentMarker && parentMarker.getAttribute("data-rhe-id")) {
|
|
363
|
+
placeholder.setAttribute(
|
|
364
|
+
"data-rhe-script-parent",
|
|
365
|
+
parentMarker.getAttribute("data-rhe-id")
|
|
366
|
+
);
|
|
367
|
+
} else {
|
|
368
|
+
placeholder.setAttribute("data-rhe-script-parent", "head");
|
|
369
|
+
}
|
|
370
|
+
(_a = s.parentNode) == null ? void 0 : _a.replaceChild(placeholder, s);
|
|
371
|
+
} catch (e) {
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
} catch (e) {
|
|
375
|
+
}
|
|
376
|
+
const snapRaw = clone.outerHTML;
|
|
377
|
+
const snap = sanitizeHtml(snapRaw, _doc);
|
|
378
|
+
if (!_undoStack.length || _undoStack[_undoStack.length - 1] !== snap) {
|
|
379
|
+
_undoStack.push(snap);
|
|
380
|
+
if (_undoStack.length > _maxStackSize) _undoStack.shift();
|
|
381
|
+
editorEventEmitter.emit({
|
|
382
|
+
type: "contentChanged",
|
|
383
|
+
timestamp: Date.now(),
|
|
384
|
+
data: { htmlLength: snap.length }
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
if (clearRedo) {
|
|
388
|
+
_redoStack = [];
|
|
389
|
+
editorEventEmitter.emit({
|
|
390
|
+
type: "redoStateChanged",
|
|
391
|
+
timestamp: Date.now(),
|
|
392
|
+
data: { canRedo: false }
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function setMaxStackSize(size) {
|
|
397
|
+
_maxStackSize = Math.max(1, size);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export {
|
|
401
|
+
editorEventEmitter,
|
|
402
|
+
getEditorEventEmitter,
|
|
403
|
+
TOOLBAR_ID,
|
|
404
|
+
STYLE_ID,
|
|
405
|
+
CLASS_EDITABLE,
|
|
406
|
+
CLASS_ACTIVE,
|
|
407
|
+
TOOLBAR_BG,
|
|
408
|
+
TOOLBAR_BORDER,
|
|
409
|
+
BUTTON_BORDER,
|
|
410
|
+
BUTTON_ACTIVE_BG,
|
|
411
|
+
BUTTON_BG,
|
|
412
|
+
BUTTON_COLOR,
|
|
413
|
+
INFO_COLOR,
|
|
414
|
+
HOVER_OUTLINE,
|
|
415
|
+
ACTIVE_OUTLINE,
|
|
416
|
+
LABEL_BOLD,
|
|
417
|
+
LABEL_ITALIC,
|
|
418
|
+
LABEL_UNDERLINE,
|
|
419
|
+
LABEL_STRIKETHROUGH,
|
|
420
|
+
LABEL_UNDO,
|
|
421
|
+
LABEL_REDO,
|
|
422
|
+
LABEL_LINK,
|
|
423
|
+
LABEL_ALIGN_LEFT,
|
|
424
|
+
LABEL_ALIGN_CENTER,
|
|
425
|
+
LABEL_ALIGN_RIGHT,
|
|
426
|
+
FONT_OPTIONS,
|
|
427
|
+
SIZE_OPTIONS,
|
|
428
|
+
FORMAT_OPTIONS,
|
|
429
|
+
sanitizeHtml,
|
|
430
|
+
_setDoc,
|
|
431
|
+
_getDoc,
|
|
432
|
+
_setUndoStack,
|
|
433
|
+
_getUndoStack,
|
|
434
|
+
_setRedoStack,
|
|
435
|
+
_getRedoStack,
|
|
436
|
+
_setCurrentEditable,
|
|
437
|
+
_getCurrentEditable,
|
|
438
|
+
pushStandaloneSnapshot,
|
|
439
|
+
setMaxStackSize
|
|
440
|
+
};
|
|
441
|
+
//# sourceMappingURL=chunk-ZDGUOGND.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/events.ts","../src/core/constants.ts","../src/utils/sanitize.ts","../src/core/state.ts"],"sourcesContent":["import type { EditorEventType, EditorEvent, EditorEventHandler } from \"./types\";\r\n\r\nexport class EditorEventEmitter {\r\n private listeners: Map<EditorEventType, Set<EditorEventHandler>> = new Map();\r\n\r\n on(type: EditorEventType, handler: EditorEventHandler): () => void {\r\n if (!this.listeners.has(type)) {\r\n this.listeners.set(type, new Set());\r\n }\r\n this.listeners.get(type)!.add(handler);\r\n return () => this.off(type, handler);\r\n }\r\n\r\n once(type: EditorEventType, handler: EditorEventHandler): void {\r\n const unsubscribe = this.on(type, (event) => {\r\n handler(event);\r\n unsubscribe();\r\n });\r\n }\r\n\r\n off(type: EditorEventType, handler: EditorEventHandler): void {\r\n const handlers = this.listeners.get(type);\r\n if (handlers) {\r\n handlers.delete(handler);\r\n if (handlers.size === 0) this.listeners.delete(type);\r\n }\r\n }\r\n\r\n emit(event: EditorEvent): void {\r\n const handlers = this.listeners.get(event.type);\r\n if (handlers) {\r\n handlers.forEach((handler) => {\r\n try {\r\n handler(event);\r\n } catch (error) {\r\n console.error(\r\n `[rich-html-editor] Error in event handler for ${event.type}:`,\r\n error\r\n );\r\n }\r\n });\r\n }\r\n }\r\n\r\n removeAllListeners(type?: EditorEventType): void {\r\n if (type) this.listeners.delete(type);\r\n else this.listeners.clear();\r\n }\r\n\r\n listenerCount(type: EditorEventType): number {\r\n return this.listeners.get(type)?.size ?? 0;\r\n }\r\n}\r\n\r\nexport const editorEventEmitter = new EditorEventEmitter();\r\n\r\nexport function getEditorEventEmitter(): EditorEventEmitter {\r\n return editorEventEmitter;\r\n}\r\n","// Shared constants for the rich-html-editor (moved to core)\r\nexport const TOOLBAR_ID = \"editor-toolbar\";\r\nexport const STYLE_ID = \"editor-styles\";\r\nexport const CLASS_EDITABLE = \"editor-editable-element\";\r\nexport const CLASS_ACTIVE = \"editor-active-element\";\r\n\r\n// Default undo/redo stack size\r\nexport const DEFAULT_MAX_STACK = 60;\r\n\r\n// Toolbar styling constants\r\nexport const TOOLBAR_BG = \"#f8fafc\";\r\nexport const TOOLBAR_BORDER = \"#e5e7eb\";\r\nexport const BUTTON_BORDER = \"#d1d5db\";\r\nexport const BUTTON_ACTIVE_BG = \"#e0e7ff\";\r\nexport const BUTTON_BG = \"#fff\";\r\nexport const BUTTON_COLOR = \"#222\";\r\nexport const INFO_COLOR = \"#888\";\r\n\r\n// Outline colors used by injected styles\r\nexport const HOVER_OUTLINE = \"#2563eb\";\r\nexport const ACTIVE_OUTLINE = \"#16a34a\";\r\n\r\n// Toolbar label/icon constants (kept in core for reuse)\r\nexport const LABEL_BOLD = \"<b>B</b>\";\r\nexport const LABEL_ITALIC = \"<i>I</i>\";\r\nexport const LABEL_UNDERLINE = \"<u>U</u>\";\r\nexport const LABEL_STRIKETHROUGH = \"<s>S</s>\";\r\nexport const LABEL_UNDO = \"↺\";\r\nexport const LABEL_REDO = \"↻\";\r\nexport const LABEL_LINK = \"🔗\";\r\nexport const LABEL_ALIGN_LEFT = `\r\n\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n\t\t<rect x=\"1\" y=\"2\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"6\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"10\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"14\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t</svg>\r\n`;\r\nexport const LABEL_ALIGN_CENTER = `\r\n\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n\t\t<rect x=\"3\" y=\"2\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"6\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"3\" y=\"10\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"14\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t</svg>\r\n`;\r\nexport const LABEL_ALIGN_RIGHT = `\r\n\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n\t\t<rect x=\"5\" y=\"2\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"6\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"5\" y=\"10\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"14\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t</svg>\r\n`;\r\n\r\n// Font and size option lists used by the toolbar\r\nexport const FONT_OPTIONS: { label: string; value: string }[] = [\r\n { label: \"Arial\", value: \"Arial\" },\r\n { label: \"Helvetica\", value: \"Helvetica, Arial, sans-serif\" },\r\n { label: \"Verdana\", value: \"Verdana, Geneva, sans-serif\" },\r\n { label: \"Tahoma\", value: \"Tahoma, Geneva, sans-serif\" },\r\n { label: \"Trebuchet MS\", value: \"Trebuchet MS, Helvetica, sans-serif\" },\r\n { label: \"Georgia\", value: \"Georgia, serif\" },\r\n { label: \"Times New Roman\", value: \"Times New Roman, Times, serif\" },\r\n { label: \"Palatino\", value: \"Palatino, 'Palatino Linotype', serif\" },\r\n { label: \"Garamond\", value: \"Garamond, serif\" },\r\n { label: \"Book Antiqua\", value: \"'Book Antiqua', Palatino, serif\" },\r\n { label: \"Courier New\", value: \"'Courier New', Courier, monospace\" },\r\n { label: \"Lucida Console\", value: \"'Lucida Console', Monaco, monospace\" },\r\n { label: \"Impact\", value: \"Impact, Charcoal, sans-serif\" },\r\n { label: \"Comic Sans MS\", value: \"'Comic Sans MS', 'Comic Sans', cursive\" },\r\n { label: \"Segoe UI\", value: \"'Segoe UI', Tahoma, Geneva, sans-serif\" },\r\n {\r\n label: \"Roboto\",\r\n value: \"Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif\",\r\n },\r\n { label: \"Open Sans\", value: \"'Open Sans', Arial, sans-serif\" },\r\n { label: \"Lato\", value: \"Lato, 'Helvetica Neue', Arial, sans-serif\" },\r\n { label: \"Montserrat\", value: \"Montserrat, Arial, sans-serif\" },\r\n { label: \"Source Sans Pro\", value: \"'Source Sans Pro', Arial, sans-serif\" },\r\n { label: \"Fira Sans\", value: \"'Fira Sans', Arial, sans-serif\" },\r\n { label: \"Ubuntu\", value: \"Ubuntu, Arial, sans-serif\" },\r\n { label: \"Noto Sans\", value: \"'Noto Sans', Arial, sans-serif\" },\r\n { label: \"Droid Sans\", value: \"'Droid Sans', Arial, sans-serif\" },\r\n {\r\n label: \"Helvetica Neue\",\r\n value: \"'Helvetica Neue', Helvetica, Arial, sans-serif\",\r\n },\r\n {\r\n label: \"System UI\",\r\n value:\r\n \"system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\",\r\n },\r\n];\r\n\r\nexport const SIZE_OPTIONS: { label: string; value: string }[] = [\r\n { label: \"8\", value: \"8\" },\r\n { label: \"9\", value: \"9\" },\r\n { label: \"10\", value: \"10\" },\r\n { label: \"11\", value: \"11\" },\r\n { label: \"12\", value: \"12\" },\r\n { label: \"14\", value: \"14\" },\r\n { label: \"16\", value: \"16\" },\r\n { label: \"18\", value: \"18\" },\r\n { label: \"20\", value: \"20\" },\r\n { label: \"22\", value: \"22\" },\r\n { label: \"24\", value: \"24\" },\r\n { label: \"26\", value: \"26\" },\r\n { label: \"28\", value: \"28\" },\r\n { label: \"36\", value: \"36\" },\r\n { label: \"48\", value: \"48\" },\r\n { label: \"72\", value: \"72\" },\r\n];\r\n\r\n// Block format options (Paragraph + Headings)\r\nexport const FORMAT_OPTIONS: { label: string; value: string }[] = [\r\n { label: \"Heading 1\", value: \"h1\" },\r\n { label: \"Heading 2\", value: \"h2\" },\r\n { label: \"Heading 3\", value: \"h3\" },\r\n { label: \"Heading 4\", value: \"h4\" },\r\n { label: \"Heading 5\", value: \"h5\" },\r\n { label: \"Heading 6\", value: \"h6\" },\r\n];\r\n\r\n// Shared SVG used by toolbar color inputs (palette icon)\r\nexport const PALETTE_SVG = `\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n <path d=\"M12 2C7 2 3 6 3 11c0 2.8 1.4 5.3 3.7 6.8.9.6 2 .9 3.3.9 1.6 0 3.1-.5 4.3-1.4.6-.4.9-1.1.6-1.8-.3-.7-1-1-1.7-.8-1.1.3-2.3.2-3.4-.3C10.3 14.6 9 13.6 9 12c0-1.7 1.3-3 3-3 .8 0 1.5.3 2.1.8.5.4 1.2.3 1.6-.2.9-1.1 1.4-2.4 1.4-3.8C20.9 6 16.9 2 12 2z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n <circle cx=\"7.5\" cy=\"10.5\" r=\"1\" fill=\"currentColor\"/>\r\n <circle cx=\"11.5\" cy=\"8.5\" r=\"1\" fill=\"currentColor\"/>\r\n <circle cx=\"14.5\" cy=\"11.5\" r=\"1\" fill=\"currentColor\"/>\r\n </svg>\r\n`;\r\n\r\n// MS Word-like icons for color controls\r\nexport const TEXT_COLOR_ICON_HTML = `\r\n <span class=\"text-color-wrapper\" aria-hidden=\"true\">\r\n <span class=\"text-A\">A</span>\r\n </span>\r\n`;\r\n\r\nexport const HIGHLIGHT_ICON_HTML = `\r\n <span class=\"highlight-wrapper\" aria-hidden=\"true\">\r\n <span class=\"highlight-bar\" data-role=\"highlight\"></span>\r\n <span class=\"text-A\">A</span>\r\n </span>\r\n`;\r\n","import createDOMPurify from \"dompurify\";\r\n\r\n/**\r\n * Sanitize HTML using DOMPurify.\r\n *\r\n * Works in browser and in jsdom (tests) by accepting either a `Window`\r\n * or a `Document` (from which `defaultView` is used).\r\n */\r\nexport function sanitizeHtml(\r\n html: string,\r\n ctx?: Window | Document | null\r\n): string {\r\n if (!html) return \"\";\r\n // Try to get a Window reference\r\n let win: Window | null = null;\r\n if (ctx && (ctx as Document).defaultView) {\r\n win = (ctx as Document).defaultView as Window;\r\n } else if (ctx && (ctx as Window).document) {\r\n win = ctx as Window;\r\n } else if (typeof window !== \"undefined\") {\r\n win = window as Window;\r\n }\r\n\r\n // If we have a window, use DOMPurify\r\n if (win) {\r\n try {\r\n const DOMPurify = createDOMPurify(win as any);\r\n // Preserve element ids and data-* attributes so restoring snapshots\r\n // during undo/redo does not break page scripts or CSS that rely on\r\n // those attributes. Still strip <script> and all inline event\r\n // handlers (on*) to avoid executing arbitrary script.\r\n // Use ADD_ATTR to allow `id`, and a hook to preserve any `data-` attrs.\r\n try {\r\n DOMPurify.addHook(\"uponSanitizeAttribute\", (node: any, data: any) => {\r\n try {\r\n if (data && data.attrName && data.attrName.startsWith(\"data-\")) {\r\n // Keep data-* attributes\r\n (data as any).keepAttr = true;\r\n }\r\n } catch (e) {\r\n /* ignore hook errors */\r\n }\r\n });\r\n } catch (e) {\r\n /* addHook may not be available in some environments; ignore */\r\n }\r\n\r\n return DOMPurify.sanitize(html, {\r\n // Use sensible defaults: allow common formatting tags but strip scripts\r\n ALLOWED_TAGS: [\r\n \"a\",\r\n \"b\",\r\n \"i\",\r\n \"em\",\r\n \"strong\",\r\n \"u\",\r\n \"p\",\r\n \"div\",\r\n \"span\",\r\n // Common semantic elements: preserve document structure so undo/redo\r\n // does not flatten header/section/nav into plain content.\r\n \"header\",\r\n \"nav\",\r\n \"section\",\r\n \"main\",\r\n \"footer\",\r\n \"article\",\r\n \"aside\",\r\n \"figure\",\r\n \"figcaption\",\r\n \"time\",\r\n // Interactive / form elements that may appear in content\r\n \"button\",\r\n \"input\",\r\n \"label\",\r\n \"select\",\r\n \"option\",\r\n \"textarea\",\r\n \"details\",\r\n \"summary\",\r\n // Allow <style> tags so user/content-provided CSS is preserved\r\n // when taking snapshots and during undo/redo operations.\r\n // DOMPurify will still sanitize the contents of style blocks.\r\n \"style\",\r\n // Preserve linked stylesheets so page/editor styling isn't lost\r\n \"link\",\r\n \"ul\",\r\n \"ol\",\r\n \"li\",\r\n \"br\",\r\n \"hr\",\r\n \"blockquote\",\r\n \"pre\",\r\n \"code\",\r\n \"h1\",\r\n \"h2\",\r\n \"h3\",\r\n \"h4\",\r\n \"h5\",\r\n \"h6\",\r\n \"img\",\r\n ],\r\n ALLOWED_ATTR: [\r\n \"href\",\r\n \"title\",\r\n \"alt\",\r\n \"src\",\r\n \"class\",\r\n \"style\",\r\n // Attributes used by <link> tags\r\n \"rel\",\r\n \"type\",\r\n \"media\",\r\n ],\r\n // Also allow `id` attributes so element ids survive sanitization.\r\n ADD_ATTR: [\"id\"],\r\n });\r\n } catch (e) {\r\n // If DOMPurify initialization fails, fall through to minimal stripping\r\n }\r\n }\r\n\r\n // Minimal fallback: remove <script> tags and on* attributes\r\n return html\r\n .replace(/<script[\\s\\S]*?>[\\s\\S]*?<\\/script>/gi, \"\")\r\n .replace(/on[a-z]+=\\\"[^\"]*\\\"/gi, \"\");\r\n}\r\n\r\nexport default sanitizeHtml;\r\n","import { editorEventEmitter } from \"./events\";\r\nimport {\r\n DEFAULT_MAX_STACK,\r\n TOOLBAR_ID,\r\n STYLE_ID,\r\n CLASS_EDITABLE,\r\n CLASS_ACTIVE,\r\n} from \"./constants\";\r\nimport { sanitizeHtml } from \"../utils/sanitize\";\r\n\r\nlet _doc: Document | null = null;\r\nlet _undoStack: string[] = [];\r\nlet _redoStack: string[] = [];\r\nlet _currentEditable: HTMLElement | null = null;\r\nlet _maxStackSize = DEFAULT_MAX_STACK;\r\n\r\nexport function _setDoc(doc: Document | null) {\r\n _doc = doc;\r\n}\r\nexport function _getDoc() {\r\n return _doc;\r\n}\r\nexport function _setUndoStack(stack: string[]) {\r\n _undoStack = stack;\r\n editorEventEmitter.emit({\r\n type: \"undoStateChanged\",\r\n timestamp: Date.now(),\r\n data: { canUndo: _undoStack.length > 1 },\r\n });\r\n}\r\nexport function _getUndoStack() {\r\n return _undoStack;\r\n}\r\nexport function _setRedoStack(stack: string[]) {\r\n _redoStack = stack;\r\n editorEventEmitter.emit({\r\n type: \"redoStateChanged\",\r\n timestamp: Date.now(),\r\n data: { canRedo: _redoStack.length > 0 },\r\n });\r\n}\r\nexport function _getRedoStack() {\r\n return _redoStack;\r\n}\r\nexport function _setCurrentEditable(el: HTMLElement | null) {\r\n _currentEditable = el;\r\n editorEventEmitter.emit({\r\n type: \"selectionChanged\",\r\n timestamp: Date.now(),\r\n data: { element: el?.tagName },\r\n });\r\n}\r\nexport function _getCurrentEditable() {\r\n return _currentEditable;\r\n}\r\nexport function pushStandaloneSnapshot(clearRedo = true) {\r\n if (!_doc) return;\r\n // Clone the documentElement and remove injected UI (toolbar/style)\r\n // so snapshots capture only the user's content.\r\n const clone = _doc.documentElement.cloneNode(true) as HTMLElement;\r\n const toolbarNode = clone.querySelector(`#${TOOLBAR_ID}`);\r\n if (toolbarNode && toolbarNode.parentNode)\r\n toolbarNode.parentNode.removeChild(toolbarNode);\r\n const styleNode = clone.querySelector(`#${STYLE_ID}`);\r\n if (styleNode && styleNode.parentNode)\r\n styleNode.parentNode.removeChild(styleNode);\r\n // Remove editor-specific attributes/classes so snapshots don't persist\r\n // transient editing state (contenteditable, toolbar classes, tabindex).\r\n try {\r\n const editableNodes = clone.querySelectorAll(\r\n \"[contenteditable], .\" + CLASS_EDITABLE + \", .\" + CLASS_ACTIVE\r\n );\r\n editableNodes.forEach((el) => {\r\n try {\r\n if (el instanceof Element) {\r\n if (el.hasAttribute(\"contenteditable\"))\r\n el.removeAttribute(\"contenteditable\");\r\n if (el.hasAttribute(\"tabindex\")) el.removeAttribute(\"tabindex\");\r\n el.classList.remove(CLASS_EDITABLE, CLASS_ACTIVE);\r\n }\r\n } catch (e) {\r\n /* ignore */\r\n }\r\n });\r\n } catch (e) {\r\n /* ignore */\r\n }\r\n // Preserve scripts by replacing them with a harmless placeholder\r\n // that contains the encoded script source/attributes. This allows the\r\n // sanitizer to run (which strips <script> tags) while keeping the\r\n // script content available to re-insert and execute on restore.\r\n try {\r\n const scripts = Array.from(\r\n clone.querySelectorAll<HTMLScriptElement>(\"script\")\r\n );\r\n scripts.forEach((s) => {\r\n try {\r\n const code = s.textContent || \"\";\r\n const attrs: Record<string, string> = {};\r\n Array.from(s.attributes).forEach((a) => (attrs[a.name] = a.value));\r\n const placeholder = clone.ownerDocument!.createElement(\"span\");\r\n // encode script body in base64 to survive sanitization\r\n try {\r\n // btoa may throw on Unicode; encodeURIComponent first\r\n const safe =\r\n typeof btoa !== \"undefined\"\r\n ? btoa(unescape(encodeURIComponent(code)))\r\n : encodeURIComponent(code);\r\n placeholder.setAttribute(\"data-rhe-script\", safe);\r\n } catch (e) {\r\n placeholder.setAttribute(\"data-rhe-script\", encodeURIComponent(code));\r\n }\r\n if (Object.keys(attrs).length) {\r\n placeholder.setAttribute(\r\n \"data-rhe-script-attrs\",\r\n encodeURIComponent(JSON.stringify(attrs))\r\n );\r\n }\r\n // mark parent editable region if present so we can reinsert in-place\r\n const parentMarker = s.closest(\"[data-rhe-id]\") as HTMLElement | null;\r\n if (parentMarker && parentMarker.getAttribute(\"data-rhe-id\")) {\r\n placeholder.setAttribute(\r\n \"data-rhe-script-parent\",\r\n parentMarker.getAttribute(\"data-rhe-id\")!\r\n );\r\n } else {\r\n placeholder.setAttribute(\"data-rhe-script-parent\", \"head\");\r\n }\r\n s.parentNode?.replaceChild(placeholder, s);\r\n } catch (e) {\r\n /* ignore per-script errors */\r\n }\r\n });\r\n } catch (e) {\r\n /* ignore script extraction errors */\r\n }\r\n const snapRaw = clone.outerHTML;\r\n const snap = sanitizeHtml(snapRaw, _doc);\r\n if (!_undoStack.length || _undoStack[_undoStack.length - 1] !== snap) {\r\n _undoStack.push(snap);\r\n if (_undoStack.length > _maxStackSize) _undoStack.shift();\r\n editorEventEmitter.emit({\r\n type: \"contentChanged\",\r\n timestamp: Date.now(),\r\n data: { htmlLength: snap.length },\r\n });\r\n }\r\n if (clearRedo) {\r\n _redoStack = [];\r\n editorEventEmitter.emit({\r\n type: \"redoStateChanged\",\r\n timestamp: Date.now(),\r\n data: { canRedo: false },\r\n });\r\n }\r\n}\r\nexport function setMaxStackSize(size: number) {\r\n _maxStackSize = Math.max(1, size);\r\n}\r\n"],"mappings":";AAEO,IAAM,qBAAN,MAAyB;AAAA,EAAzB;AACL,SAAQ,YAA2D,oBAAI,IAAI;AAAA;AAAA,EAE3E,GAAG,MAAuB,SAAyC;AACjE,QAAI,CAAC,KAAK,UAAU,IAAI,IAAI,GAAG;AAC7B,WAAK,UAAU,IAAI,MAAM,oBAAI,IAAI,CAAC;AAAA,IACpC;AACA,SAAK,UAAU,IAAI,IAAI,EAAG,IAAI,OAAO;AACrC,WAAO,MAAM,KAAK,IAAI,MAAM,OAAO;AAAA,EACrC;AAAA,EAEA,KAAK,MAAuB,SAAmC;AAC7D,UAAM,cAAc,KAAK,GAAG,MAAM,CAAC,UAAU;AAC3C,cAAQ,KAAK;AACb,kBAAY;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,MAAuB,SAAmC;AAC5D,UAAM,WAAW,KAAK,UAAU,IAAI,IAAI;AACxC,QAAI,UAAU;AACZ,eAAS,OAAO,OAAO;AACvB,UAAI,SAAS,SAAS,EAAG,MAAK,UAAU,OAAO,IAAI;AAAA,IACrD;AAAA,EACF;AAAA,EAEA,KAAK,OAA0B;AAC7B,UAAM,WAAW,KAAK,UAAU,IAAI,MAAM,IAAI;AAC9C,QAAI,UAAU;AACZ,eAAS,QAAQ,CAAC,YAAY;AAC5B,YAAI;AACF,kBAAQ,KAAK;AAAA,QACf,SAAS,OAAO;AACd,kBAAQ;AAAA,YACN,iDAAiD,MAAM,IAAI;AAAA,YAC3D;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,mBAAmB,MAA8B;AAC/C,QAAI,KAAM,MAAK,UAAU,OAAO,IAAI;AAAA,QAC/B,MAAK,UAAU,MAAM;AAAA,EAC5B;AAAA,EAEA,cAAc,MAA+B;AAjD/C;AAkDI,YAAO,gBAAK,UAAU,IAAI,IAAI,MAAvB,mBAA0B,SAA1B,YAAkC;AAAA,EAC3C;AACF;AAEO,IAAM,qBAAqB,IAAI,mBAAmB;AAElD,SAAS,wBAA4C;AAC1D,SAAO;AACT;;;ACzDO,IAAM,aAAa;AACnB,IAAM,WAAW;AACjB,IAAM,iBAAiB;AACvB,IAAM,eAAe;AAGrB,IAAM,oBAAoB;AAG1B,IAAM,aAAa;AACnB,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AACtB,IAAM,mBAAmB;AACzB,IAAM,YAAY;AAClB,IAAM,eAAe;AACrB,IAAM,aAAa;AAGnB,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AAGvB,IAAM,aAAa;AACnB,IAAM,eAAe;AACrB,IAAM,kBAAkB;AACxB,IAAM,sBAAsB;AAC5B,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQzB,IAAM,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ3B,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAU1B,IAAM,eAAmD;AAAA,EAC9D,EAAE,OAAO,SAAS,OAAO,QAAQ;AAAA,EACjC,EAAE,OAAO,aAAa,OAAO,+BAA+B;AAAA,EAC5D,EAAE,OAAO,WAAW,OAAO,8BAA8B;AAAA,EACzD,EAAE,OAAO,UAAU,OAAO,6BAA6B;AAAA,EACvD,EAAE,OAAO,gBAAgB,OAAO,sCAAsC;AAAA,EACtE,EAAE,OAAO,WAAW,OAAO,iBAAiB;AAAA,EAC5C,EAAE,OAAO,mBAAmB,OAAO,gCAAgC;AAAA,EACnE,EAAE,OAAO,YAAY,OAAO,uCAAuC;AAAA,EACnE,EAAE,OAAO,YAAY,OAAO,kBAAkB;AAAA,EAC9C,EAAE,OAAO,gBAAgB,OAAO,kCAAkC;AAAA,EAClE,EAAE,OAAO,eAAe,OAAO,oCAAoC;AAAA,EACnE,EAAE,OAAO,kBAAkB,OAAO,sCAAsC;AAAA,EACxE,EAAE,OAAO,UAAU,OAAO,+BAA+B;AAAA,EACzD,EAAE,OAAO,iBAAiB,OAAO,yCAAyC;AAAA,EAC1E,EAAE,OAAO,YAAY,OAAO,yCAAyC;AAAA,EACrE;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAAA,EACA,EAAE,OAAO,aAAa,OAAO,iCAAiC;AAAA,EAC9D,EAAE,OAAO,QAAQ,OAAO,4CAA4C;AAAA,EACpE,EAAE,OAAO,cAAc,OAAO,gCAAgC;AAAA,EAC9D,EAAE,OAAO,mBAAmB,OAAO,uCAAuC;AAAA,EAC1E,EAAE,OAAO,aAAa,OAAO,iCAAiC;AAAA,EAC9D,EAAE,OAAO,UAAU,OAAO,4BAA4B;AAAA,EACtD,EAAE,OAAO,aAAa,OAAO,iCAAiC;AAAA,EAC9D,EAAE,OAAO,cAAc,OAAO,kCAAkC;AAAA,EAChE;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OACE;AAAA,EACJ;AACF;AAEO,IAAM,eAAmD;AAAA,EAC9D,EAAE,OAAO,KAAK,OAAO,IAAI;AAAA,EACzB,EAAE,OAAO,KAAK,OAAO,IAAI;AAAA,EACzB,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EAC3B,EAAE,OAAO,MAAM,OAAO,KAAK;AAC7B;AAGO,IAAM,iBAAqD;AAAA,EAChE,EAAE,OAAO,aAAa,OAAO,KAAK;AAAA,EAClC,EAAE,OAAO,aAAa,OAAO,KAAK;AAAA,EAClC,EAAE,OAAO,aAAa,OAAO,KAAK;AAAA,EAClC,EAAE,OAAO,aAAa,OAAO,KAAK;AAAA,EAClC,EAAE,OAAO,aAAa,OAAO,KAAK;AAAA,EAClC,EAAE,OAAO,aAAa,OAAO,KAAK;AACpC;;;AC1HA,OAAO,qBAAqB;AAQrB,SAAS,aACd,MACA,KACQ;AACR,MAAI,CAAC,KAAM,QAAO;AAElB,MAAI,MAAqB;AACzB,MAAI,OAAQ,IAAiB,aAAa;AACxC,UAAO,IAAiB;AAAA,EAC1B,WAAW,OAAQ,IAAe,UAAU;AAC1C,UAAM;AAAA,EACR,WAAW,OAAO,WAAW,aAAa;AACxC,UAAM;AAAA,EACR;AAGA,MAAI,KAAK;AACP,QAAI;AACF,YAAM,YAAY,gBAAgB,GAAU;AAM5C,UAAI;AACF,kBAAU,QAAQ,yBAAyB,CAAC,MAAW,SAAc;AACnE,cAAI;AACF,gBAAI,QAAQ,KAAK,YAAY,KAAK,SAAS,WAAW,OAAO,GAAG;AAE9D,cAAC,KAAa,WAAW;AAAA,YAC3B;AAAA,UACF,SAAS,GAAG;AAAA,UAEZ;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AAAA,MAEZ;AAEA,aAAO,UAAU,SAAS,MAAM;AAAA;AAAA,QAE9B,cAAc;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA;AAAA,UAGA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,UAEA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA;AAAA;AAAA,UAIA;AAAA;AAAA,UAEA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,cAAc;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,UAEA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA;AAAA,QAEA,UAAU,CAAC,IAAI;AAAA,MACjB,CAAC;AAAA,IACH,SAAS,GAAG;AAAA,IAEZ;AAAA,EACF;AAGA,SAAO,KACJ,QAAQ,wCAAwC,EAAE,EAClD,QAAQ,wBAAwB,EAAE;AACvC;;;ACpHA,IAAI,OAAwB;AAC5B,IAAI,aAAuB,CAAC;AAC5B,IAAI,aAAuB,CAAC;AAC5B,IAAI,mBAAuC;AAC3C,IAAI,gBAAgB;AAEb,SAAS,QAAQ,KAAsB;AAC5C,SAAO;AACT;AACO,SAAS,UAAU;AACxB,SAAO;AACT;AACO,SAAS,cAAc,OAAiB;AAC7C,eAAa;AACb,qBAAmB,KAAK;AAAA,IACtB,MAAM;AAAA,IACN,WAAW,KAAK,IAAI;AAAA,IACpB,MAAM,EAAE,SAAS,WAAW,SAAS,EAAE;AAAA,EACzC,CAAC;AACH;AACO,SAAS,gBAAgB;AAC9B,SAAO;AACT;AACO,SAAS,cAAc,OAAiB;AAC7C,eAAa;AACb,qBAAmB,KAAK;AAAA,IACtB,MAAM;AAAA,IACN,WAAW,KAAK,IAAI;AAAA,IACpB,MAAM,EAAE,SAAS,WAAW,SAAS,EAAE;AAAA,EACzC,CAAC;AACH;AACO,SAAS,gBAAgB;AAC9B,SAAO;AACT;AACO,SAAS,oBAAoB,IAAwB;AAC1D,qBAAmB;AACnB,qBAAmB,KAAK;AAAA,IACtB,MAAM;AAAA,IACN,WAAW,KAAK,IAAI;AAAA,IACpB,MAAM,EAAE,SAAS,yBAAI,QAAQ;AAAA,EAC/B,CAAC;AACH;AACO,SAAS,sBAAsB;AACpC,SAAO;AACT;AACO,SAAS,uBAAuB,YAAY,MAAM;AACvD,MAAI,CAAC,KAAM;AAGX,QAAM,QAAQ,KAAK,gBAAgB,UAAU,IAAI;AACjD,QAAM,cAAc,MAAM,cAAc,IAAI,UAAU,EAAE;AACxD,MAAI,eAAe,YAAY;AAC7B,gBAAY,WAAW,YAAY,WAAW;AAChD,QAAM,YAAY,MAAM,cAAc,IAAI,QAAQ,EAAE;AACpD,MAAI,aAAa,UAAU;AACzB,cAAU,WAAW,YAAY,SAAS;AAG5C,MAAI;AACF,UAAM,gBAAgB,MAAM;AAAA,MAC1B,yBAAyB,iBAAiB,QAAQ;AAAA,IACpD;AACA,kBAAc,QAAQ,CAAC,OAAO;AAC5B,UAAI;AACF,YAAI,cAAc,SAAS;AACzB,cAAI,GAAG,aAAa,iBAAiB;AACnC,eAAG,gBAAgB,iBAAiB;AACtC,cAAI,GAAG,aAAa,UAAU,EAAG,IAAG,gBAAgB,UAAU;AAC9D,aAAG,UAAU,OAAO,gBAAgB,YAAY;AAAA,QAClD;AAAA,MACF,SAAS,GAAG;AAAA,MAEZ;AAAA,IACF,CAAC;AAAA,EACH,SAAS,GAAG;AAAA,EAEZ;AAKA,MAAI;AACF,UAAM,UAAU,MAAM;AAAA,MACpB,MAAM,iBAAoC,QAAQ;AAAA,IACpD;AACA,YAAQ,QAAQ,CAAC,MAAM;AA/F3B;AAgGM,UAAI;AACF,cAAM,OAAO,EAAE,eAAe;AAC9B,cAAM,QAAgC,CAAC;AACvC,cAAM,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,MAAO,MAAM,EAAE,IAAI,IAAI,EAAE,KAAM;AACjE,cAAM,cAAc,MAAM,cAAe,cAAc,MAAM;AAE7D,YAAI;AAEF,gBAAM,OACJ,OAAO,SAAS,cACZ,KAAK,SAAS,mBAAmB,IAAI,CAAC,CAAC,IACvC,mBAAmB,IAAI;AAC7B,sBAAY,aAAa,mBAAmB,IAAI;AAAA,QAClD,SAAS,GAAG;AACV,sBAAY,aAAa,mBAAmB,mBAAmB,IAAI,CAAC;AAAA,QACtE;AACA,YAAI,OAAO,KAAK,KAAK,EAAE,QAAQ;AAC7B,sBAAY;AAAA,YACV;AAAA,YACA,mBAAmB,KAAK,UAAU,KAAK,CAAC;AAAA,UAC1C;AAAA,QACF;AAEA,cAAM,eAAe,EAAE,QAAQ,eAAe;AAC9C,YAAI,gBAAgB,aAAa,aAAa,aAAa,GAAG;AAC5D,sBAAY;AAAA,YACV;AAAA,YACA,aAAa,aAAa,aAAa;AAAA,UACzC;AAAA,QACF,OAAO;AACL,sBAAY,aAAa,0BAA0B,MAAM;AAAA,QAC3D;AACA,gBAAE,eAAF,mBAAc,aAAa,aAAa;AAAA,MAC1C,SAAS,GAAG;AAAA,MAEZ;AAAA,IACF,CAAC;AAAA,EACH,SAAS,GAAG;AAAA,EAEZ;AACA,QAAM,UAAU,MAAM;AACtB,QAAM,OAAO,aAAa,SAAS,IAAI;AACvC,MAAI,CAAC,WAAW,UAAU,WAAW,WAAW,SAAS,CAAC,MAAM,MAAM;AACpE,eAAW,KAAK,IAAI;AACpB,QAAI,WAAW,SAAS,cAAe,YAAW,MAAM;AACxD,uBAAmB,KAAK;AAAA,MACtB,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB,MAAM,EAAE,YAAY,KAAK,OAAO;AAAA,IAClC,CAAC;AAAA,EACH;AACA,MAAI,WAAW;AACb,iBAAa,CAAC;AACd,uBAAmB,KAAK;AAAA,MACtB,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB,MAAM,EAAE,SAAS,MAAM;AAAA,IACzB,CAAC;AAAA,EACH;AACF;AACO,SAAS,gBAAgB,MAAc;AAC5C,kBAAgB,KAAK,IAAI,GAAG,IAAI;AAClC;","names":[]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public TypeScript types and interfaces for rich-html-editor
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The state of text formatting in the current selection
|
|
6
|
+
*/
|
|
7
|
+
interface FormatState {
|
|
8
|
+
/** Whether the selection contains bold text */
|
|
9
|
+
bold: boolean;
|
|
10
|
+
/** Whether the selection contains italic text */
|
|
11
|
+
italic: boolean;
|
|
12
|
+
/** Whether the selection contains underlined text */
|
|
13
|
+
underline: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Toolbar configuration and callback options
|
|
17
|
+
*/
|
|
18
|
+
interface ToolbarOptions {
|
|
19
|
+
/** Callback when a formatting command is executed */
|
|
20
|
+
onCommand: (command: string, value?: string) => void;
|
|
21
|
+
/** Function to determine if undo is available */
|
|
22
|
+
canUndo: () => boolean;
|
|
23
|
+
/** Function to determine if redo is available */
|
|
24
|
+
canRedo: () => boolean;
|
|
25
|
+
/** Callback when undo button is clicked */
|
|
26
|
+
onUndo: () => void;
|
|
27
|
+
/** Callback when redo button is clicked */
|
|
28
|
+
onRedo: () => void;
|
|
29
|
+
/** Function to get current formatting state */
|
|
30
|
+
getFormatState: () => FormatState;
|
|
31
|
+
/** Function to get information about selected element */
|
|
32
|
+
getSelectedElementInfo: () => string | null;
|
|
33
|
+
}
|
|
34
|
+
type EditorEventType = "contentChanged" | "undoStateChanged" | "redoStateChanged" | "selectionChanged" | "formatStateChanged" | "error";
|
|
35
|
+
interface EditorEvent {
|
|
36
|
+
type: EditorEventType;
|
|
37
|
+
timestamp: number;
|
|
38
|
+
data?: unknown;
|
|
39
|
+
}
|
|
40
|
+
type EditorEventHandler = (event: EditorEvent) => void;
|
|
41
|
+
type FormattingCommand = "bold" | "italic" | "underline" | "fontName" | "fontSize" | "link" | "foreColor" | "hiliteColor" | "align" | "undo" | "redo";
|
|
42
|
+
type TextAlignment = "left" | "center" | "right" | "justify";
|
|
43
|
+
interface FontSizeMap {
|
|
44
|
+
[key: string]: string;
|
|
45
|
+
}
|
|
46
|
+
interface EditorConfig {
|
|
47
|
+
maxStackSize?: number;
|
|
48
|
+
enableEvents?: boolean;
|
|
49
|
+
toolbarOptions?: Partial<ToolbarOptions>;
|
|
50
|
+
keyboardShortcuts?: boolean | {
|
|
51
|
+
mapping?: Partial<Record<FormattingCommand | "undo" | "redo", string>>;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Initialize the rich HTML editor on an iframe element
|
|
57
|
+
*/
|
|
58
|
+
declare function initRichEditor(iframe: HTMLIFrameElement, config?: EditorConfig): void;
|
|
59
|
+
declare function getCleanHTML(): string;
|
|
60
|
+
|
|
61
|
+
declare class EditorEventEmitter {
|
|
62
|
+
private listeners;
|
|
63
|
+
on(type: EditorEventType, handler: EditorEventHandler): () => void;
|
|
64
|
+
once(type: EditorEventType, handler: EditorEventHandler): void;
|
|
65
|
+
off(type: EditorEventType, handler: EditorEventHandler): void;
|
|
66
|
+
emit(event: EditorEvent): void;
|
|
67
|
+
removeAllListeners(type?: EditorEventType): void;
|
|
68
|
+
listenerCount(type: EditorEventType): number;
|
|
69
|
+
}
|
|
70
|
+
declare const editorEventEmitter: EditorEventEmitter;
|
|
71
|
+
declare function getEditorEventEmitter(): EditorEventEmitter;
|
|
72
|
+
|
|
73
|
+
export { type EditorConfig, type EditorEvent, EditorEventEmitter, type EditorEventHandler, type EditorEventType, type FontSizeMap, type FormatState, type FormattingCommand, type TextAlignment, type ToolbarOptions, editorEventEmitter, getCleanHTML, getEditorEventEmitter, initRichEditor };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public TypeScript types and interfaces for rich-html-editor
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The state of text formatting in the current selection
|
|
6
|
+
*/
|
|
7
|
+
interface FormatState {
|
|
8
|
+
/** Whether the selection contains bold text */
|
|
9
|
+
bold: boolean;
|
|
10
|
+
/** Whether the selection contains italic text */
|
|
11
|
+
italic: boolean;
|
|
12
|
+
/** Whether the selection contains underlined text */
|
|
13
|
+
underline: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Toolbar configuration and callback options
|
|
17
|
+
*/
|
|
18
|
+
interface ToolbarOptions {
|
|
19
|
+
/** Callback when a formatting command is executed */
|
|
20
|
+
onCommand: (command: string, value?: string) => void;
|
|
21
|
+
/** Function to determine if undo is available */
|
|
22
|
+
canUndo: () => boolean;
|
|
23
|
+
/** Function to determine if redo is available */
|
|
24
|
+
canRedo: () => boolean;
|
|
25
|
+
/** Callback when undo button is clicked */
|
|
26
|
+
onUndo: () => void;
|
|
27
|
+
/** Callback when redo button is clicked */
|
|
28
|
+
onRedo: () => void;
|
|
29
|
+
/** Function to get current formatting state */
|
|
30
|
+
getFormatState: () => FormatState;
|
|
31
|
+
/** Function to get information about selected element */
|
|
32
|
+
getSelectedElementInfo: () => string | null;
|
|
33
|
+
}
|
|
34
|
+
type EditorEventType = "contentChanged" | "undoStateChanged" | "redoStateChanged" | "selectionChanged" | "formatStateChanged" | "error";
|
|
35
|
+
interface EditorEvent {
|
|
36
|
+
type: EditorEventType;
|
|
37
|
+
timestamp: number;
|
|
38
|
+
data?: unknown;
|
|
39
|
+
}
|
|
40
|
+
type EditorEventHandler = (event: EditorEvent) => void;
|
|
41
|
+
type FormattingCommand = "bold" | "italic" | "underline" | "fontName" | "fontSize" | "link" | "foreColor" | "hiliteColor" | "align" | "undo" | "redo";
|
|
42
|
+
type TextAlignment = "left" | "center" | "right" | "justify";
|
|
43
|
+
interface FontSizeMap {
|
|
44
|
+
[key: string]: string;
|
|
45
|
+
}
|
|
46
|
+
interface EditorConfig {
|
|
47
|
+
maxStackSize?: number;
|
|
48
|
+
enableEvents?: boolean;
|
|
49
|
+
toolbarOptions?: Partial<ToolbarOptions>;
|
|
50
|
+
keyboardShortcuts?: boolean | {
|
|
51
|
+
mapping?: Partial<Record<FormattingCommand | "undo" | "redo", string>>;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Initialize the rich HTML editor on an iframe element
|
|
57
|
+
*/
|
|
58
|
+
declare function initRichEditor(iframe: HTMLIFrameElement, config?: EditorConfig): void;
|
|
59
|
+
declare function getCleanHTML(): string;
|
|
60
|
+
|
|
61
|
+
declare class EditorEventEmitter {
|
|
62
|
+
private listeners;
|
|
63
|
+
on(type: EditorEventType, handler: EditorEventHandler): () => void;
|
|
64
|
+
once(type: EditorEventType, handler: EditorEventHandler): void;
|
|
65
|
+
off(type: EditorEventType, handler: EditorEventHandler): void;
|
|
66
|
+
emit(event: EditorEvent): void;
|
|
67
|
+
removeAllListeners(type?: EditorEventType): void;
|
|
68
|
+
listenerCount(type: EditorEventType): number;
|
|
69
|
+
}
|
|
70
|
+
declare const editorEventEmitter: EditorEventEmitter;
|
|
71
|
+
declare function getEditorEventEmitter(): EditorEventEmitter;
|
|
72
|
+
|
|
73
|
+
export { type EditorConfig, type EditorEvent, EditorEventEmitter, type EditorEventHandler, type EditorEventType, type FontSizeMap, type FormatState, type FormattingCommand, type TextAlignment, type ToolbarOptions, editorEventEmitter, getCleanHTML, getEditorEventEmitter, initRichEditor };
|