@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.
Files changed (75) hide show
  1. package/README.md +269 -0
  2. package/dist/components/Dropdown.d.ts +19 -0
  3. package/dist/components/Dropdown.d.ts.map +1 -0
  4. package/dist/components/Editor.d.ts +4 -0
  5. package/dist/components/Editor.d.ts.map +1 -0
  6. package/dist/components/FloatingToolbar.d.ts +10 -0
  7. package/dist/components/FloatingToolbar.d.ts.map +1 -0
  8. package/dist/components/IconWrapper.d.ts +10 -0
  9. package/dist/components/IconWrapper.d.ts.map +1 -0
  10. package/dist/components/Icons.d.ts +32 -0
  11. package/dist/components/Icons.d.ts.map +1 -0
  12. package/dist/components/Toolbar.d.ts +10 -0
  13. package/dist/components/Toolbar.d.ts.map +1 -0
  14. package/dist/components/index.d.ts +3 -0
  15. package/dist/components/index.d.ts.map +1 -0
  16. package/dist/index.d.ts +208 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.esm.js +2080 -0
  19. package/dist/index.esm.js.map +1 -0
  20. package/dist/index.js +2116 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/plugins/base.d.ts +10 -0
  23. package/dist/plugins/base.d.ts.map +1 -0
  24. package/dist/plugins/clearFormatting.d.ts +6 -0
  25. package/dist/plugins/clearFormatting.d.ts.map +1 -0
  26. package/dist/plugins/colors.d.ts +4 -0
  27. package/dist/plugins/colors.d.ts.map +1 -0
  28. package/dist/plugins/fontSize.d.ts +3 -0
  29. package/dist/plugins/fontSize.d.ts.map +1 -0
  30. package/dist/plugins/headings.d.ts +3 -0
  31. package/dist/plugins/headings.d.ts.map +1 -0
  32. package/dist/plugins/image.d.ts +6 -0
  33. package/dist/plugins/image.d.ts.map +1 -0
  34. package/dist/plugins/index.d.ts +14 -0
  35. package/dist/plugins/index.d.ts.map +1 -0
  36. package/dist/plugins/optional.d.ts +19 -0
  37. package/dist/plugins/optional.d.ts.map +1 -0
  38. package/dist/styles.css +638 -0
  39. package/dist/types.d.ts +81 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/utils/clearFormatting.d.ts +21 -0
  42. package/dist/utils/clearFormatting.d.ts.map +1 -0
  43. package/dist/utils/content.d.ts +12 -0
  44. package/dist/utils/content.d.ts.map +1 -0
  45. package/dist/utils/history.d.ts +14 -0
  46. package/dist/utils/history.d.ts.map +1 -0
  47. package/dist/utils/listIndent.d.ts +9 -0
  48. package/dist/utils/listIndent.d.ts.map +1 -0
  49. package/dist/utils/stateReflection.d.ts +18 -0
  50. package/dist/utils/stateReflection.d.ts.map +1 -0
  51. package/package.json +48 -0
  52. package/src/components/Dropdown.tsx +103 -0
  53. package/src/components/Editor.css +2 -0
  54. package/src/components/Editor.tsx +785 -0
  55. package/src/components/FloatingToolbar.tsx +214 -0
  56. package/src/components/IconWrapper.tsx +14 -0
  57. package/src/components/Icons.tsx +145 -0
  58. package/src/components/Toolbar.tsx +137 -0
  59. package/src/components/index.ts +3 -0
  60. package/src/index.ts +19 -0
  61. package/src/plugins/base.tsx +91 -0
  62. package/src/plugins/clearFormatting.tsx +31 -0
  63. package/src/plugins/colors.tsx +122 -0
  64. package/src/plugins/fontSize.tsx +81 -0
  65. package/src/plugins/headings.tsx +76 -0
  66. package/src/plugins/image.tsx +189 -0
  67. package/src/plugins/index.ts +54 -0
  68. package/src/plugins/optional.tsx +221 -0
  69. package/src/styles.css +638 -0
  70. package/src/types.ts +92 -0
  71. package/src/utils/clearFormatting.ts +244 -0
  72. package/src/utils/content.ts +290 -0
  73. package/src/utils/history.ts +59 -0
  74. package/src/utils/listIndent.ts +171 -0
  75. 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
+