@flexiui/svelte-rich-text 0.0.11 → 0.0.13

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,28 +1,26 @@
1
1
  <script lang="ts">
2
- import { EnhancedLink } from './extensions/EnhancedLink';
2
+ import { exampleJSONContent, HEADINGS } from './utils';
3
3
  import "./styles.css";
4
4
 
5
- import { Color } from "@tiptap/extension-text-style";
6
- import Highlight from "@tiptap/extension-highlight";
7
- import TextAlign from '@tiptap/extension-text-align'
8
- import { ListItem, ListKit } from "@tiptap/extension-list";
9
- import { TextStyle } from "@tiptap/extension-text-style";
10
- import StarterKit from "@tiptap/starter-kit";
11
- // import { Editor } from "@tiptap/core";
12
- import {
13
- BubbleMenu,
5
+ import { onMount, onDestroy } from "svelte";
6
+ import type { Readable } from 'svelte/store';
7
+
8
+ import {
14
9
  createEditor,
15
10
  Editor,
16
11
  EditorContent,
17
12
  } from "svelte-tiptap";
18
- import Link from "@tiptap/extension-link";
19
- import Underline from "@tiptap/extension-underline";
20
- import { onMount, onDestroy } from "svelte";
21
13
 
22
- import { Dropdown, DropdownItem } from "@flexiui/svelte-dropdown";
23
- import type { Readable } from 'svelte/store';
14
+ import StarterKit from "@tiptap/starter-kit";
15
+ import Highlight from "@tiptap/extension-highlight";
16
+ import TextAlign from '@tiptap/extension-text-align'
17
+ import Underline from "@tiptap/extension-underline";
18
+ import Link from "@tiptap/extension-link";
19
+ import { ListKit } from "@tiptap/extension-list";
20
+ import { TextStyleKit } from "@tiptap/extension-text-style";
21
+ import { EnhancedLink } from './extensions/EnhancedLink';
22
+ import { computePosition, offset, autoUpdate } from "@floating-ui/dom";
24
23
 
25
- // const {id, className, editable = false, value = ''} = $props();
26
24
  declare interface Props {
27
25
  id?: string;
28
26
  className?: string;
@@ -67,80 +65,10 @@
67
65
  }
68
66
  }: Props = $props();
69
67
 
70
- const HEADINGS = [
71
- {
72
- level: 1,
73
- ariaLabel: "H1",
74
- icon: `<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 20H11V13H4V20H2V4H4V11H11V4H13V20ZM21.0005 8V20H19.0005L19 10.204L17 10.74V8.67L19.5005 8H21.0005Z"></path></svg>`
75
- },
76
- {
77
- level: 2,
78
- ariaLabel: "H2",
79
- icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4 4V11H11V4H13V20H11V13H4V20H2V4H4ZM18.5 8C20.5711 8 22.25 9.67893 22.25 11.75C22.25 12.6074 21.9623 13.3976 21.4781 14.0292L21.3302 14.2102L18.0343 18H22V20H15L14.9993 18.444L19.8207 12.8981C20.0881 12.5908 20.25 12.1893 20.25 11.75C20.25 10.7835 19.4665 10 18.5 10C17.5818 10 16.8288 10.7071 16.7558 11.6065L16.75 11.75H14.75C14.75 9.67893 16.4289 8 18.5 8Z"></path></svg>`
80
- },
81
- {
82
- level: 3,
83
- ariaLabel: "H3",
84
- icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M22 8L21.9984 10L19.4934 12.883C21.0823 13.3184 22.25 14.7728 22.25 16.5C22.25 18.5711 20.5711 20.25 18.5 20.25C16.674 20.25 15.1528 18.9449 14.8184 17.2166L16.7821 16.8352C16.9384 17.6413 17.6481 18.25 18.5 18.25C19.4665 18.25 20.25 17.4665 20.25 16.5C20.25 15.5335 19.4665 14.75 18.5 14.75C18.214 14.75 17.944 14.8186 17.7056 14.9403L16.3992 13.3932L19.3484 10H15V8H22ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4Z"></path></svg>`
85
- },
86
- {
87
- level: 4,
88
- ariaLabel: "H4",
89
- icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 20H11V13H4V20H2V4H4V11H11V4H13V20ZM22 8V16H23.5V18H22V20H20V18H14.5V16.66L19.5 8H22ZM20 11.133L17.19 16H20V11.133Z"></path></svg>`
90
- },
91
- {
92
- level: 5,
93
- ariaLabel: "H5",
94
- icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M22 8V10H17.6769L17.2126 12.6358C17.5435 12.5472 17.8912 12.5 18.25 12.5C20.4591 12.5 22.25 14.2909 22.25 16.5C22.25 18.7091 20.4591 20.5 18.25 20.5C16.4233 20.5 14.8827 19.2756 14.4039 17.6027L16.3271 17.0519C16.5667 17.8881 17.3369 18.5 18.25 18.5C19.3546 18.5 20.25 17.6046 20.25 16.5C20.25 15.3954 19.3546 14.5 18.25 14.5C17.6194 14.5 17.057 14.7918 16.6904 15.2478L14.8803 14.3439L16 8H22ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4Z"></path></svg>`
95
- },
96
- {
97
- level: 6,
98
- ariaLabel: "H6",
99
- icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M21.097 8L18.499 12.5C20.7091 12.5 22.5 14.2909 22.5 16.5C22.5 18.7091 20.7091 20.5 18.5 20.5C16.2909 20.5 14.5 18.7091 14.5 16.5C14.5 15.7636 14.699 15.0737 15.0461 14.4811L18.788 8H21.097ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4ZM18.5 14.5C17.3954 14.5 16.5 15.3954 16.5 16.5C16.5 17.6046 17.3954 18.5 18.5 18.5C19.6046 18.5 20.5 17.6046 20.5 16.5C20.5 15.3954 19.6046 14.5 18.5 14.5Z"></path></svg>`
100
- }
101
- ];
102
-
103
- let recalculateDropdownPosition = $state(null);
104
-
105
- let activeDropdownKey = $state(null);
106
-
107
- function handleOpenDropdown(e) {
108
- console.log(e);
109
- activeDropdownKey = e.detail.key;
110
- }
111
-
112
- function handleCloseDropdown(e) {
113
- console.log(e);
114
- }
115
-
116
- let activePosition:
117
- | "top-left"
118
- | "top-center"
119
- | "top-right"
120
- | "bottom-left"
121
- | "bottom-center"
122
- | "bottom-right"
123
- | "left-top"
124
- | "left-center"
125
- | "left-bottom"
126
- | "right-top"
127
- | "right-center"
128
- | "right-bottom" = $state("bottom-center");
129
-
130
- let refreshDropdown = $state(null);
131
- let toggleByKey = $state(null);
132
-
133
- let editor = $state() as Readable<Editor>;
134
-
135
- let currentJSON = { "type": "doc", "content": [ { "type": "heading", "attrs": { "level": 1 }, "content": [ { "type": "text", "text": "Hi there," } ] }, { "type": "taskList", "content": [ { "type": "taskItem", "attrs": { "checked": true }, "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "Esto es un checkbox" } ] } ] }, { "type": "taskItem", "attrs": { "checked": true }, "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "Otro item del checkbox" } ] } ] }, { "type": "taskItem", "attrs": { "checked": false }, "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "jhjkhjkjhkjh" } ] } ] }, { "type": "taskItem", "attrs": { "checked": false }, "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "hgfhfgh" } ] } ] } ] }, { "type": "paragraph", "content": [ { "type": "text", "text": "this is a " }, { "type": "text", "marks": [ { "type": "italic" } ], "text": "basic" }, { "type": "text", "text": " example of " }, { "type": "text", "marks": [ { "type": "bold" } ], "text": "Tiptap" }, { "type": "text", "text": ". Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists:" } ] }, { "type": "bulletList", "content": [ { "type": "listItem", "attrs": { "color": "" }, "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "That’s a bullet list with one …" } ] } ] }, { "type": "listItem", "attrs": { "color": "" }, "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "… or two list items." } ] } ] } ] }, { "type": "paragraph", "content": [ { "type": "text", "text": "Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block:" } ] }, { "type": "codeBlock", "attrs": { "language": "css" }, "content": [ { "type": "text", "text": "body {\n display: none;\n}" } ] }, { "type": "paragraph", "content": [ { "type": "text", "text": "I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too." } ] }, { "type": "blockquote", "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "Wow, that’s amazing. Good work, boy! 👏 " }, { "type": "hardBreak" }, { "type": "text", "text": "— Mom" } ] } ] }, { "type": "paragraph" } ] }
136
- if (!content) {
137
- content = currentJSON;
138
- }
139
-
140
68
  const extensions = [
141
- Color.configure({ types: [TextStyle.name, ListItem.name] }),
69
+ // Color.configure({ types: [TextStyle.name, ListItem.name] }),
142
70
  Highlight.configure({ multicolor: true }),
143
- TextStyle,
71
+ TextStyleKit,
144
72
  StarterKit.configure({
145
73
  // Disable an included extension
146
74
  trailingNode: false,
@@ -151,77 +79,6 @@
151
79
  // listKeymap: false,
152
80
  }),
153
81
  Underline,
154
- // Link.configure({
155
- // openOnClick: false,
156
- // autolink: true,
157
- // defaultProtocol: "https",
158
- // protocols: ["http", "https"],
159
- // isAllowedUri: (url, ctx) => {
160
- // try {
161
- // // construct URL
162
- // const parsedUrl = url.includes(":")
163
- // ? new URL(url)
164
- // : new URL(`${ctx.defaultProtocol}://${url}`);
165
-
166
- // // use default validation
167
- // if (!ctx.defaultValidate(parsedUrl.href)) {
168
- // return false;
169
- // }
170
-
171
- // // disallowed protocols
172
- // const disallowedProtocols = ["ftp", "file", "mailto"];
173
- // const protocol = parsedUrl.protocol.replace(":", "");
174
-
175
- // if (disallowedProtocols.includes(protocol)) {
176
- // return false;
177
- // }
178
-
179
- // // only allow protocols specified in ctx.protocols
180
- // const allowedProtocols = ctx.protocols.map((p) =>
181
- // typeof p === "string" ? p : p.scheme
182
- // );
183
-
184
- // if (!allowedProtocols.includes(protocol)) {
185
- // return false;
186
- // }
187
-
188
- // // disallowed domains
189
- // const disallowedDomains = [
190
- // "example-phishing.com",
191
- // "malicious-site.net",
192
- // ];
193
- // const domain = parsedUrl.hostname;
194
-
195
- // if (disallowedDomains.includes(domain)) {
196
- // return false;
197
- // }
198
-
199
- // // all checks have passed
200
- // return true;
201
- // } catch {
202
- // return false;
203
- // }
204
- // },
205
- // shouldAutoLink: (url) => {
206
- // try {
207
- // // construct URL
208
- // const parsedUrl = url.includes(":")
209
- // ? new URL(url)
210
- // : new URL(`https://${url}`);
211
-
212
- // // only auto-link if the domain is not in the disallowed list
213
- // const disallowedDomains = [
214
- // "example-no-autolink.com",
215
- // "another-no-autolink.com",
216
- // ];
217
- // const domain = parsedUrl.hostname;
218
-
219
- // return !disallowedDomains.includes(domain);
220
- // } catch {
221
- // return false;
222
- // }
223
- // },
224
- // }),
225
82
  EnhancedLink,
226
83
  Link.configure({
227
84
  openOnClick: false,
@@ -236,70 +93,124 @@
236
93
  }),
237
94
  ...customExtensions,
238
95
  ]
96
+
97
+ let editor = $state() as Readable<Editor>;
98
+
99
+ let tooltipVisible = $state(false);
100
+ let tooltipX = $state(0);
101
+ let tooltipY = $state(0);
102
+ let tooltip: HTMLDivElement = $state(null) as HTMLDivElement;
103
+ let cleanup: () => void;
104
+ let currentTriggerEl: HTMLElement | null = null;
105
+ let activeDropdownType = $state(null);
106
+
107
+ let currentJSON = {
108
+ type: "doc",
109
+ content: exampleJSONContent,
110
+ };
111
+
112
+ if (!content) {
113
+ content = currentJSON;
114
+ }
115
+
116
+ function toogleDropdown(el: HTMLElement, type: string = null) {
117
+ if (!el) return;
118
+ // console.log(el);
119
+ activeDropdownType = type;
120
+
121
+ if (tooltipVisible) {
122
+ hideDropdown(); // Ocultar
123
+ } else {
124
+ hideDropdown(); // limpiar antes de abrir
125
+ currentTriggerEl = el;
126
+ tooltipVisible = true;
127
+ document.body.append(tooltip);
128
+
129
+ document.addEventListener("mousedown", handleClickOutside);
130
+ cleanup = autoUpdate(el, tooltip, () => updatePosition(el));
131
+ }
132
+ }
133
+
134
+ function hideDropdown() {
135
+ tooltipVisible = false;
136
+ tooltip?.remove();
137
+ document.removeEventListener("mousedown", handleClickOutside);
138
+ cleanup && cleanup();
139
+ currentTriggerEl = null;
140
+ }
141
+
142
+ function handleClickOutside(e: MouseEvent) {
143
+ if (!tooltip) return;
144
+ const target = e.target as Node;
145
+
146
+ // Excepciones: tooltip, trigger actual, y el drag-area contenedor
147
+ if (
148
+ tooltip.contains(target) ||
149
+ currentTriggerEl?.contains(target)
150
+ ) {
151
+ return; // no cerrar
152
+ }
153
+
154
+ hideDropdown();
155
+ }
156
+
157
+ function updatePosition(el: HTMLElement) {
158
+ computePosition(el, tooltip, {
159
+ placement: "bottom",
160
+ middleware: [
161
+ offset(4),
162
+ ],
163
+ }).then(({ x, y }) => {
164
+ tooltipX = x;
165
+ tooltipY = y;
166
+ });
167
+ }
168
+
239
169
  onMount(() => {
240
170
  editor = createEditor({
241
171
  extensions,
242
172
  content,
243
173
  onTransaction: ({ editor, transaction }) => {
244
- // console.log('onTransaction', editor, transaction)
245
174
  editorEvents.onTransaction({ editor, transaction });
246
175
  editor = editor;
247
176
  },
248
177
 
249
178
  onBeforeCreate({ editor }) {
250
- // console.log('onBeforeCreate', editor)
251
179
  editorEvents.onBeforeCreate({ editor });
252
- // Before the view is created.
253
180
  },
181
+
254
182
  onCreate: ({ editor }) => {
255
- // console.log('onCreate', editor)
256
183
  editorEvents.onCreate({ editor });
257
184
  },
258
185
 
259
186
  onUpdate: ({ editor }) => {
260
- // console.log('onUpdate', editor)
261
187
  editorEvents.onUpdate({ editor, html: editor.getHTML(), json: editor.getJSON() });
262
188
  },
263
189
 
264
190
  onPaste: (event, slice) => {
265
- // console.log('onPaste', event, slice)
266
191
  editorEvents.onPaste({ event, slice });
267
192
  },
268
193
 
269
194
  onSelectionUpdate({ editor }) {
270
- // console.log('onSelectionUpdate', editor)
271
- // The selection has changed.
272
195
  editorEvents.onSelectionUpdate({ editor });
273
196
  },
274
197
  onFocus({ editor, event }) {
275
- // console.log('onFocus', editor, event)
276
- // The editor is focused.
277
198
  editorEvents.onFocus({ editor, event });
278
199
  },
279
200
  onBlur({ editor, event }) {
280
- // The editor isn’t focused anymore.
281
- // console.log('onBlur', editor, event)
282
201
  editorEvents.onBlur({ editor, event });
283
202
  },
284
203
  onDestroy() {
285
- // The editor is being destroyed.
286
- // console.log('onDestroy')
287
204
  editorEvents.onDestroy({ editor, message: "onDestroy" });
288
205
  },
289
206
  onDrop(event: any, slice: any, moved: boolean) {
290
- // The editor is being pasted into.
291
- // console.log('onDrop', event, slice, moved)
292
207
  editorEvents.onDrop({ editor, event, slice, moved });
293
208
  },
294
209
  onDelete({ type, deletedRange, newRange, partial, from, to }) {
295
- // Content was deleted from the editor (either a node or mark).
296
- // console.log('onDelete', type, deletedRange, newRange, partial, from, to)
297
210
  editorEvents.onDelete({ editor, type, deletedRange, newRange, partial, from, to });
298
211
  },
299
212
  onContentError({ editor, error, disableCollaboration }) {
300
- // console.log('onContentError', editor, error, disableCollaboration)
301
213
  editorEvents.onContentError({ editor, error, disableCollaboration });
302
- // The editor content does not match the schema.
303
214
  },
304
215
  });
305
216
  });
@@ -310,27 +221,20 @@
310
221
  }
311
222
  });
312
223
 
313
- function changeDropdownPosition(position) {
314
- activePosition = position;
315
- }
316
-
317
224
  function setLink() {
318
225
  const previousUrl = $editor.getAttributes("link").href;
319
226
  const url = window.prompt("URL", previousUrl);
320
227
 
321
- // cancelled
322
228
  if (url === null) {
323
229
  return;
324
230
  }
325
231
 
326
- // empty
327
232
  if (url === "") {
328
233
  $editor.chain().focus().extendMarkRange("link").unsetLink().run();
329
234
 
330
235
  return;
331
236
  }
332
237
 
333
- // update link
334
238
  try {
335
239
  $editor
336
240
  .chain()
@@ -344,12 +248,11 @@
344
248
  allLinks.forEach((link: any) => {
345
249
  link.addEventListener("click", (e: any) => {
346
250
  e.preventDefault();
347
- // console.log("Prevented navigation:", link.getAttribute("href"));
348
251
  });
349
252
  });
350
253
  }, 100);
351
254
  } catch (e) {
352
- alert(e.message);
255
+ console.log(e.message);
353
256
  }
354
257
  }
355
258
 
@@ -360,7 +263,6 @@
360
263
  }
361
264
  </script>
362
265
 
363
-
364
266
  <div class="fl-rich-text {className}" class:editable>
365
267
  {#if editor}
366
268
  <header class="fl-rich-text-toolbar">
@@ -409,13 +311,10 @@
409
311
  </button>
410
312
  </div>
411
313
 
412
- <div class="fl-rich-text-toolbar-group">
314
+ <div class="fl-rich-text-toolbar-group">
413
315
  <button
414
316
  type="button"
415
- onclick={() => changeDropdownPosition("bottom-center")}
416
- data-dropdown-toggle="dropdown"
417
- data-dropdown-trigger="click"
418
- data-dropdown-key={"heading-dropdown-key"}
317
+ onclick={(e) => toogleDropdown(e.currentTarget, "headings-dropdown")}
419
318
  class:is-active={$editor.isActive("heading")}
420
319
  aria-label="Heading"
421
320
  >
@@ -451,10 +350,7 @@
451
350
  <div role="group" class="fl-rich-text-toolbar-group">
452
351
  <button aria-label="List"
453
352
  type="button"
454
- onclick={() => changeDropdownPosition("bottom-center")}
455
- data-dropdown-toggle="dropdown"
456
- data-dropdown-trigger="click"
457
- data-dropdown-key={"list-dropdown-key"}
353
+ onclick={(e) => toogleDropdown(e.currentTarget, "list-dropdown")}
458
354
  class:is-active={$editor.isActive("bulletList") || $editor.isActive("orderedList") || $editor.isActive("taskList")}
459
355
  >
460
356
 
@@ -559,6 +455,7 @@
559
455
  ></path></svg
560
456
  >
561
457
  </button>
458
+
562
459
  <button
563
460
  type="button"
564
461
  onclick={() => $editor.chain().focus().toggleItalic().run()}
@@ -579,6 +476,7 @@
579
476
  ></path></svg
580
477
  >
581
478
  </button>
479
+
582
480
  <button
583
481
  type="button"
584
482
  onclick={() => $editor.chain().focus().toggleUnderline().run()}
@@ -601,6 +499,7 @@
601
499
  ></path></svg
602
500
  >
603
501
  </button>
502
+
604
503
  <button
605
504
  type="button"
606
505
  onclick={() => $editor.chain().focus().toggleStrike().run()}
@@ -624,6 +523,7 @@
624
523
  ></path></svg
625
524
  >
626
525
  </button>
526
+
627
527
  <button
628
528
  type="button"
629
529
  onclick={() => $editor.chain().focus().toggleCode().run()}
@@ -652,14 +552,6 @@
652
552
  </button>
653
553
  </div>
654
554
 
655
- <!-- <button
656
- type="button"
657
- onclick={() => $editor.chain().focus().setParagraph().run()}
658
- class={$editor.isActive("paragraph") ? "is-active" : ""}
659
- >
660
- Paragraph
661
- </button> -->
662
-
663
555
  <div role="group" class="fl-rich-text-toolbar-group">
664
556
  <button
665
557
  type="button"
@@ -818,121 +710,121 @@
818
710
  <button
819
711
  type="button"
820
712
  onclick={() => $editor.chain().focus().unsetAllMarks().run()}
821
- >Clear marks</button
822
713
  >
714
+ Clear marks
715
+ </button>
716
+
823
717
  <button
824
718
  type="button"
825
719
  onclick={() => $editor.chain().focus().clearNodes().run()}
826
- >Clear nodes</button
827
720
  >
721
+ Clear nodes
722
+ </button>
828
723
  </div>
829
-
830
- <Dropdown
831
- bind:refreshDropdown
832
- bind:calculatePosition={recalculateDropdownPosition}
833
- bind:toggleByKey
834
- position={activePosition}
835
- yOffset={0}
836
- xOffset={0}
837
- margin={3}
838
- id="dropdown"
839
- on:close={handleCloseDropdown}
840
- on:open={handleOpenDropdown}
841
- >
842
- {#if activeDropdownKey === 'heading-dropdown-key'}
843
- <div role="group" class="fl-rich-text-toolbar-group">
844
- <button
845
- type="button"
846
- onclick={() =>
847
- $editor.chain().focus().toggleHeading({ level: 1 }).run()}
848
- class={$editor.isActive("heading", { level: 1 }) ? "is-active" : ""}
849
- aria-label="H1"
850
- >
851
- <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 20H11V13H4V20H2V4H4V11H11V4H13V20ZM21.0005 8V20H19.0005L19 10.204L17 10.74V8.67L19.5005 8H21.0005Z"></path></svg>
852
- </button>
853
-
854
- <button
855
- type="button"
856
- onclick={() =>
857
- $editor.chain().focus().toggleHeading({ level: 2 }).run()}
858
- class={$editor.isActive("heading", { level: 2 }) ? "is-active" : ""}
859
- aria-label="H2"
860
- >
861
- <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4 4V11H11V4H13V20H11V13H4V20H2V4H4ZM18.5 8C20.5711 8 22.25 9.67893 22.25 11.75C22.25 12.6074 21.9623 13.3976 21.4781 14.0292L21.3302 14.2102L18.0343 18H22V20H15L14.9993 18.444L19.8207 12.8981C20.0881 12.5908 20.25 12.1893 20.25 11.75C20.25 10.7835 19.4665 10 18.5 10C17.5818 10 16.8288 10.7071 16.7558 11.6065L16.75 11.75H14.75C14.75 9.67893 16.4289 8 18.5 8Z"></path></svg>
862
- </button>
863
-
864
- <button
865
- type="button"
866
- onclick={() =>
867
- $editor.chain().focus().toggleHeading({ level: 3 }).run()}
868
- class={$editor.isActive("heading", { level: 3 }) ? "is-active" : ""}
869
- aria-label="H3"
870
- >
871
- <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M22 8L21.9984 10L19.4934 12.883C21.0823 13.3184 22.25 14.7728 22.25 16.5C22.25 18.5711 20.5711 20.25 18.5 20.25C16.674 20.25 15.1528 18.9449 14.8184 17.2166L16.7821 16.8352C16.9384 17.6413 17.6481 18.25 18.5 18.25C19.4665 18.25 20.25 17.4665 20.25 16.5C20.25 15.5335 19.4665 14.75 18.5 14.75C18.214 14.75 17.944 14.8186 17.7056 14.9403L16.3992 13.3932L19.3484 10H15V8H22ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4Z"></path></svg>
872
- </button>
873
-
874
- <button
875
- type="button"
876
- onclick={() =>
877
- $editor.chain().focus().toggleHeading({ level: 4 }).run()}
878
- class={$editor.isActive("heading", { level: 4 }) ? "is-active" : ""}
879
- aria-label="H4"
880
- >
881
- <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 20H11V13H4V20H2V4H4V11H11V4H13V20ZM22 8V16H23.5V18H22V20H20V18H14.5V16.66L19.5 8H22ZM20 11.133L17.19 16H20V11.133Z"></path></svg>
882
- </button>
883
-
884
- <button
885
- type="button"
886
- onclick={() =>
887
- $editor.chain().focus().toggleHeading({ level: 5 }).run()}
888
- class={$editor.isActive("heading", { level: 5 }) ? "is-active" : ""}
889
- aria-label="H5"
890
- >
891
- <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M22 8V10H17.6769L17.2126 12.6358C17.5435 12.5472 17.8912 12.5 18.25 12.5C20.4591 12.5 22.25 14.2909 22.25 16.5C22.25 18.7091 20.4591 20.5 18.25 20.5C16.4233 20.5 14.8827 19.2756 14.4039 17.6027L16.3271 17.0519C16.5667 17.8881 17.3369 18.5 18.25 18.5C19.3546 18.5 20.25 17.6046 20.25 16.5C20.25 15.3954 19.3546 14.5 18.25 14.5C17.6194 14.5 17.057 14.7918 16.6904 15.2478L14.8803 14.3439L16 8H22ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4Z"></path></svg>
892
- </button>
893
-
894
- <button
895
- type="button"
896
- onclick={() =>
897
- $editor.chain().focus().toggleHeading({ level: 6 }).run()}
898
- class={$editor.isActive("heading", { level: 6 }) ? "is-active" : ""}
899
- aria-label="H6"
900
- >
901
- <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M21.097 8L18.499 12.5C20.7091 12.5 22.5 14.2909 22.5 16.5C22.5 18.7091 20.7091 20.5 18.5 20.5C16.2909 20.5 14.5 18.7091 14.5 16.5C14.5 15.7636 14.699 15.0737 15.0461 14.4811L18.788 8H21.097ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4ZM18.5 14.5C17.3954 14.5 16.5 15.3954 16.5 16.5C16.5 17.6046 17.3954 18.5 18.5 18.5C19.6046 18.5 20.5 17.6046 20.5 16.5C20.5 15.3954 19.6046 14.5 18.5 14.5Z"></path></svg>
902
- </button>
903
- </div>
904
- {:else if activeDropdownKey === 'list-dropdown-key'}
905
- <div role="group" class="fl-rich-text-toolbar-group">
906
- <button aria-label="Bullet list"
907
- type="button"
908
- onclick={() => $editor.chain().focus().toggleBulletList().run()}
909
- class={$editor.isActive("bulletList") ? "is-active" : ""}
910
- >
911
- <svg width="24" height="24" class="tiptap-button-icon" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 6C7 5.44772 7.44772 5 8 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H8C7.44772 7 7 6.55228 7 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M7 12C7 11.4477 7.44772 11 8 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H8C7.44772 13 7 12.5523 7 12Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M7 18C7 17.4477 7.44772 17 8 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H8C7.44772 19 7 18.5523 7 18Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M2 6C2 5.44772 2.44772 5 3 5H3.01C3.56228 5 4.01 5.44772 4.01 6C4.01 6.55228 3.56228 7 3.01 7H3C2.44772 7 2 6.55228 2 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 11.4477 2.44772 11 3 11H3.01C3.56228 11 4.01 11.4477 4.01 12C4.01 12.5523 3.56228 13 3.01 13H3C2.44772 13 2 12.5523 2 12Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M2 18C2 17.4477 2.44772 17 3 17H3.01C3.56228 17 4.01 17.4477 4.01 18C4.01 18.5523 3.56228 19 3.01 19H3C2.44772 19 2 18.5523 2 18Z" fill="currentColor"></path></svg>
912
- </button>
913
-
914
- <button aria-label="Ordered list"
915
- type="button"
916
- onclick={() => $editor.chain().focus().toggleOrderedList().run()}
917
- class={$editor.isActive("orderedList") ? "is-active" : ""}
918
- >
919
- <svg width="24" height="24" class="tiptap-button-icon" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M9 6C9 5.44772 9.44772 5 10 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H10C9.44772 7 9 6.55228 9 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M9 12C9 11.4477 9.44772 11 10 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H10C9.44772 13 9 12.5523 9 12Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M9 18C9 17.4477 9.44772 17 10 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H10C9.44772 19 9 18.5523 9 18Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M3 6C3 5.44772 3.44772 5 4 5H5C5.55228 5 6 5.44772 6 6V10C6 10.5523 5.55228 11 5 11C4.44772 11 4 10.5523 4 10V7C3.44772 7 3 6.55228 3 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M3 10C3 9.44772 3.44772 9 4 9H6C6.55228 9 7 9.44772 7 10C7 10.5523 6.55228 11 6 11H4C3.44772 11 3 10.5523 3 10Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M5.82219 13.0431C6.54543 13.4047 6.99997 14.1319 6.99997 15C6.99997 15.5763 6.71806 16.0426 6.48747 16.35C6.31395 16.5814 6.1052 16.8044 5.91309 17H5.99997C6.55226 17 6.99997 17.4477 6.99997 18C6.99997 18.5523 6.55226 19 5.99997 19H3.99997C3.44769 19 2.99997 18.5523 2.99997 18C2.99997 17.4237 3.28189 16.9575 3.51247 16.65C3.74323 16.3424 4.03626 16.0494 4.26965 15.8161C4.27745 15.8083 4.2852 15.8006 4.29287 15.7929C4.55594 15.5298 4.75095 15.3321 4.88748 15.15C4.96287 15.0495 4.99021 14.9922 4.99911 14.9714C4.99535 14.9112 4.9803 14.882 4.9739 14.8715C4.96613 14.8588 4.95382 14.845 4.92776 14.8319C4.87723 14.8067 4.71156 14.7623 4.44719 14.8944C3.95321 15.1414 3.35254 14.9412 3.10555 14.4472C2.85856 13.9533 3.05878 13.3526 3.55276 13.1056C4.28839 12.7378 5.12272 12.6934 5.82219 13.0431Z" fill="currentColor"></path></svg>
920
- </button>
921
-
922
- <button aria-label="Task list"
923
- type="button"
924
- onclick={() => $editor.chain().focus().toggleTaskList().run()}
925
- class={$editor.isActive("taskList") ? "is-active" : ""}
926
- >
927
- <svg width="24" height="24" class="tiptap-button-icon" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M2 6C2 4.89543 2.89543 4 4 4H8C9.10457 4 10 4.89543 10 6V10C10 11.1046 9.10457 12 8 12H4C2.89543 12 2 11.1046 2 10V6ZM8 6H4V10H8V6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M9.70711 14.2929C10.0976 14.6834 10.0976 15.3166 9.70711 15.7071L5.70711 19.7071C5.31658 20.0976 4.68342 20.0976 4.29289 19.7071L2.29289 17.7071C1.90237 17.3166 1.90237 16.6834 2.29289 16.2929C2.68342 15.9024 3.31658 15.9024 3.70711 16.2929L5 17.5858L8.29289 14.2929C8.68342 13.9024 9.31658 13.9024 9.70711 14.2929Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M12 6C12 5.44772 12.4477 5 13 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H13C12.4477 7 12 6.55228 12 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M12 12C12 11.4477 12.4477 11 13 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H13C12.4477 13 12 12.5523 12 12Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M12 18C12 17.4477 12.4477 17 13 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H13C12.4477 19 12 18.5523 12 18Z" fill="currentColor"></path></svg>
928
- </button>
929
- </div>
930
- {/if}
931
- </Dropdown>
932
724
  </header>
933
725
  {/if}
934
726
 
935
727
  <EditorContent editor={$editor} class="fl-rich-text-content" />
728
+
729
+ <div
730
+ class="fl-toolbar-dropdown-panel"
731
+ bind:this={tooltip}
732
+ style="display: {tooltipVisible ? 'flex' : 'none'}; left: {tooltipX}px; top: {tooltipY}px;"
733
+ >
734
+ {#if activeDropdownType === "headings-dropdown"}
735
+
736
+ <div role="group" class="fl-rich-text-toolbar-group">
737
+ <button
738
+ type="button"
739
+ onclick={() =>
740
+ $editor.chain().focus().toggleHeading({ level: 1 }).run()}
741
+ class={$editor.isActive("heading", { level: 1 }) ? "is-active" : ""}
742
+ aria-label="H1"
743
+ >
744
+ <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 20H11V13H4V20H2V4H4V11H11V4H13V20ZM21.0005 8V20H19.0005L19 10.204L17 10.74V8.67L19.5005 8H21.0005Z"></path></svg>
745
+ </button>
746
+
747
+ <button
748
+ type="button"
749
+ onclick={() =>
750
+ $editor.chain().focus().toggleHeading({ level: 2 }).run()}
751
+ class={$editor.isActive("heading", { level: 2 }) ? "is-active" : ""}
752
+ aria-label="H2"
753
+ >
754
+ <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4 4V11H11V4H13V20H11V13H4V20H2V4H4ZM18.5 8C20.5711 8 22.25 9.67893 22.25 11.75C22.25 12.6074 21.9623 13.3976 21.4781 14.0292L21.3302 14.2102L18.0343 18H22V20H15L14.9993 18.444L19.8207 12.8981C20.0881 12.5908 20.25 12.1893 20.25 11.75C20.25 10.7835 19.4665 10 18.5 10C17.5818 10 16.8288 10.7071 16.7558 11.6065L16.75 11.75H14.75C14.75 9.67893 16.4289 8 18.5 8Z"></path></svg>
755
+ </button>
756
+
757
+ <button
758
+ type="button"
759
+ onclick={() =>
760
+ $editor.chain().focus().toggleHeading({ level: 3 }).run()}
761
+ class={$editor.isActive("heading", { level: 3 }) ? "is-active" : ""}
762
+ aria-label="H3"
763
+ >
764
+ <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M22 8L21.9984 10L19.4934 12.883C21.0823 13.3184 22.25 14.7728 22.25 16.5C22.25 18.5711 20.5711 20.25 18.5 20.25C16.674 20.25 15.1528 18.9449 14.8184 17.2166L16.7821 16.8352C16.9384 17.6413 17.6481 18.25 18.5 18.25C19.4665 18.25 20.25 17.4665 20.25 16.5C20.25 15.5335 19.4665 14.75 18.5 14.75C18.214 14.75 17.944 14.8186 17.7056 14.9403L16.3992 13.3932L19.3484 10H15V8H22ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4Z"></path></svg>
765
+ </button>
766
+
767
+ <button
768
+ type="button"
769
+ onclick={() =>
770
+ $editor.chain().focus().toggleHeading({ level: 4 }).run()}
771
+ class={$editor.isActive("heading", { level: 4 }) ? "is-active" : ""}
772
+ aria-label="H4"
773
+ >
774
+ <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 20H11V13H4V20H2V4H4V11H11V4H13V20ZM22 8V16H23.5V18H22V20H20V18H14.5V16.66L19.5 8H22ZM20 11.133L17.19 16H20V11.133Z"></path></svg>
775
+ </button>
776
+
777
+ <button
778
+ type="button"
779
+ onclick={() =>
780
+ $editor.chain().focus().toggleHeading({ level: 5 }).run()}
781
+ class={$editor.isActive("heading", { level: 5 }) ? "is-active" : ""}
782
+ aria-label="H5"
783
+ >
784
+ <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M22 8V10H17.6769L17.2126 12.6358C17.5435 12.5472 17.8912 12.5 18.25 12.5C20.4591 12.5 22.25 14.2909 22.25 16.5C22.25 18.7091 20.4591 20.5 18.25 20.5C16.4233 20.5 14.8827 19.2756 14.4039 17.6027L16.3271 17.0519C16.5667 17.8881 17.3369 18.5 18.25 18.5C19.3546 18.5 20.25 17.6046 20.25 16.5C20.25 15.3954 19.3546 14.5 18.25 14.5C17.6194 14.5 17.057 14.7918 16.6904 15.2478L14.8803 14.3439L16 8H22ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4Z"></path></svg>
785
+ </button>
786
+
787
+ <button
788
+ type="button"
789
+ onclick={() =>
790
+ $editor.chain().focus().toggleHeading({ level: 6 }).run()}
791
+ class={$editor.isActive("heading", { level: 6 }) ? "is-active" : ""}
792
+ aria-label="H6"
793
+ >
794
+ <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M21.097 8L18.499 12.5C20.7091 12.5 22.5 14.2909 22.5 16.5C22.5 18.7091 20.7091 20.5 18.5 20.5C16.2909 20.5 14.5 18.7091 14.5 16.5C14.5 15.7636 14.699 15.0737 15.0461 14.4811L18.788 8H21.097ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4ZM18.5 14.5C17.3954 14.5 16.5 15.3954 16.5 16.5C16.5 17.6046 17.3954 18.5 18.5 18.5C19.6046 18.5 20.5 17.6046 20.5 16.5C20.5 15.3954 19.6046 14.5 18.5 14.5Z"></path></svg>
795
+ </button>
796
+ </div>
797
+
798
+ {:else if activeDropdownType === "list-dropdown"}
799
+
800
+ <div role="group" class="fl-rich-text-toolbar-group">
801
+ <button aria-label="Bullet list"
802
+ type="button"
803
+ onclick={() => $editor.chain().focus().toggleBulletList().run()}
804
+ class={$editor.isActive("bulletList") ? "is-active" : ""}
805
+ >
806
+ <svg width="24" height="24" class="tiptap-button-icon" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 6C7 5.44772 7.44772 5 8 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H8C7.44772 7 7 6.55228 7 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M7 12C7 11.4477 7.44772 11 8 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H8C7.44772 13 7 12.5523 7 12Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M7 18C7 17.4477 7.44772 17 8 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H8C7.44772 19 7 18.5523 7 18Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M2 6C2 5.44772 2.44772 5 3 5H3.01C3.56228 5 4.01 5.44772 4.01 6C4.01 6.55228 3.56228 7 3.01 7H3C2.44772 7 2 6.55228 2 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 11.4477 2.44772 11 3 11H3.01C3.56228 11 4.01 11.4477 4.01 12C4.01 12.5523 3.56228 13 3.01 13H3C2.44772 13 2 12.5523 2 12Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M2 18C2 17.4477 2.44772 17 3 17H3.01C3.56228 17 4.01 17.4477 4.01 18C4.01 18.5523 3.56228 19 3.01 19H3C2.44772 19 2 18.5523 2 18Z" fill="currentColor"></path></svg>
807
+ </button>
808
+
809
+ <button aria-label="Ordered list"
810
+ type="button"
811
+ onclick={() => $editor.chain().focus().toggleOrderedList().run()}
812
+ class={$editor.isActive("orderedList") ? "is-active" : ""}
813
+ >
814
+ <svg width="24" height="24" class="tiptap-button-icon" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M9 6C9 5.44772 9.44772 5 10 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H10C9.44772 7 9 6.55228 9 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M9 12C9 11.4477 9.44772 11 10 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H10C9.44772 13 9 12.5523 9 12Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M9 18C9 17.4477 9.44772 17 10 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H10C9.44772 19 9 18.5523 9 18Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M3 6C3 5.44772 3.44772 5 4 5H5C5.55228 5 6 5.44772 6 6V10C6 10.5523 5.55228 11 5 11C4.44772 11 4 10.5523 4 10V7C3.44772 7 3 6.55228 3 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M3 10C3 9.44772 3.44772 9 4 9H6C6.55228 9 7 9.44772 7 10C7 10.5523 6.55228 11 6 11H4C3.44772 11 3 10.5523 3 10Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M5.82219 13.0431C6.54543 13.4047 6.99997 14.1319 6.99997 15C6.99997 15.5763 6.71806 16.0426 6.48747 16.35C6.31395 16.5814 6.1052 16.8044 5.91309 17H5.99997C6.55226 17 6.99997 17.4477 6.99997 18C6.99997 18.5523 6.55226 19 5.99997 19H3.99997C3.44769 19 2.99997 18.5523 2.99997 18C2.99997 17.4237 3.28189 16.9575 3.51247 16.65C3.74323 16.3424 4.03626 16.0494 4.26965 15.8161C4.27745 15.8083 4.2852 15.8006 4.29287 15.7929C4.55594 15.5298 4.75095 15.3321 4.88748 15.15C4.96287 15.0495 4.99021 14.9922 4.99911 14.9714C4.99535 14.9112 4.9803 14.882 4.9739 14.8715C4.96613 14.8588 4.95382 14.845 4.92776 14.8319C4.87723 14.8067 4.71156 14.7623 4.44719 14.8944C3.95321 15.1414 3.35254 14.9412 3.10555 14.4472C2.85856 13.9533 3.05878 13.3526 3.55276 13.1056C4.28839 12.7378 5.12272 12.6934 5.82219 13.0431Z" fill="currentColor"></path></svg>
815
+ </button>
816
+
817
+ <button aria-label="Task list"
818
+ type="button"
819
+ onclick={() => $editor.chain().focus().toggleTaskList().run()}
820
+ class={$editor.isActive("taskList") ? "is-active" : ""}
821
+ >
822
+ <svg width="24" height="24" class="tiptap-button-icon" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M2 6C2 4.89543 2.89543 4 4 4H8C9.10457 4 10 4.89543 10 6V10C10 11.1046 9.10457 12 8 12H4C2.89543 12 2 11.1046 2 10V6ZM8 6H4V10H8V6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M9.70711 14.2929C10.0976 14.6834 10.0976 15.3166 9.70711 15.7071L5.70711 19.7071C5.31658 20.0976 4.68342 20.0976 4.29289 19.7071L2.29289 17.7071C1.90237 17.3166 1.90237 16.6834 2.29289 16.2929C2.68342 15.9024 3.31658 15.9024 3.70711 16.2929L5 17.5858L8.29289 14.2929C8.68342 13.9024 9.31658 13.9024 9.70711 14.2929Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M12 6C12 5.44772 12.4477 5 13 5H21C21.5523 5 22 5.44772 22 6C22 6.55228 21.5523 7 21 7H13C12.4477 7 12 6.55228 12 6Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M12 12C12 11.4477 12.4477 11 13 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H13C12.4477 13 12 12.5523 12 12Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M12 18C12 17.4477 12.4477 17 13 17H21C21.5523 17 22 17.4477 22 18C22 18.5523 21.5523 19 21 19H13C12.4477 19 12 18.5523 12 18Z" fill="currentColor"></path></svg>
823
+ </button>
824
+ </div>
825
+
826
+ {/if}
827
+ </div>
936
828
  </div>
937
829
 
938
830
  <style>
@@ -961,6 +853,12 @@
961
853
  align-items: center;
962
854
  gap: var(--toolbar-gap);
963
855
  padding: var(--toolbar-padding);
856
+ }
857
+
858
+ .fl-rich-text-toolbar-group {
859
+ display: flex;
860
+ flex-wrap: nowrap;
861
+ gap: var(--toolbar-gap);
964
862
 
965
863
  button {
966
864
  padding: 8px 8px;
@@ -978,32 +876,24 @@
978
876
  cursor: pointer;
979
877
  line-height: 1;
980
878
 
879
+ & svg {
880
+ width: 16px;
881
+ height: 16px;
882
+
883
+ &.toogle-dropdown-button-icon {
884
+ width: 7px;
885
+ height: 7px;
886
+ margin-left: 4px;
887
+ }
888
+ }
889
+
981
890
  &:disabled {
982
891
  cursor: not-allowed;
983
892
  opacity: 0.5;
984
893
  }
985
-
986
- .toogle-dropdown-button-icon {
987
- width: 7px;
988
- height: 7px;
989
- margin-left: 4px;
990
- }
991
894
  }
992
895
  }
993
896
 
994
- :global(.fl-rich-text-toolbar button svg) {
995
-
996
- width: 16px;
997
- height: 16px;
998
-
999
- }
1000
-
1001
- .fl-rich-text-toolbar-group {
1002
- display: flex;
1003
- flex-wrap: nowrap;
1004
- gap: var(--toolbar-gap);
1005
- }
1006
-
1007
897
  button.is-active {
1008
898
  background: var(--purple);
1009
899
  color: white;
@@ -1023,4 +913,4 @@
1023
913
  outline: 1px dashed #818181;
1024
914
  scale: 1.1;
1025
915
  }
1026
- </style>
916
+ </style>
package/dist/styles.css CHANGED
@@ -178,4 +178,20 @@
178
178
  min-height: 56px;
179
179
  padding: 2rem;
180
180
  background: #242424;
181
- }
181
+ }
182
+
183
+ .fl-toolbar-dropdown-panel {
184
+ position: absolute;
185
+ padding: 9px;
186
+ background: #2626268c;
187
+ border: 1px solid #ffffff12;
188
+ backdrop-filter: blur(42px);
189
+ border-radius: 14px;
190
+ min-width: auto;
191
+ z-index: 10;
192
+ box-sizing: border-box;
193
+
194
+ &.fl-toolbar-dropdown-panel--table {
195
+ min-width: 160px
196
+ }
197
+ }
@@ -0,0 +1,89 @@
1
+ export declare const exampleJSONContent: ({
2
+ type: string;
3
+ attrs: {
4
+ level: number;
5
+ language?: undefined;
6
+ };
7
+ content: {
8
+ type: string;
9
+ text: string;
10
+ }[];
11
+ } | {
12
+ type: string;
13
+ content: {
14
+ type: string;
15
+ attrs: {
16
+ checked: boolean;
17
+ };
18
+ content: {
19
+ type: string;
20
+ content: {
21
+ type: string;
22
+ text: string;
23
+ }[];
24
+ }[];
25
+ }[];
26
+ attrs?: undefined;
27
+ } | {
28
+ type: string;
29
+ content: ({
30
+ type: string;
31
+ text: string;
32
+ marks?: undefined;
33
+ } | {
34
+ type: string;
35
+ marks: {
36
+ type: string;
37
+ }[];
38
+ text: string;
39
+ })[];
40
+ attrs?: undefined;
41
+ } | {
42
+ type: string;
43
+ content: {
44
+ type: string;
45
+ attrs: {
46
+ color: string;
47
+ };
48
+ content: {
49
+ type: string;
50
+ content: {
51
+ type: string;
52
+ text: string;
53
+ }[];
54
+ }[];
55
+ }[];
56
+ attrs?: undefined;
57
+ } | {
58
+ type: string;
59
+ attrs: {
60
+ language: string;
61
+ level?: undefined;
62
+ };
63
+ content: {
64
+ type: string;
65
+ text: string;
66
+ }[];
67
+ } | {
68
+ type: string;
69
+ content: {
70
+ type: string;
71
+ content: ({
72
+ type: string;
73
+ text: string;
74
+ } | {
75
+ type: string;
76
+ text?: undefined;
77
+ })[];
78
+ }[];
79
+ attrs?: undefined;
80
+ } | {
81
+ type: string;
82
+ attrs?: undefined;
83
+ content?: undefined;
84
+ })[];
85
+ export declare const HEADINGS: {
86
+ level: number;
87
+ ariaLabel: string;
88
+ icon: string;
89
+ }[];
package/dist/utils.js ADDED
@@ -0,0 +1,177 @@
1
+ export const exampleJSONContent = [
2
+ {
3
+ type: "heading",
4
+ attrs: { level: 1 },
5
+ content: [{ type: "text", text: "Hi there," }],
6
+ },
7
+ {
8
+ type: "taskList",
9
+ content: [
10
+ {
11
+ type: "taskItem",
12
+ attrs: { checked: true },
13
+ content: [
14
+ {
15
+ type: "paragraph",
16
+ content: [{ type: "text", text: "Esto es un checkbox" }],
17
+ },
18
+ ],
19
+ },
20
+ {
21
+ type: "taskItem",
22
+ attrs: { checked: true },
23
+ content: [
24
+ {
25
+ type: "paragraph",
26
+ content: [{ type: "text", text: "Otro item del checkbox" }],
27
+ },
28
+ ],
29
+ },
30
+ {
31
+ type: "taskItem",
32
+ attrs: { checked: false },
33
+ content: [
34
+ {
35
+ type: "paragraph",
36
+ content: [{ type: "text", text: "jhjkhjkjhkjh" }],
37
+ },
38
+ ],
39
+ },
40
+ {
41
+ type: "taskItem",
42
+ attrs: { checked: false },
43
+ content: [
44
+ {
45
+ type: "paragraph",
46
+ content: [{ type: "text", text: "hgfhfgh" }],
47
+ },
48
+ ],
49
+ },
50
+ ],
51
+ },
52
+ {
53
+ type: "paragraph",
54
+ content: [
55
+ { type: "text", text: "this is a " },
56
+ { type: "text", marks: [{ type: "italic" }], text: "basic" },
57
+ { type: "text", text: " example of " },
58
+ { type: "text", marks: [{ type: "bold" }], text: "Tiptap" },
59
+ {
60
+ type: "text",
61
+ text: ". Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists:",
62
+ },
63
+ ],
64
+ },
65
+ {
66
+ type: "bulletList",
67
+ content: [
68
+ {
69
+ type: "listItem",
70
+ attrs: { color: "" },
71
+ content: [
72
+ {
73
+ type: "paragraph",
74
+ content: [
75
+ { type: "text", text: "That’s a bullet list with one …" },
76
+ ],
77
+ },
78
+ ],
79
+ },
80
+ {
81
+ type: "listItem",
82
+ attrs: { color: "" },
83
+ content: [
84
+ {
85
+ type: "paragraph",
86
+ content: [{ type: "text", text: "… or two list items." }],
87
+ },
88
+ ],
89
+ },
90
+ ],
91
+ },
92
+ {
93
+ type: "paragraph",
94
+ content: [
95
+ {
96
+ type: "text",
97
+ text: "Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block:",
98
+ },
99
+ ],
100
+ },
101
+ {
102
+ type: "codeBlock",
103
+ attrs: { language: "css" },
104
+ content: [{ type: "text", text: "body {\n display: none;\n}" }],
105
+ },
106
+ {
107
+ type: "paragraph",
108
+ content: [
109
+ {
110
+ type: "text",
111
+ text: "I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too.",
112
+ },
113
+ ],
114
+ },
115
+ {
116
+ type: "blockquote",
117
+ content: [
118
+ {
119
+ type: "paragraph",
120
+ content: [
121
+ {
122
+ type: "text",
123
+ text: "Wow, that’s amazing. Good work, boy! 👏 ",
124
+ },
125
+ { type: "hardBreak" },
126
+ { type: "text", text: "— Mom" },
127
+ ],
128
+ },
129
+ ],
130
+ },
131
+ { type: "paragraph" },
132
+ // {
133
+ // type: "MediaGridComponent",
134
+ // attrs: { cols: 2 },
135
+ // },
136
+ // {
137
+ // type: "SvelteEditableComponent",
138
+ // content: [
139
+ // {
140
+ // type: "text",
141
+ // text: "Editable component",
142
+ // },
143
+ // ],
144
+ // }
145
+ ];
146
+ export const HEADINGS = [
147
+ {
148
+ level: 1,
149
+ ariaLabel: "H1",
150
+ icon: `<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 20H11V13H4V20H2V4H4V11H11V4H13V20ZM21.0005 8V20H19.0005L19 10.204L17 10.74V8.67L19.5005 8H21.0005Z"></path></svg>`
151
+ },
152
+ {
153
+ level: 2,
154
+ ariaLabel: "H2",
155
+ icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4 4V11H11V4H13V20H11V13H4V20H2V4H4ZM18.5 8C20.5711 8 22.25 9.67893 22.25 11.75C22.25 12.6074 21.9623 13.3976 21.4781 14.0292L21.3302 14.2102L18.0343 18H22V20H15L14.9993 18.444L19.8207 12.8981C20.0881 12.5908 20.25 12.1893 20.25 11.75C20.25 10.7835 19.4665 10 18.5 10C17.5818 10 16.8288 10.7071 16.7558 11.6065L16.75 11.75H14.75C14.75 9.67893 16.4289 8 18.5 8Z"></path></svg>`
156
+ },
157
+ {
158
+ level: 3,
159
+ ariaLabel: "H3",
160
+ icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M22 8L21.9984 10L19.4934 12.883C21.0823 13.3184 22.25 14.7728 22.25 16.5C22.25 18.5711 20.5711 20.25 18.5 20.25C16.674 20.25 15.1528 18.9449 14.8184 17.2166L16.7821 16.8352C16.9384 17.6413 17.6481 18.25 18.5 18.25C19.4665 18.25 20.25 17.4665 20.25 16.5C20.25 15.5335 19.4665 14.75 18.5 14.75C18.214 14.75 17.944 14.8186 17.7056 14.9403L16.3992 13.3932L19.3484 10H15V8H22ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4Z"></path></svg>`
161
+ },
162
+ {
163
+ level: 4,
164
+ ariaLabel: "H4",
165
+ icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 20H11V13H4V20H2V4H4V11H11V4H13V20ZM22 8V16H23.5V18H22V20H20V18H14.5V16.66L19.5 8H22ZM20 11.133L17.19 16H20V11.133Z"></path></svg>`
166
+ },
167
+ {
168
+ level: 5,
169
+ ariaLabel: "H5",
170
+ icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M22 8V10H17.6769L17.2126 12.6358C17.5435 12.5472 17.8912 12.5 18.25 12.5C20.4591 12.5 22.25 14.2909 22.25 16.5C22.25 18.7091 20.4591 20.5 18.25 20.5C16.4233 20.5 14.8827 19.2756 14.4039 17.6027L16.3271 17.0519C16.5667 17.8881 17.3369 18.5 18.25 18.5C19.3546 18.5 20.25 17.6046 20.25 16.5C20.25 15.3954 19.3546 14.5 18.25 14.5C17.6194 14.5 17.057 14.7918 16.6904 15.2478L14.8803 14.3439L16 8H22ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4Z"></path></svg>`
171
+ },
172
+ {
173
+ level: 6,
174
+ ariaLabel: "H6",
175
+ icon: `<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M21.097 8L18.499 12.5C20.7091 12.5 22.5 14.2909 22.5 16.5C22.5 18.7091 20.7091 20.5 18.5 20.5C16.2909 20.5 14.5 18.7091 14.5 16.5C14.5 15.7636 14.699 15.0737 15.0461 14.4811L18.788 8H21.097ZM4 4V11H11V4H13V20H11V13H4V20H2V4H4ZM18.5 14.5C17.3954 14.5 16.5 15.3954 16.5 16.5C16.5 17.6046 17.3954 18.5 18.5 18.5C19.6046 18.5 20.5 17.6046 20.5 16.5C20.5 15.3954 19.6046 14.5 18.5 14.5Z"></path></svg>`
176
+ }
177
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flexiui/svelte-rich-text",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "A lightweight and flexible rich text editor component for Svelte",
5
5
  "keywords": [
6
6
  "svelte",
@@ -40,6 +40,7 @@
40
40
  "license": "MIT",
41
41
  "dependencies": {
42
42
  "@flexiui/svelte-dropdown": "^0.4.0",
43
+ "@floating-ui/dom": "^1.7.4",
43
44
  "@tiptap/core": "^3.0.9",
44
45
  "@tiptap/extension-highlight": "^3.6.6",
45
46
  "@tiptap/extension-text-align": "^3.6.6",