@lazlon-platform/html-editor 0.3.4 → 0.3.5

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.
@@ -149,39 +149,8 @@ function useTextMarksState(
149
149
  return null
150
150
  }
151
151
 
152
- // HACK:
153
- // Clicking on the toolbar blurs the editor, but for editing actions we need to track it.
154
- // We cannot do it purely in the `useFocusedTiptap` hook because the component using it
155
- // might render *after* the blur, e.g the sidebar, but the `useTextMarks` hook operates
156
- // on locally selected editor instances, e.g CalendarFields in the sidebar. We might be
157
- // able to preserve the local state with an <Activity> but I feel like it makes more sense
158
- // to handle it here, since there might only be one focused editor at a time either way.
159
- let lastFocusedEditor: EditableNode | null = null
160
-
161
- function getFocusedEditor(node: EditableNode[]) {
162
- const focused = node.find((e) => e.tiptap.isFocused)
163
-
164
- if (focused) {
165
- return (lastFocusedEditor = focused)
166
- }
167
-
168
- if (lastFocusedEditor && node.includes(lastFocusedEditor)) {
169
- return lastFocusedEditor
170
- }
171
-
172
- return null
173
- }
174
-
175
- export function blurNode(node: EditableNode) {
176
- if (lastFocusedEditor === node) {
177
- node.tiptap.commands.blur()
178
- node.tiptap.commands.selectAll()
179
- lastFocusedEditor = null
180
- }
181
- }
182
-
183
152
  function useFocusedTiptap(editables: EditableNode[]) {
184
- const [focused, setFocused] = useState(getFocusedEditor(editables))
153
+ const [focused, setFocused] = useState(EditableNode.getFocused(editables))
185
154
 
186
155
  useEffect(() => {
187
156
  const dispose = editables.map((e) => {
@@ -13,6 +13,7 @@ export type EditableNodeProps = NodeProps &
13
13
  export abstract class EditableNode extends Node {
14
14
  @state accessor contentRef: HTMLElement | null = null
15
15
  @state private accessor _isEmpty: boolean = true
16
+ @state private accessor _isFocused: boolean = false
16
17
  @state private accessor _content: JSONContent = { type: "doc", content: [] }
17
18
 
18
19
  @state accessor lineHeight: number
@@ -33,6 +34,10 @@ export abstract class EditableNode extends Node {
33
34
  return this._isEmpty
34
35
  }
35
36
 
37
+ @computed get isFocused() {
38
+ return this._isFocused
39
+ }
40
+
36
41
  set locked(isLocked: boolean) {
37
42
  super.locked = isLocked
38
43
  // this is invoked from super() which has no tiptap, hence the ?
@@ -45,12 +50,25 @@ export abstract class EditableNode extends Node {
45
50
 
46
51
  readonly tiptap = new Tiptap({
47
52
  extensions: TiptapExtensions,
48
- onUpdate: () => {
49
- this._content = this.tiptap.getJSON()
50
- this._isEmpty = this.tiptap.isEmpty
53
+ onUpdate: ({ editor }) => {
54
+ this._content = editor.getJSON()
55
+ this._isEmpty = editor.isEmpty
56
+ },
57
+ onFocus: () => {
58
+ /**
59
+ * The blur event does not work, because we hide the UI before.
60
+ * {@link EditableNode.blur} is invoked in `EditableContent`
61
+ */
62
+ this._isFocused = true
51
63
  },
52
64
  })
53
65
 
66
+ blur() {
67
+ this.tiptap.commands.selectAll()
68
+ this._isFocused = false
69
+ EditableNode.lastFocused = null
70
+ }
71
+
54
72
  constructor(
55
73
  editor: Editor,
56
74
  page: Page,
@@ -81,4 +99,25 @@ export abstract class EditableNode extends Node {
81
99
  serialize(): SerializedNode<string, EditableNodeProps> {
82
100
  return super.serialize()
83
101
  }
102
+
103
+ /**
104
+ * {@link Tiptap.isFocused} uses the DOM element which gets blurred when
105
+ * an interactive element, such as a button is clicked on another panel.
106
+ * We need to track the last focused tiptap instance for editing purposes.
107
+ */
108
+ private static lastFocused: EditableNode | null = null
109
+
110
+ static getFocused(node: EditableNode[]) {
111
+ const focused = node.find((e) => e.tiptap.isFocused)
112
+
113
+ if (focused) {
114
+ return (this.lastFocused = focused)
115
+ }
116
+
117
+ if (this.lastFocused && node.includes(this.lastFocused)) {
118
+ return this.lastFocused
119
+ }
120
+
121
+ return null
122
+ }
84
123
  }
@@ -4,7 +4,6 @@ import { debounce, isEqual } from "es-toolkit"
4
4
  import { useCallback, useEffect, useRef, useState } from "react"
5
5
  import { useStore } from "react-bolt"
6
6
  import { useEditor } from "../../hooks/editor"
7
- import { blurNode } from "../../hooks/textMarks"
8
7
  import type { EditableNode, EditableNodeProps } from "../../model/node/editable"
9
8
  import { useDoubleClick } from "./useDoubleClick"
10
9
 
@@ -38,7 +37,7 @@ export function EditableContent(props: {
38
37
 
39
38
  useEffect(() => {
40
39
  if (!selection.has(node)) {
41
- blurNode(node)
40
+ node.blur()
42
41
  setClicked(false)
43
42
  }
44
43
  }, [selection, node])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazlon-platform/html-editor",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "lib"