@alpaca-editor/core 1.0.4045 → 1.0.4048
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/field-types/RichTextEditorComponent.js +3 -10
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.js +300 -342
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/components/SimpleToolbar.js +9 -9
- package/dist/editor/field-types/richtext/components/SimpleToolbar.js.map +1 -1
- package/dist/editor/field-types/richtext/config/pluginFactory.d.ts +7 -6
- package/dist/editor/field-types/richtext/config/pluginFactory.js +2 -1
- package/dist/editor/field-types/richtext/config/pluginFactory.js.map +1 -1
- package/dist/editor/field-types/richtext/hooks/useProfileCache.js +24 -18
- package/dist/editor/field-types/richtext/hooks/useProfileCache.js.map +1 -1
- package/dist/editor/field-types/richtext/hooks/useRichTextProfile.js +1 -1
- package/dist/editor/field-types/richtext/hooks/useRichTextProfile.js.map +1 -1
- package/dist/editor/field-types/richtext/types.d.ts +236 -90
- package/dist/editor/field-types/richtext/types.js +3 -3
- package/dist/editor/field-types/richtext/types.js.map +1 -1
- package/dist/editor/field-types/richtext/utils/conversion.d.ts +4 -2
- package/dist/editor/field-types/richtext/utils/conversion.js +79 -12
- package/dist/editor/field-types/richtext/utils/conversion.js.map +1 -1
- package/dist/editor/field-types/richtext/utils/plugins.d.ts +66 -39
- package/dist/editor/field-types/richtext/utils/plugins.js +377 -233
- package/dist/editor/field-types/richtext/utils/plugins.js.map +1 -1
- package/dist/editor/field-types/richtext/utils/profileMapper.js +22 -2
- package/dist/editor/field-types/richtext/utils/profileMapper.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/package.json +1 -1
- package/src/editor/field-types/RichTextEditorComponent.tsx +4 -10
- package/src/editor/field-types/richtext/components/ReactSlate.css +85 -24
- package/src/editor/field-types/richtext/components/ReactSlate.tsx +375 -428
- package/src/editor/field-types/richtext/components/SimpleRichTextEditor.tsx +4 -2
- package/src/editor/field-types/richtext/components/SimpleToolbar.tsx +3 -3
- package/src/editor/field-types/richtext/config/pluginFactory.tsx +2 -1
- package/src/editor/field-types/richtext/hooks/useProfileCache.ts +25 -19
- package/src/editor/field-types/richtext/hooks/useRichTextProfile.ts +1 -1
- package/src/editor/field-types/richtext/types.ts +150 -112
- package/src/editor/field-types/richtext/utils/conversion.ts +100 -27
- package/src/editor/field-types/richtext/utils/plugins.ts +469 -268
- package/src/editor/field-types/richtext/utils/profileMapper.ts +26 -3
- package/src/revision.ts +2 -2
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { Descendant, Element as SlateElement } from 'slate';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
CustomText,
|
|
4
|
+
Alignment,
|
|
5
|
+
CustomElement,
|
|
6
|
+
LinkElement,
|
|
7
|
+
SimplifiedProfile,
|
|
8
|
+
SLATE_MARKS,
|
|
9
|
+
SLATE_BLOCKS,
|
|
10
|
+
MarkId,
|
|
11
|
+
BlockId,
|
|
12
|
+
HtmlToSlateResult,
|
|
13
|
+
SlateToHtmlResult
|
|
14
|
+
} from '../types';
|
|
3
15
|
|
|
4
16
|
// Elements that should be completely preserved as raw HTML
|
|
5
17
|
const PRESERVE_AS_RAW_ELEMENTS = new Set([
|
|
@@ -10,8 +22,8 @@ const PRESERVE_AS_RAW_ELEMENTS = new Set([
|
|
|
10
22
|
'script', 'style', 'noscript'
|
|
11
23
|
]);
|
|
12
24
|
|
|
13
|
-
// Shared utility: Get default block type from profile
|
|
14
|
-
const getDefaultBlockType = (blocks:
|
|
25
|
+
// Shared utility: Get default block type from profile with strict typing
|
|
26
|
+
const getDefaultBlockType = (blocks: readonly BlockId[]): BlockId => {
|
|
15
27
|
return blocks.length > 0 && blocks[0] ? blocks[0] : 'paragraph';
|
|
16
28
|
};
|
|
17
29
|
|
|
@@ -43,16 +55,16 @@ const shouldPreserveAsRaw = (element: HTMLElement): boolean => {
|
|
|
43
55
|
// Check for complex styling that would be hard to preserve
|
|
44
56
|
const style = element.getAttribute('style') || '';
|
|
45
57
|
const hasComplexStyling = style.includes('background-color') ||
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
style.includes('font-family') ||
|
|
59
|
+
style.includes('font-size') ||
|
|
60
|
+
style.includes('color:');
|
|
49
61
|
|
|
50
62
|
return hasComplexStyling && element.children.length > 0;
|
|
51
63
|
};
|
|
52
64
|
|
|
53
|
-
// Shared utility: Process marks from element
|
|
54
|
-
const getMarksFromElement = (element: HTMLElement, configuredMarks:
|
|
55
|
-
const marks: Record<
|
|
65
|
+
// Shared utility: Process marks from element with strict typing
|
|
66
|
+
const getMarksFromElement = (element: HTMLElement, configuredMarks: readonly MarkId[]): Record<MarkId, boolean> => {
|
|
67
|
+
const marks: Partial<Record<MarkId, boolean>> = {};
|
|
56
68
|
|
|
57
69
|
// Check for HTML tag-based marks
|
|
58
70
|
const tagName = element.tagName.toLowerCase();
|
|
@@ -69,23 +81,50 @@ const getMarksFromElement = (element: HTMLElement, configuredMarks: string[]): R
|
|
|
69
81
|
const style = element.getAttribute('style');
|
|
70
82
|
if (style) {
|
|
71
83
|
if (style.includes('font-weight: bold') || style.includes('font-weight:bold')) {
|
|
72
|
-
marks['bold'] = true;
|
|
84
|
+
if (configuredMarks.includes('bold')) marks['bold'] = true;
|
|
73
85
|
}
|
|
74
86
|
if (style.includes('font-style: italic') || style.includes('font-style:italic')) {
|
|
75
|
-
marks['italic'] = true;
|
|
87
|
+
if (configuredMarks.includes('italic')) marks['italic'] = true;
|
|
76
88
|
}
|
|
77
89
|
if (style.includes('text-decoration: underline') || style.includes('text-decoration:underline')) {
|
|
78
|
-
marks['underline'] = true;
|
|
90
|
+
if (configuredMarks.includes('underline')) marks['underline'] = true;
|
|
79
91
|
}
|
|
80
92
|
if (style.includes('text-decoration: line-through') || style.includes('text-decoration:line-through')) {
|
|
81
|
-
marks['strikethrough'] = true;
|
|
93
|
+
if (configuredMarks.includes('strikethrough')) marks['strikethrough'] = true;
|
|
82
94
|
}
|
|
83
95
|
}
|
|
84
96
|
|
|
85
|
-
return marks
|
|
97
|
+
return marks as Record<MarkId, boolean>;
|
|
86
98
|
};
|
|
87
99
|
|
|
100
|
+
// Enhanced htmlToSlate with better error handling and strict typing
|
|
88
101
|
export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendant[] => {
|
|
102
|
+
try {
|
|
103
|
+
return htmlToSlateInternal(html, profile);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.warn("HTML to Slate conversion failed:", error);
|
|
106
|
+
const defaultBlockType = getDefaultBlockType(profile.blocks);
|
|
107
|
+
return [{ type: defaultBlockType, children: [{ text: '' }] }];
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Enhanced htmlToSlate with result type for better error handling
|
|
112
|
+
export const htmlToSlateWithResult = (html: string, profile: SimplifiedProfile): HtmlToSlateResult => {
|
|
113
|
+
try {
|
|
114
|
+
const data = htmlToSlateInternal(html, profile);
|
|
115
|
+
return { success: true, data };
|
|
116
|
+
} catch (error) {
|
|
117
|
+
const defaultBlockType = getDefaultBlockType(profile.blocks);
|
|
118
|
+
const fallback = [{ type: defaultBlockType, children: [{ text: '' }] }];
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
122
|
+
fallback
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const htmlToSlateInternal = (html: string, profile: SimplifiedProfile): Descendant[] => {
|
|
89
128
|
const defaultBlockType = getDefaultBlockType(profile.blocks);
|
|
90
129
|
|
|
91
130
|
if (!html || html === '<p><br></p>') {
|
|
@@ -97,8 +136,8 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
|
|
|
97
136
|
|
|
98
137
|
const nodes: Descendant[] = [];
|
|
99
138
|
|
|
100
|
-
// Determine block type for element
|
|
101
|
-
const getBlockTypeForElement = (element: HTMLElement):
|
|
139
|
+
// Determine block type for element with strict typing
|
|
140
|
+
const getBlockTypeForElement = (element: HTMLElement): BlockId => {
|
|
102
141
|
const tagName = element.tagName.toLowerCase();
|
|
103
142
|
|
|
104
143
|
// Check if we have a configured block for this element
|
|
@@ -144,8 +183,8 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
|
|
|
144
183
|
});
|
|
145
184
|
};
|
|
146
185
|
|
|
147
|
-
// Process inline elements and text, excluding nested lists
|
|
148
|
-
const processNodeWithInlinesExcludingNestedLists = (element: HTMLElement, configuredMarks:
|
|
186
|
+
// Process inline elements and text, excluding nested lists - with strict typing
|
|
187
|
+
const processNodeWithInlinesExcludingNestedLists = (element: HTMLElement, configuredMarks: readonly MarkId[]): (CustomText | CustomElement)[] => {
|
|
149
188
|
const results: (CustomText | CustomElement)[] = [];
|
|
150
189
|
const currentElementMarks = getMarksFromElement(element, configuredMarks);
|
|
151
190
|
|
|
@@ -216,8 +255,8 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
|
|
|
216
255
|
return results;
|
|
217
256
|
};
|
|
218
257
|
|
|
219
|
-
// Process inline elements and text
|
|
220
|
-
const processNodeWithInlines = (element: HTMLElement, configuredMarks:
|
|
258
|
+
// Process inline elements and text - with strict typing
|
|
259
|
+
const processNodeWithInlines = (element: HTMLElement, configuredMarks: readonly MarkId[]): (CustomText | CustomElement)[] => {
|
|
221
260
|
const results: (CustomText | CustomElement)[] = [];
|
|
222
261
|
const currentElementMarks = getMarksFromElement(element, configuredMarks);
|
|
223
262
|
|
|
@@ -336,6 +375,12 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
|
|
|
336
375
|
type: defaultBlockType,
|
|
337
376
|
children: [{ text: '<br>' }]
|
|
338
377
|
});
|
|
378
|
+
} else if (tagName === 'hr') {
|
|
379
|
+
// Handle horizontal rule elements
|
|
380
|
+
nodes.push({
|
|
381
|
+
type: 'horizontal-rule',
|
|
382
|
+
children: [{ text: '' }]
|
|
383
|
+
});
|
|
339
384
|
} else if (tagName === 'ul' || tagName === 'ol') {
|
|
340
385
|
if (profile.lists) {
|
|
341
386
|
processListElement(element);
|
|
@@ -394,7 +439,8 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
|
|
|
394
439
|
return nodes.length > 0 ? nodes : [{ type: defaultBlockType, children: [{ text: '' }] }];
|
|
395
440
|
};
|
|
396
441
|
|
|
397
|
-
|
|
442
|
+
// Enhanced formatTextWithMarks with strict typing
|
|
443
|
+
export const formatTextWithMarks = (child: CustomText, configuredMarks: readonly MarkId[]): string => {
|
|
398
444
|
// If this is raw HTML, return it as-is with unescaping
|
|
399
445
|
if (child.isRawHtml) {
|
|
400
446
|
return unescapeHtmlEntities(child.text);
|
|
@@ -405,7 +451,7 @@ export const formatTextWithMarks = (child: CustomText, configuredMarks: string[]
|
|
|
405
451
|
// Convert literal <br> text back to <br> tags
|
|
406
452
|
text = text.replace(/<br>/g, '<br>');
|
|
407
453
|
|
|
408
|
-
// Apply marks using Slate's built-in mark system
|
|
454
|
+
// Apply marks using Slate's built-in mark system with strict typing
|
|
409
455
|
configuredMarks.forEach(markId => {
|
|
410
456
|
const markConfig = SLATE_MARKS[markId];
|
|
411
457
|
if (markConfig && (child as any)[markId]) {
|
|
@@ -416,7 +462,31 @@ export const formatTextWithMarks = (child: CustomText, configuredMarks: string[]
|
|
|
416
462
|
return text;
|
|
417
463
|
};
|
|
418
464
|
|
|
465
|
+
// Enhanced slateToHtml with better error handling
|
|
419
466
|
export const slateToHtml = (value: Descendant[], profile: SimplifiedProfile): string => {
|
|
467
|
+
try {
|
|
468
|
+
return slateToHtmlInternal(value, profile);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
console.warn("Slate to HTML conversion failed:", error);
|
|
471
|
+
return '';
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// Enhanced slateToHtml with result type for better error handling
|
|
476
|
+
export const slateToHtmlWithResult = (value: Descendant[], profile: SimplifiedProfile): SlateToHtmlResult => {
|
|
477
|
+
try {
|
|
478
|
+
const data = slateToHtmlInternal(value, profile);
|
|
479
|
+
return { success: true, data };
|
|
480
|
+
} catch (error) {
|
|
481
|
+
return {
|
|
482
|
+
success: false,
|
|
483
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
484
|
+
fallback: ''
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const slateToHtmlInternal = (value: Descendant[], profile: SimplifiedProfile): string => {
|
|
420
490
|
let html = '';
|
|
421
491
|
let i = 0;
|
|
422
492
|
|
|
@@ -487,8 +557,8 @@ export const slateToHtml = (value: Descendant[], profile: SimplifiedProfile): st
|
|
|
487
557
|
return { html: listHtml, nextIndex: currentIndex };
|
|
488
558
|
};
|
|
489
559
|
|
|
490
|
-
// Process children with inline elements
|
|
491
|
-
const processChildrenWithInlines = (children: (CustomText | CustomElement)[], configuredMarks:
|
|
560
|
+
// Process children with inline elements - with strict typing
|
|
561
|
+
const processChildrenWithInlines = (children: (CustomText | CustomElement)[], configuredMarks: readonly MarkId[]): string => {
|
|
492
562
|
let result = '';
|
|
493
563
|
|
|
494
564
|
children.forEach(child => {
|
|
@@ -546,14 +616,17 @@ export const slateToHtml = (value: Descendant[], profile: SimplifiedProfile): st
|
|
|
546
616
|
|
|
547
617
|
const alignStyle = element.align ? ` style="text-align: ${element.align};"` : '';
|
|
548
618
|
|
|
549
|
-
|
|
619
|
+
// Handle horizontal rule elements
|
|
620
|
+
if (element.type === 'horizontal-rule') {
|
|
621
|
+
html += '<hr>';
|
|
622
|
+
} else if (element.type === 'preserved-element') {
|
|
550
623
|
// Handle preserved elements by restoring their original HTML
|
|
551
624
|
const preservedElement = element as any;
|
|
552
625
|
html += preservedElement.originalHtml;
|
|
553
626
|
} else {
|
|
554
|
-
const blockConfig = SLATE_BLOCKS[element.type];
|
|
627
|
+
const blockConfig = SLATE_BLOCKS[element.type as BlockId];
|
|
555
628
|
|
|
556
|
-
if (blockConfig && profile.blocks.includes(element.type) && element.children) {
|
|
629
|
+
if (blockConfig && profile.blocks.includes(element.type as BlockId) && element.children) {
|
|
557
630
|
const processedChildren = processChildrenWithInlines(element.children as CustomText[], profile.marks);
|
|
558
631
|
html += blockConfig.renderHtml(processedChildren, alignStyle);
|
|
559
632
|
} else {
|