@overlap/rte 0.1.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/README.md +269 -0
- package/dist/components/Dropdown.d.ts +19 -0
- package/dist/components/Dropdown.d.ts.map +1 -0
- package/dist/components/Editor.d.ts +4 -0
- package/dist/components/Editor.d.ts.map +1 -0
- package/dist/components/FloatingToolbar.d.ts +10 -0
- package/dist/components/FloatingToolbar.d.ts.map +1 -0
- package/dist/components/IconWrapper.d.ts +10 -0
- package/dist/components/IconWrapper.d.ts.map +1 -0
- package/dist/components/Icons.d.ts +32 -0
- package/dist/components/Icons.d.ts.map +1 -0
- package/dist/components/Toolbar.d.ts +10 -0
- package/dist/components/Toolbar.d.ts.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/index.d.ts +208 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +2080 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +2116 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/base.d.ts +10 -0
- package/dist/plugins/base.d.ts.map +1 -0
- package/dist/plugins/clearFormatting.d.ts +6 -0
- package/dist/plugins/clearFormatting.d.ts.map +1 -0
- package/dist/plugins/colors.d.ts +4 -0
- package/dist/plugins/colors.d.ts.map +1 -0
- package/dist/plugins/fontSize.d.ts +3 -0
- package/dist/plugins/fontSize.d.ts.map +1 -0
- package/dist/plugins/headings.d.ts +3 -0
- package/dist/plugins/headings.d.ts.map +1 -0
- package/dist/plugins/image.d.ts +6 -0
- package/dist/plugins/image.d.ts.map +1 -0
- package/dist/plugins/index.d.ts +14 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/optional.d.ts +19 -0
- package/dist/plugins/optional.d.ts.map +1 -0
- package/dist/styles.css +638 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/clearFormatting.d.ts +21 -0
- package/dist/utils/clearFormatting.d.ts.map +1 -0
- package/dist/utils/content.d.ts +12 -0
- package/dist/utils/content.d.ts.map +1 -0
- package/dist/utils/history.d.ts +14 -0
- package/dist/utils/history.d.ts.map +1 -0
- package/dist/utils/listIndent.d.ts +9 -0
- package/dist/utils/listIndent.d.ts.map +1 -0
- package/dist/utils/stateReflection.d.ts +18 -0
- package/dist/utils/stateReflection.d.ts.map +1 -0
- package/package.json +48 -0
- package/src/components/Dropdown.tsx +103 -0
- package/src/components/Editor.css +2 -0
- package/src/components/Editor.tsx +785 -0
- package/src/components/FloatingToolbar.tsx +214 -0
- package/src/components/IconWrapper.tsx +14 -0
- package/src/components/Icons.tsx +145 -0
- package/src/components/Toolbar.tsx +137 -0
- package/src/components/index.ts +3 -0
- package/src/index.ts +19 -0
- package/src/plugins/base.tsx +91 -0
- package/src/plugins/clearFormatting.tsx +31 -0
- package/src/plugins/colors.tsx +122 -0
- package/src/plugins/fontSize.tsx +81 -0
- package/src/plugins/headings.tsx +76 -0
- package/src/plugins/image.tsx +189 -0
- package/src/plugins/index.ts +54 -0
- package/src/plugins/optional.tsx +221 -0
- package/src/styles.css +638 -0
- package/src/types.ts +92 -0
- package/src/utils/clearFormatting.ts +244 -0
- package/src/utils/content.ts +290 -0
- package/src/utils/history.ts +59 -0
- package/src/utils/listIndent.ts +171 -0
- package/src/utils/stateReflection.ts +175 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Plugin, EditorAPI, ButtonProps } from '../types';
|
|
3
|
+
import { IconWrapper } from '../components/IconWrapper';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Link-Plugin mit verbesserter Funktionalität
|
|
7
|
+
*/
|
|
8
|
+
export function createLinkPlugin(): Plugin {
|
|
9
|
+
return {
|
|
10
|
+
name: 'link',
|
|
11
|
+
type: 'inline',
|
|
12
|
+
command: 'createLink',
|
|
13
|
+
renderButton: (props: ButtonProps) => (
|
|
14
|
+
<button
|
|
15
|
+
type="button"
|
|
16
|
+
onClick={props.onClick}
|
|
17
|
+
disabled={props.disabled}
|
|
18
|
+
className={`rte-toolbar-button ${props.isActive ? 'rte-toolbar-button-active' : ''}`}
|
|
19
|
+
title="Link einfügen"
|
|
20
|
+
aria-label="Link einfügen"
|
|
21
|
+
>
|
|
22
|
+
<IconWrapper icon="mdi:link" width={18} height={18} />
|
|
23
|
+
</button>
|
|
24
|
+
),
|
|
25
|
+
execute: (editor: EditorAPI) => {
|
|
26
|
+
const selection = editor.getSelection();
|
|
27
|
+
if (!selection || selection.rangeCount === 0) return;
|
|
28
|
+
|
|
29
|
+
const range = selection.getRangeAt(0);
|
|
30
|
+
const container = range.commonAncestorContainer;
|
|
31
|
+
const element = container.nodeType === Node.TEXT_NODE
|
|
32
|
+
? container.parentElement
|
|
33
|
+
: container as HTMLElement;
|
|
34
|
+
|
|
35
|
+
// Prüfe ob bereits ein Link vorhanden ist
|
|
36
|
+
const existingLink = element?.closest('a') as HTMLAnchorElement;
|
|
37
|
+
|
|
38
|
+
if (existingLink) {
|
|
39
|
+
// Link entfernen
|
|
40
|
+
const parent = existingLink.parentNode;
|
|
41
|
+
if (parent) {
|
|
42
|
+
while (existingLink.firstChild) {
|
|
43
|
+
parent.insertBefore(existingLink.firstChild, existingLink);
|
|
44
|
+
}
|
|
45
|
+
parent.removeChild(existingLink);
|
|
46
|
+
// Content aktualisieren
|
|
47
|
+
const editorEl = editor.getSelection()?.anchorNode;
|
|
48
|
+
if (editorEl) {
|
|
49
|
+
const content = editor.getContent();
|
|
50
|
+
editor.setContent(content);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
// Neuen Link einfügen
|
|
55
|
+
const url = prompt('URL eingeben:');
|
|
56
|
+
if (url) {
|
|
57
|
+
editor.executeCommand('createLink', url);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
isActive: (editor: EditorAPI) => {
|
|
62
|
+
const selection = editor.getSelection();
|
|
63
|
+
if (!selection || selection.rangeCount === 0) return false;
|
|
64
|
+
|
|
65
|
+
const range = selection.getRangeAt(0);
|
|
66
|
+
const container = range.commonAncestorContainer;
|
|
67
|
+
const element = container.nodeType === Node.TEXT_NODE
|
|
68
|
+
? container.parentElement
|
|
69
|
+
: container as HTMLElement;
|
|
70
|
+
|
|
71
|
+
if (!element) return false;
|
|
72
|
+
|
|
73
|
+
return element.closest('a') !== null;
|
|
74
|
+
},
|
|
75
|
+
getCurrentValue: (editor: EditorAPI) => {
|
|
76
|
+
const selection = editor.getSelection();
|
|
77
|
+
if (!selection || selection.rangeCount === 0) return undefined;
|
|
78
|
+
|
|
79
|
+
const range = selection.getRangeAt(0);
|
|
80
|
+
const container = range.commonAncestorContainer;
|
|
81
|
+
const element = container.nodeType === Node.TEXT_NODE
|
|
82
|
+
? container.parentElement
|
|
83
|
+
: container as HTMLElement;
|
|
84
|
+
|
|
85
|
+
if (!element) return undefined;
|
|
86
|
+
|
|
87
|
+
const link = element.closest('a') as HTMLAnchorElement;
|
|
88
|
+
return link ? link.href : undefined;
|
|
89
|
+
},
|
|
90
|
+
canExecute: (editor: EditorAPI) => {
|
|
91
|
+
const selection = editor.getSelection();
|
|
92
|
+
return selection !== null && selection.rangeCount > 0;
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const linkPlugin = createLinkPlugin();
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Blockquote-Plugin
|
|
101
|
+
*/
|
|
102
|
+
export const blockquotePlugin: Plugin = {
|
|
103
|
+
name: 'blockquote',
|
|
104
|
+
type: 'block',
|
|
105
|
+
command: 'formatBlock',
|
|
106
|
+
renderButton: (props: ButtonProps) => (
|
|
107
|
+
<button
|
|
108
|
+
type="button"
|
|
109
|
+
onClick={props.onClick}
|
|
110
|
+
disabled={props.disabled}
|
|
111
|
+
className={`rte-toolbar-button ${props.isActive ? 'rte-toolbar-button-active' : ''}`}
|
|
112
|
+
title="Zitat"
|
|
113
|
+
aria-label="Zitat"
|
|
114
|
+
>
|
|
115
|
+
<IconWrapper icon="mdi:format-quote-close" width={18} height={18} />
|
|
116
|
+
</button>
|
|
117
|
+
),
|
|
118
|
+
execute: (editor: EditorAPI) => {
|
|
119
|
+
const selection = editor.getSelection();
|
|
120
|
+
if (!selection || selection.rangeCount === 0) return;
|
|
121
|
+
|
|
122
|
+
const range = selection.getRangeAt(0);
|
|
123
|
+
const container = range.commonAncestorContainer;
|
|
124
|
+
const element = container.nodeType === Node.TEXT_NODE
|
|
125
|
+
? container.parentElement
|
|
126
|
+
: container as HTMLElement;
|
|
127
|
+
|
|
128
|
+
if (!element) return;
|
|
129
|
+
|
|
130
|
+
const isBlockquote = element.closest('blockquote') !== null;
|
|
131
|
+
|
|
132
|
+
if (isBlockquote) {
|
|
133
|
+
editor.executeCommand('formatBlock', '<p>');
|
|
134
|
+
} else {
|
|
135
|
+
editor.executeCommand('formatBlock', '<blockquote>');
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
isActive: (editor: EditorAPI) => {
|
|
139
|
+
const selection = editor.getSelection();
|
|
140
|
+
if (!selection || selection.rangeCount === 0) return false;
|
|
141
|
+
|
|
142
|
+
const range = selection.getRangeAt(0);
|
|
143
|
+
const container = range.commonAncestorContainer;
|
|
144
|
+
const element = container.nodeType === Node.TEXT_NODE
|
|
145
|
+
? container.parentElement
|
|
146
|
+
: container as HTMLElement;
|
|
147
|
+
|
|
148
|
+
if (!element) return false;
|
|
149
|
+
|
|
150
|
+
return element.closest('blockquote') !== null;
|
|
151
|
+
},
|
|
152
|
+
canExecute: (editor: EditorAPI) => {
|
|
153
|
+
const selection = editor.getSelection();
|
|
154
|
+
return selection !== null && selection.rangeCount > 0;
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Unordered List Plugin
|
|
160
|
+
*/
|
|
161
|
+
export const unorderedListPlugin: Plugin = {
|
|
162
|
+
name: 'unorderedList',
|
|
163
|
+
type: 'block',
|
|
164
|
+
command: 'insertUnorderedList',
|
|
165
|
+
renderButton: (props: ButtonProps) => (
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
onClick={props.onClick}
|
|
169
|
+
disabled={props.disabled}
|
|
170
|
+
className={`rte-toolbar-button ${props.isActive ? 'rte-toolbar-button-active' : ''}`}
|
|
171
|
+
title="Aufzählungsliste"
|
|
172
|
+
aria-label="Aufzählungsliste"
|
|
173
|
+
>
|
|
174
|
+
<IconWrapper icon="mdi:format-list-bulleted" width={18} height={18} />
|
|
175
|
+
</button>
|
|
176
|
+
),
|
|
177
|
+
execute: (editor: EditorAPI) => {
|
|
178
|
+
editor.executeCommand('insertUnorderedList');
|
|
179
|
+
},
|
|
180
|
+
isActive: (editor: EditorAPI) => {
|
|
181
|
+
if (typeof document === 'undefined') return false;
|
|
182
|
+
return document.queryCommandState('insertUnorderedList');
|
|
183
|
+
},
|
|
184
|
+
canExecute: (editor: EditorAPI) => {
|
|
185
|
+
const selection = editor.getSelection();
|
|
186
|
+
return selection !== null && selection.rangeCount > 0;
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Ordered List Plugin
|
|
192
|
+
*/
|
|
193
|
+
export const orderedListPlugin: Plugin = {
|
|
194
|
+
name: 'orderedList',
|
|
195
|
+
type: 'block',
|
|
196
|
+
command: 'insertOrderedList',
|
|
197
|
+
renderButton: (props: ButtonProps) => (
|
|
198
|
+
<button
|
|
199
|
+
type="button"
|
|
200
|
+
onClick={props.onClick}
|
|
201
|
+
disabled={props.disabled}
|
|
202
|
+
className={`rte-toolbar-button ${props.isActive ? 'rte-toolbar-button-active' : ''}`}
|
|
203
|
+
title="Nummerierte Liste"
|
|
204
|
+
aria-label="Nummerierte Liste"
|
|
205
|
+
>
|
|
206
|
+
<IconWrapper icon="mdi:format-list-numbered" width={18} height={18} />
|
|
207
|
+
</button>
|
|
208
|
+
),
|
|
209
|
+
execute: (editor: EditorAPI) => {
|
|
210
|
+
editor.executeCommand('insertOrderedList');
|
|
211
|
+
},
|
|
212
|
+
isActive: (editor: EditorAPI) => {
|
|
213
|
+
if (typeof document === 'undefined') return false;
|
|
214
|
+
return document.queryCommandState('insertOrderedList');
|
|
215
|
+
},
|
|
216
|
+
canExecute: (editor: EditorAPI) => {
|
|
217
|
+
const selection = editor.getSelection();
|
|
218
|
+
return selection !== null && selection.rangeCount > 0;
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
|