@overlap/rte 0.1.1 → 0.1.2

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