@domternal/vue 0.9.1 → 0.11.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 CHANGED
@@ -21,14 +21,14 @@ See <u>[Packages & Bundle Size](https://domternal.dev/v1/packages)</u> for a ful
21
21
  - **Vue components** - composable `Domternal` component, `useEditor`/`useEditorState` composables, toolbar, bubble menu, floating menu, emoji picker, notion color picker, custom node views (Vue 3.3+)
22
22
  - **Vanilla wrapper** - framework-free class-based API for Astro, Svelte, Solid, plain HTML, and Web Components - editor, toolbar, bubble menu, floating menu, emoji picker, notion color picker
23
23
  - **Notion-style block UX** - drag-to-reorder, block context menu, slash command, smart paste, keyboard reorder, floating Table of Contents
24
- - **65+ extensions across 15 packages** - nodes, marks, and behavior extensions
25
- - **120+ chainable commands** - `editor.chain().focus().toggleBold().run()`
24
+ - **70+ extensions across 16 packages** - nodes, marks, and behavior extensions
25
+ - **125+ chainable commands** - `editor.chain().focus().toggleBold().run()`
26
26
  - **Full table support** - cell merging, column resize, row/column controls, cell toolbar, all free and MIT licensed
27
27
  - **Tree-shakeable** - import only what you use, your bundler strips the rest
28
28
  - **~44 KB gzipped** (own code), <u>[see Packages](https://domternal.dev/v1/packages)</u> for full bundle breakdown with ProseMirror
29
29
  - **TypeScript first** - 100% typed, zero `any`
30
30
  - **15,000+ tests** - 4,000+ unit and 11,000+ E2E across 230+ Playwright specs and 4 demo apps
31
- - **Light and dark theme** - 70+ CSS custom properties for full visual control
31
+ - **Light and dark theme** - 120+ CSS custom properties for full visual control
32
32
  - **Inline styles export** - `getHTML({ styled: true })` produces inline CSS ready for email clients, CMS, and Google Docs
33
33
  - **SSR helpers** - `generateHTML`, `generateJSON`, `generateText` for server-side rendering
34
34
 
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, h, computed, Fragment, ref, watch, shallowRef, onMounted, nextTick, onScopeDispose, watchEffect, Teleport, inject, provide, getCurrentInstance, customRef, markRaw, shallowReactive, render } from 'vue';
2
- import { positionFloatingOnce, PluginKey, positionFloating, ToolbarController, createFloatingMenuPlugin, FloatingMenuController, defaultIcons, defaultBubbleContexts, createBubbleMenuPlugin, Editor, Document, Paragraph, Text, BaseKeymap, History } from '@domternal/core';
2
+ import { positionFloatingOnce, PluginKey, positionFloating, ToolbarController, refocusEditorAfterCommand, createFloatingMenuPlugin, FloatingMenuController, defaultIcons, defaultBubbleContexts, createBubbleMenuPlugin, Editor, Document, Paragraph, Text, BaseKeymap, History } from '@domternal/core';
3
3
  export { Editor, generateHTML, generateJSON, generateText } from '@domternal/core';
4
4
 
5
5
  // src/useEditor.ts
@@ -10,7 +10,7 @@ function useEditor(options = {}) {
10
10
  let pendingContent = null;
11
11
  function wireEvents(ed) {
12
12
  ed.on("transaction", ({ transaction }) => {
13
- if (transaction.docChanged) {
13
+ if (transaction.docChanged && !transaction.getMeta("skipUpdate")) {
14
14
  options.onUpdate?.({ editor: ed });
15
15
  }
16
16
  if (!transaction.docChanged && transaction.selectionSet) {
@@ -40,17 +40,19 @@ function useEditor(options = {}) {
40
40
  options.onCreate?.(ed);
41
41
  return ed;
42
42
  }
43
- function destroyCurrentEditor() {
43
+ function destroyCurrentEditor(insertClone = true) {
44
44
  const current = editor.value;
45
45
  if (current && !current.isDestroyed) {
46
46
  pendingContent = current.getJSON();
47
47
  options.onDestroy?.();
48
- const dom = current.view.dom;
49
- const parent = dom.parentNode;
50
- if (parent) {
51
- const clone = dom.cloneNode(true);
52
- clone.style.pointerEvents = "none";
53
- parent.insertBefore(clone, dom);
48
+ if (insertClone) {
49
+ const dom = current.view.dom;
50
+ const parent = dom.parentNode;
51
+ if (parent) {
52
+ const clone = dom.cloneNode(true);
53
+ clone.style.pointerEvents = "none";
54
+ parent.insertBefore(clone, dom);
55
+ }
54
56
  }
55
57
  current.destroy();
56
58
  }
@@ -61,7 +63,14 @@ function useEditor(options = {}) {
61
63
  createEditorInstance(element, options.content ?? "", options.autofocus ?? false);
62
64
  }
63
65
  onMounted(() => {
64
- if (editor.value) return;
66
+ const ed = editor.value;
67
+ if (ed) {
68
+ const mount = editorRef.value;
69
+ if (mount && ed.view.dom.parentElement !== mount) {
70
+ mount.appendChild(ed.view.dom);
71
+ }
72
+ return;
73
+ }
65
74
  const element = editorRef.value ?? document.createElement("div");
66
75
  const initialContent = pendingContent ?? options.content ?? "";
67
76
  pendingContent = null;
@@ -85,7 +94,7 @@ function useEditor(options = {}) {
85
94
  if (!editor.value || editor.value.isDestroyed) return;
86
95
  if (newExtensions === oldExtensions) return;
87
96
  const element = editor.value.view.dom.parentElement ?? document.createElement("div");
88
- destroyCurrentEditor();
97
+ destroyCurrentEditor(false);
89
98
  const initialContent = pendingContent ?? "";
90
99
  pendingContent = null;
91
100
  createEditorInstance(element, initialContent, false);
@@ -858,9 +867,7 @@ var DomternalToolbar = defineComponent({
858
867
  return;
859
868
  }
860
869
  executeCommand(item);
861
- requestAnimationFrame(() => {
862
- editor.view.focus();
863
- });
870
+ refocusEditorAfterCommand(editor.view);
864
871
  }
865
872
  function onDropdownItemClick(item, event) {
866
873
  const editor = props.editor ?? contextEditor.value;
@@ -876,9 +883,7 @@ var DomternalToolbar = defineComponent({
876
883
  } else {
877
884
  executeCommand(item);
878
885
  }
879
- requestAnimationFrame(() => {
880
- editor.view.focus();
881
- });
886
+ refocusEditorAfterCommand(editor.view);
882
887
  }
883
888
  function onButtonFocus(name) {
884
889
  const index = controllerRef.current?.getFlatIndex(name) ?? -1;
@@ -1385,9 +1390,7 @@ var DomternalBubbleMenu = defineComponent({
1385
1390
  const ed = editorRef.value;
1386
1391
  if (!ed) return;
1387
1392
  ToolbarController.executeItem(ed, sub);
1388
- requestAnimationFrame(() => {
1389
- ed.view.focus();
1390
- });
1393
+ refocusEditorAfterCommand(ed.view);
1391
1394
  };
1392
1395
  const colorBtnRef = ref();
1393
1396
  const blockMenuBtnRef = ref();
@@ -1700,9 +1703,7 @@ var DomternalFloatingMenu = defineComponent({
1700
1703
  const ed = currentEditor;
1701
1704
  if (!ctl || !ed) return;
1702
1705
  ctl.execute(item);
1703
- requestAnimationFrame(() => {
1704
- ed.view.focus();
1705
- });
1706
+ refocusEditorAfterCommand(ed.view);
1706
1707
  };
1707
1708
  const onMenuKeyDown = (e) => {
1708
1709
  const ctl = controller.value;