@prosekit/web 0.7.3 → 0.7.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.
Files changed (172) hide show
  1. package/dist/{get-default-state-CIEy7xrl.js → get-default-state-BzBimBWi.js} +2 -1
  2. package/dist/get-default-state-BzBimBWi.js.map +1 -0
  3. package/dist/{get-safe-editor-view-DENm4avv.js → get-safe-editor-view-Dt9Amrcn.js} +2 -1
  4. package/dist/get-safe-editor-view-Dt9Amrcn.js.map +1 -0
  5. package/dist/{inject-style-D5jj7cme.js → inject-style-Dm6W58W3.js} +6 -9
  6. package/dist/inject-style-Dm6W58W3.js.map +1 -0
  7. package/dist/prosekit-web-autocomplete.d.ts +2 -1
  8. package/dist/prosekit-web-autocomplete.d.ts.map +1 -0
  9. package/dist/prosekit-web-autocomplete.js +9 -13
  10. package/dist/prosekit-web-autocomplete.js.map +1 -0
  11. package/dist/prosekit-web-block-handle.d.ts +2 -1
  12. package/dist/prosekit-web-block-handle.d.ts.map +1 -0
  13. package/dist/prosekit-web-block-handle.js +31 -31
  14. package/dist/prosekit-web-block-handle.js.map +1 -0
  15. package/dist/prosekit-web-drop-indicator.d.ts +2 -1
  16. package/dist/prosekit-web-drop-indicator.d.ts.map +1 -0
  17. package/dist/prosekit-web-drop-indicator.js +6 -6
  18. package/dist/prosekit-web-drop-indicator.js.map +1 -0
  19. package/dist/prosekit-web-inline-popover.d.ts +2 -1
  20. package/dist/prosekit-web-inline-popover.d.ts.map +1 -0
  21. package/dist/prosekit-web-inline-popover.js +8 -12
  22. package/dist/prosekit-web-inline-popover.js.map +1 -0
  23. package/dist/prosekit-web-popover.d.ts +2 -1
  24. package/dist/prosekit-web-popover.d.ts.map +1 -0
  25. package/dist/prosekit-web-popover.js +2 -1
  26. package/dist/prosekit-web-popover.js.map +1 -0
  27. package/dist/prosekit-web-resizable.d.ts +2 -1
  28. package/dist/prosekit-web-resizable.d.ts.map +1 -0
  29. package/dist/prosekit-web-resizable.js +5 -7
  30. package/dist/prosekit-web-resizable.js.map +1 -0
  31. package/dist/prosekit-web-table-handle.d.ts +2 -1
  32. package/dist/prosekit-web-table-handle.d.ts.map +1 -0
  33. package/dist/prosekit-web-table-handle.js +39 -72
  34. package/dist/prosekit-web-table-handle.js.map +1 -0
  35. package/dist/prosekit-web-tooltip.d.ts +2 -1
  36. package/dist/prosekit-web-tooltip.d.ts.map +1 -0
  37. package/dist/prosekit-web-tooltip.js +2 -1
  38. package/dist/prosekit-web-tooltip.js.map +1 -0
  39. package/dist/prosekit-web.js +1 -0
  40. package/dist/{use-editor-extension-Cc7ZG7uj.js → use-editor-extension-B2WuUfnd.js} +2 -1
  41. package/dist/use-editor-extension-B2WuUfnd.js.map +1 -0
  42. package/dist/{use-scrolling-BNfsQs3S.js → use-scrolling-BOvyjDvH.js} +2 -1
  43. package/dist/use-scrolling-BOvyjDvH.js.map +1 -0
  44. package/package.json +21 -21
  45. package/src/components/autocomplete/autocomplete-empty/element.gen.ts +18 -0
  46. package/src/components/autocomplete/autocomplete-empty/setup.ts +6 -0
  47. package/src/components/autocomplete/autocomplete-empty/types.ts +16 -0
  48. package/src/components/autocomplete/autocomplete-item/element.gen.ts +18 -0
  49. package/src/components/autocomplete/autocomplete-item/setup.ts +38 -0
  50. package/src/components/autocomplete/autocomplete-item/types.ts +31 -0
  51. package/src/components/autocomplete/autocomplete-list/element.gen.ts +18 -0
  52. package/src/components/autocomplete/autocomplete-list/setup.ts +140 -0
  53. package/src/components/autocomplete/autocomplete-list/types.ts +30 -0
  54. package/src/components/autocomplete/autocomplete-popover/element.gen.ts +18 -0
  55. package/src/components/autocomplete/autocomplete-popover/helpers.spec.ts +21 -0
  56. package/src/components/autocomplete/autocomplete-popover/helpers.ts +7 -0
  57. package/src/components/autocomplete/autocomplete-popover/setup.ts +185 -0
  58. package/src/components/autocomplete/autocomplete-popover/types.ts +103 -0
  59. package/src/components/autocomplete/context.ts +19 -0
  60. package/src/components/autocomplete/index.gen.ts +17 -0
  61. package/src/components/block-handle/block-handle-add/element.gen.ts +18 -0
  62. package/src/components/block-handle/block-handle-add/setup.ts +37 -0
  63. package/src/components/block-handle/block-handle-add/types.ts +26 -0
  64. package/src/components/block-handle/block-handle-draggable/element.gen.ts +18 -0
  65. package/src/components/block-handle/block-handle-draggable/set-drag-preview.ts +88 -0
  66. package/src/components/block-handle/block-handle-draggable/setup.ts +133 -0
  67. package/src/components/block-handle/block-handle-draggable/types.ts +26 -0
  68. package/src/components/block-handle/block-handle-popover/element.gen.ts +18 -0
  69. package/src/components/block-handle/block-handle-popover/pointer-move.ts +262 -0
  70. package/src/components/block-handle/block-handle-popover/setup.ts +88 -0
  71. package/src/components/block-handle/block-handle-popover/types.ts +84 -0
  72. package/src/components/block-handle/context.ts +34 -0
  73. package/src/components/block-handle/index.gen.ts +13 -0
  74. package/src/components/drop-indicator/drop-indicator/element.gen.ts +18 -0
  75. package/src/components/drop-indicator/drop-indicator/setup.ts +87 -0
  76. package/src/components/drop-indicator/drop-indicator/types.ts +34 -0
  77. package/src/components/drop-indicator/index.gen.ts +5 -0
  78. package/src/components/inline-popover/index.gen.ts +5 -0
  79. package/src/components/inline-popover/inline-popover/element.gen.ts +18 -0
  80. package/src/components/inline-popover/inline-popover/setup.ts +97 -0
  81. package/src/components/inline-popover/inline-popover/types.ts +115 -0
  82. package/src/components/inline-popover/inline-popover/virtual-selection-element.ts +75 -0
  83. package/src/components/popover/index.gen.ts +13 -0
  84. package/src/components/popover/popover-content/element.gen.ts +18 -0
  85. package/src/components/popover/popover-content/setup.ts +1 -0
  86. package/src/components/popover/popover-content/types.ts +12 -0
  87. package/src/components/popover/popover-root/element.gen.ts +18 -0
  88. package/src/components/popover/popover-root/setup.ts +1 -0
  89. package/src/components/popover/popover-root/types.ts +12 -0
  90. package/src/components/popover/popover-trigger/element.gen.ts +18 -0
  91. package/src/components/popover/popover-trigger/setup.ts +1 -0
  92. package/src/components/popover/popover-trigger/types.ts +12 -0
  93. package/src/components/resizable/context.ts +45 -0
  94. package/src/components/resizable/index.gen.ts +9 -0
  95. package/src/components/resizable/resizable-handle/calc-resize.spec.ts +280 -0
  96. package/src/components/resizable/resizable-handle/calc-resize.ts +121 -0
  97. package/src/components/resizable/resizable-handle/element.gen.ts +18 -0
  98. package/src/components/resizable/resizable-handle/setup.ts +112 -0
  99. package/src/components/resizable/resizable-handle/types.ts +32 -0
  100. package/src/components/resizable/resizable-root/element.gen.ts +18 -0
  101. package/src/components/resizable/resizable-root/setup.ts +93 -0
  102. package/src/components/resizable/resizable-root/types.ts +28 -0
  103. package/src/components/table-handle/context.ts +49 -0
  104. package/src/components/table-handle/dnd.ts +135 -0
  105. package/src/components/table-handle/hooks/use-drop.ts +94 -0
  106. package/src/components/table-handle/hooks/use-empty-image.ts +30 -0
  107. package/src/components/table-handle/index.gen.ts +37 -0
  108. package/src/components/table-handle/table-handle-column-root/element.gen.ts +18 -0
  109. package/src/components/table-handle/table-handle-column-root/setup.ts +71 -0
  110. package/src/components/table-handle/table-handle-column-root/types.ts +76 -0
  111. package/src/components/table-handle/table-handle-column-trigger/element.gen.ts +18 -0
  112. package/src/components/table-handle/table-handle-column-trigger/setup.ts +75 -0
  113. package/src/components/table-handle/table-handle-column-trigger/types.ts +23 -0
  114. package/src/components/table-handle/table-handle-drag-preview/element.gen.ts +18 -0
  115. package/src/components/table-handle/table-handle-drag-preview/render-preview.ts +80 -0
  116. package/src/components/table-handle/table-handle-drag-preview/setup.ts +67 -0
  117. package/src/components/table-handle/table-handle-drag-preview/types.ts +17 -0
  118. package/src/components/table-handle/table-handle-drag-preview/updater.ts +101 -0
  119. package/src/components/table-handle/table-handle-drop-indicator/calc-drag-over.ts +44 -0
  120. package/src/components/table-handle/table-handle-drop-indicator/element.gen.ts +18 -0
  121. package/src/components/table-handle/table-handle-drop-indicator/setup.ts +56 -0
  122. package/src/components/table-handle/table-handle-drop-indicator/types.ts +18 -0
  123. package/src/components/table-handle/table-handle-drop-indicator/updater.ts +110 -0
  124. package/src/components/table-handle/table-handle-popover-content/element.gen.ts +18 -0
  125. package/src/components/table-handle/table-handle-popover-content/setup.ts +90 -0
  126. package/src/components/table-handle/table-handle-popover-content/types.ts +40 -0
  127. package/src/components/table-handle/table-handle-popover-item/element.gen.ts +18 -0
  128. package/src/components/table-handle/table-handle-popover-item/setup.ts +23 -0
  129. package/src/components/table-handle/table-handle-popover-item/types.ts +24 -0
  130. package/src/components/table-handle/table-handle-root/element.gen.ts +18 -0
  131. package/src/components/table-handle/table-handle-root/setup.ts +93 -0
  132. package/src/components/table-handle/table-handle-root/types.ts +26 -0
  133. package/src/components/table-handle/table-handle-row-root/element.gen.ts +18 -0
  134. package/src/components/table-handle/table-handle-row-root/setup.ts +77 -0
  135. package/src/components/table-handle/table-handle-row-root/types.ts +75 -0
  136. package/src/components/table-handle/table-handle-row-trigger/element.gen.ts +18 -0
  137. package/src/components/table-handle/table-handle-row-trigger/setup.ts +74 -0
  138. package/src/components/table-handle/table-handle-row-trigger/types.ts +26 -0
  139. package/src/components/table-handle/utils.ts +107 -0
  140. package/src/components/tooltip/index.gen.ts +13 -0
  141. package/src/components/tooltip/tooltip-content/element.gen.ts +18 -0
  142. package/src/components/tooltip/tooltip-content/setup.ts +1 -0
  143. package/src/components/tooltip/tooltip-content/types.ts +12 -0
  144. package/src/components/tooltip/tooltip-root/element.gen.ts +18 -0
  145. package/src/components/tooltip/tooltip-root/setup.ts +1 -0
  146. package/src/components/tooltip/tooltip-root/types.ts +12 -0
  147. package/src/components/tooltip/tooltip-trigger/element.gen.ts +18 -0
  148. package/src/components/tooltip/tooltip-trigger/setup.ts +1 -0
  149. package/src/components/tooltip/tooltip-trigger/types.ts +12 -0
  150. package/src/hooks/use-editor-extension.ts +19 -0
  151. package/src/hooks/use-editor-focus-event.ts +23 -0
  152. package/src/hooks/use-editor-typing.ts +36 -0
  153. package/src/hooks/use-editor-update-event.ts +23 -0
  154. package/src/hooks/use-first-rendering.ts +20 -0
  155. package/src/hooks/use-keymap.ts +20 -0
  156. package/src/hooks/use-scrolling.ts +33 -0
  157. package/src/hooks/use-selecting.ts +63 -0
  158. package/src/index.ts +1 -0
  159. package/src/utils/assign-styles.ts +14 -0
  160. package/src/utils/clone-element.ts +110 -0
  161. package/src/utils/css-feature-detection.ts +9 -0
  162. package/src/utils/fade-color.ts +15 -0
  163. package/src/utils/get-box-element.ts +20 -0
  164. package/src/utils/get-client-rect.ts +35 -0
  165. package/src/utils/get-default-state.spec.ts +50 -0
  166. package/src/utils/get-default-state.ts +22 -0
  167. package/src/utils/get-effective-background-color.ts +21 -0
  168. package/src/utils/get-safe-editor-view.ts +10 -0
  169. package/src/utils/inject-style.ts +11 -0
  170. package/src/utils/is-finite-positive-number.ts +3 -0
  171. package/src/utils/max-z-index.ts +3 -0
  172. package/src/utils/throttle.ts +17 -0
@@ -0,0 +1,133 @@
1
+ import {
2
+ useAttribute,
3
+ useEffect,
4
+ useEventListener,
5
+ type ConnectableElement,
6
+ type ReadonlySignal,
7
+ type SignalState,
8
+ } from '@aria-ui/core'
9
+ import { isHTMLElement } from '@ocavue/utils'
10
+ import type { Editor } from '@prosekit/core'
11
+ import type { ViewDragging } from '@prosekit/extensions/drop-indicator'
12
+ import {
13
+ Fragment,
14
+ Slice,
15
+ } from '@prosekit/pm/model'
16
+ import { NodeSelection } from '@prosekit/pm/state'
17
+ import type { EditorView } from '@prosekit/pm/view'
18
+
19
+ import { getBoxElement } from '../../../utils/get-box-element'
20
+ import { getSafeEditorView } from '../../../utils/get-safe-editor-view'
21
+ import {
22
+ blockPopoverContext,
23
+ draggingContext,
24
+ type BlockPopoverContext,
25
+ type HoverState,
26
+ } from '../context'
27
+
28
+ import { setDragPreview } from './set-drag-preview'
29
+ import type { BlockHandleDraggableProps } from './types'
30
+
31
+ /**
32
+ * @internal
33
+ */
34
+ export function useBlockHandleDraggable(
35
+ host: ConnectableElement,
36
+ { state }: { state: SignalState<BlockHandleDraggableProps> },
37
+ ): void {
38
+ const context = blockPopoverContext.consume(host)
39
+ const dragging = draggingContext.consume(host)
40
+
41
+ useEffect(host, () => {
42
+ host.draggable = true
43
+ })
44
+
45
+ usePointerDownHandler(host, context, state.editor)
46
+
47
+ useEventListener(host, 'dragstart', (event) => {
48
+ dragging.set(true)
49
+
50
+ const view = getSafeEditorView(state.editor.get())
51
+ const hoverState = context.get()
52
+
53
+ if (view && hoverState) {
54
+ view.dom.classList.add('prosekit-dragging')
55
+ createDraggingPreview(view, hoverState, event)
56
+ setViewDragging(view, hoverState)
57
+ }
58
+ })
59
+
60
+ useEventListener(host, 'dragend', () => {
61
+ dragging.set(false)
62
+
63
+ const view = getSafeEditorView(state.editor.get())
64
+ if (view) {
65
+ view.dom.classList.remove('prosekit-dragging')
66
+ }
67
+ })
68
+
69
+ useAttribute(host, 'data-dragging', () => (dragging.get() ? '' : undefined))
70
+ }
71
+
72
+ function usePointerDownHandler(
73
+ host: ConnectableElement,
74
+ context: ReadonlySignal<BlockPopoverContext>,
75
+ editor: ReadonlySignal<Editor | null>,
76
+ ) {
77
+ useEventListener(host, 'pointerdown', () => {
78
+ const { pos } = context.get() ?? {}
79
+ const { view } = editor.get() ?? {}
80
+
81
+ if (pos == null || view == null) {
82
+ return
83
+ }
84
+
85
+ view.dispatch(
86
+ view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos)),
87
+ )
88
+
89
+ // Clicking the handle will blur the editor, so we need to focus it again.
90
+ // We cannot call `event.preventDefault()` here to prevent the blur
91
+ // because it will prevent the drag event from firing.
92
+ requestAnimationFrame(() => {
93
+ view.focus()
94
+ })
95
+ })
96
+ }
97
+
98
+ function createDraggingPreview(view: EditorView, hoverState: HoverState, event: DragEvent): void {
99
+ if (!event.dataTransfer) {
100
+ return
101
+ }
102
+
103
+ const { pos } = hoverState
104
+
105
+ const element = view.nodeDOM(pos)
106
+ if (!element || !isHTMLElement(element)) {
107
+ return
108
+ }
109
+
110
+ const boxElement = getBoxElement(element)
111
+ if (!boxElement || !isHTMLElement(boxElement)) {
112
+ return
113
+ }
114
+
115
+ event.dataTransfer.clearData()
116
+ event.dataTransfer.setData('text/html', boxElement.outerHTML)
117
+ event.dataTransfer.effectAllowed = 'copyMove'
118
+ setDragPreview(event, boxElement)
119
+
120
+ return
121
+ }
122
+
123
+ function setViewDragging(view: EditorView, hoverState: HoverState): void {
124
+ const { node, pos } = hoverState
125
+
126
+ const dragging: ViewDragging = {
127
+ slice: new Slice(Fragment.from(node), 0, 0),
128
+ move: true,
129
+ node: NodeSelection.create(view.state.doc, pos),
130
+ }
131
+
132
+ view.dragging = dragging
133
+ }
@@ -0,0 +1,26 @@
1
+ import type {
2
+ EventDeclarations,
3
+ PropDeclarations,
4
+ } from '@aria-ui/core'
5
+ import type { Editor } from '@prosekit/core'
6
+
7
+ export interface BlockHandleDraggableProps {
8
+ /**
9
+ * The ProseKit editor instance.
10
+ *
11
+ * @default null
12
+ * @hidden
13
+ */
14
+ editor: Editor | null
15
+ }
16
+
17
+ /** @internal */
18
+ export const blockHandleDraggableProps: PropDeclarations<BlockHandleDraggableProps> = {
19
+ editor: { default: null },
20
+ }
21
+
22
+ /** @internal */
23
+ export interface BlockHandleDraggableEvents {}
24
+
25
+ /** @internal */
26
+ export const blockHandleDraggableEvents: EventDeclarations<BlockHandleDraggableEvents> = {}
@@ -0,0 +1,18 @@
1
+ import { defineCustomElement, registerCustomElement, type BaseElementConstructor } from "@aria-ui/core"
2
+
3
+ import { useBlockHandlePopover } from "./setup"
4
+ import { blockHandlePopoverEvents, blockHandlePopoverProps, type BlockHandlePopoverEvents, type BlockHandlePopoverProps } from "./types"
5
+
6
+ const BlockHandlePopoverElementBase: BaseElementConstructor<BlockHandlePopoverProps> = defineCustomElement<
7
+ BlockHandlePopoverProps,
8
+ BlockHandlePopoverEvents
9
+ >({
10
+ props: blockHandlePopoverProps,
11
+ events: blockHandlePopoverEvents,
12
+ setup: useBlockHandlePopover,
13
+ })
14
+ class BlockHandlePopoverElement extends BlockHandlePopoverElementBase {}
15
+
16
+ registerCustomElement('prosekit-block-handle-popover', BlockHandlePopoverElement)
17
+
18
+ export { BlockHandlePopoverElement }
@@ -0,0 +1,262 @@
1
+ import type { VirtualElement } from '@floating-ui/dom'
2
+ import {
3
+ isElement,
4
+ isHTMLElement,
5
+ isTextNode,
6
+ } from '@ocavue/utils'
7
+ import {
8
+ defineDOMEventHandler,
9
+ union,
10
+ type PlainExtension,
11
+ } from '@prosekit/core'
12
+ import type { ProseMirrorNode } from '@prosekit/pm/model'
13
+ import type { EditorView } from '@prosekit/pm/view'
14
+
15
+ import { getClientRect } from '../../../utils/get-client-rect'
16
+ import { throttle } from '../../../utils/throttle'
17
+ import type { HoverState } from '../context'
18
+
19
+ export type ElementHoverHandler = (
20
+ reference: VirtualElement | null,
21
+ hoverState: HoverState | null,
22
+ ) => void
23
+
24
+ export function defineElementHoverHandler(handler: ElementHoverHandler): PlainExtension {
25
+ const handleElement = (
26
+ node: ProseMirrorNode,
27
+ pos: number,
28
+ element: HTMLElement,
29
+ parentElement?: Node | null,
30
+ ) => {
31
+ const reference: VirtualElement = {
32
+ contextElement: element,
33
+
34
+ getBoundingClientRect: () => {
35
+ const rect = findFirstLineRect(parentElement, element)
36
+ return rect ? fulfillRect(rect) : fallbackRect
37
+ },
38
+ }
39
+
40
+ handler(reference, { node, pos })
41
+ }
42
+
43
+ let lastX = -1
44
+ let lastY = -1
45
+ let lastTime = -1
46
+
47
+ const handlePointerEvent = (view: EditorView, event: PointerEvent) => {
48
+ const { x, y } = event
49
+
50
+ // Simple performance optimization. If the pointer is not moving, we don't
51
+ // want to recalculate the block handle position within a short period of
52
+ // time window.
53
+ if (lastX === x && lastY === y) {
54
+ const now = Date.now()
55
+ if (now - lastTime < 100) {
56
+ return
57
+ }
58
+ lastTime = now
59
+ }
60
+ lastX = x
61
+ lastY = y
62
+
63
+ const block = findBlockByCoords(view, x, y)
64
+ if (!block) {
65
+ handler(null, null)
66
+ return
67
+ }
68
+
69
+ const { node, pos } = block
70
+ const element = view.nodeDOM(pos)
71
+ if (!element || !isHTMLElement(element)) {
72
+ handler(null, null)
73
+ return
74
+ }
75
+
76
+ // If `node` is the first child of another non-doc block node, for example a
77
+ // list node or a blockquote node, we want to put the block handle agains
78
+ // the parent node.
79
+ const $pos = view.state.doc.resolve(pos)
80
+ if ($pos.depth > 0 && $pos.index($pos.depth) === 0) {
81
+ const parentPos = $pos.before($pos.depth)
82
+ const parentNode = $pos.parent
83
+ const parentElement = view.nodeDOM(parentPos)
84
+ handleElement(parentNode, parentPos, element, parentElement)
85
+ } else {
86
+ handleElement(node, pos, element)
87
+ }
88
+ }
89
+
90
+ return union(
91
+ defineDOMEventHandler('pointermove', throttle(handlePointerEvent, 200)),
92
+ defineDOMEventHandler('pointerenter', handlePointerEvent),
93
+ defineDOMEventHandler('pointerout', handlePointerEvent),
94
+ defineDOMEventHandler('keypress', () => handler(null, null)),
95
+ )
96
+ }
97
+
98
+ function findBlockByCoords(view: EditorView, x: number, y: number): { node: ProseMirrorNode; pos: number } | undefined {
99
+ const rect = getClientRect(view.dom)
100
+ if (!isWithinRect(rect, x, y)) {
101
+ return
102
+ }
103
+
104
+ let parent: ProseMirrorNode | undefined = view.state.doc
105
+ let pos = -1
106
+
107
+ while (parent) {
108
+ if (parent.isBlock && (parent.isTextblock || parent.isAtom || parent.type.spec.isolating)) {
109
+ return { node: parent, pos }
110
+ }
111
+
112
+ // Collect all children and their positions
113
+ const children: ProseMirrorNode[] = []
114
+ const positions: number[] = []
115
+ parent.forEach((child, offset) => {
116
+ children.push(child)
117
+ positions.push(offset + pos + 1)
118
+ })
119
+
120
+ let lo = 0
121
+ let hi = children.length - 1
122
+
123
+ while (lo <= hi) {
124
+ const i = hi - ((hi - lo) >> 1)
125
+ const childDOM = view.nodeDOM(positions[i])
126
+ const childRect = getNodeRect(childDOM)
127
+ if (!childRect) {
128
+ console.warn(`[prosekit] Unable to get rect at position: ${positions[i]}`)
129
+ return
130
+ }
131
+ if (childRect.top > y) {
132
+ hi = i - 1
133
+ } else if (childRect.bottom < y) {
134
+ lo = i + 1
135
+ } else {
136
+ lo = i
137
+ break
138
+ }
139
+ }
140
+
141
+ if (lo > hi) {
142
+ return
143
+ }
144
+
145
+ parent = children[lo]
146
+ pos = positions[lo]
147
+ }
148
+ }
149
+
150
+ function getNodeRect(node: Node | null | undefined): Rect | undefined {
151
+ if (node && isElement(node) && node.isConnected) {
152
+ return getClientRect(node)
153
+ }
154
+ }
155
+
156
+ function isWithinRect(rect: Rect, x: number, y: number) {
157
+ return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
158
+ }
159
+
160
+ interface Rect {
161
+ top: number
162
+ right: number
163
+ bottom: number
164
+ left: number
165
+ }
166
+
167
+ function findFirstLineRect(outer?: Node | null, inner?: Node | null): Rect | undefined {
168
+ if (outer && !outer.isConnected) {
169
+ return
170
+ }
171
+ if (inner && !inner.isConnected) {
172
+ return
173
+ }
174
+
175
+ if (outer && inner) {
176
+ const outerRect = findOuterRect(outer)
177
+ const innerRect = findFirstLineRectInNode(inner)
178
+ if (outerRect && innerRect) {
179
+ const { top, bottom } = innerRect
180
+ const { left, right } = outerRect
181
+ return { top, bottom, left, right }
182
+ } else {
183
+ return outerRect || innerRect
184
+ }
185
+ } else if (outer) {
186
+ return findFirstLineRectInNode(outer)
187
+ } else if (inner) {
188
+ return findFirstLineRectInNode(inner)
189
+ }
190
+ }
191
+
192
+ function findOuterRect(node: Node): Rect | undefined {
193
+ if (!isElement(node)) {
194
+ return
195
+ }
196
+
197
+ const rect = getClientRect(node)
198
+ const style = node.ownerDocument.defaultView?.getComputedStyle(node)
199
+ const marginLeft = style && Number.parseInt(style.marginLeft, 10) || 0
200
+ const marginRight = style && Number.parseInt(style.marginRight, 10) || 0
201
+ const left = rect.left - marginLeft
202
+ const right = rect.right + marginRight
203
+
204
+ return { top: rect.top, bottom: rect.bottom, left, right }
205
+ }
206
+
207
+ function findFirstLineRectInNode(node: Node): Rect | undefined {
208
+ if (isElement(node)) {
209
+ return findFirstLineRectInElement(node)
210
+ } else if (isTextNode(node)) {
211
+ return findFirstLineRectInTextNode(node)
212
+ }
213
+ }
214
+
215
+ function findFirstLineRectInTextNode(node: Text): Rect | undefined {
216
+ const ownerDocument = node.ownerDocument
217
+ if (!ownerDocument) {
218
+ return
219
+ }
220
+ const range = ownerDocument.createRange()
221
+ range.setStart(node, 0)
222
+ range.setEnd(node, 0)
223
+ const rects = range.getClientRects()
224
+ return rects[0]
225
+ }
226
+
227
+ function findFirstLineRectInElement(element: Element): Rect | undefined {
228
+ if (element.nodeName === 'BR') {
229
+ return element.getBoundingClientRect()
230
+ }
231
+
232
+ const rect = getClientRect(element)
233
+ const style = element.ownerDocument.defaultView?.getComputedStyle(element)
234
+ const marginLeft = style && Number.parseInt(style.marginLeft, 10) || 0
235
+ const marginRight = style && Number.parseInt(style.marginRight, 10) || 0
236
+ const left = rect.left - marginLeft
237
+ const right = rect.right + marginRight
238
+
239
+ const lineHeight = style && Number.parseInt(style.lineHeight, 10) || 24
240
+ const paddingTop = style && Number.parseInt(style.paddingTop, 10) || 0
241
+ const borderTop = style && Number.parseInt(style.borderTopWidth, 10) || 0
242
+ const top = rect.top + paddingTop + borderTop
243
+ const bottom = top + lineHeight
244
+
245
+ return { top, bottom, left, right }
246
+ }
247
+
248
+ function fulfillRect({ top, right, bottom, left }: Rect) {
249
+ return { top, right, bottom, left, width: right - left, height: bottom - top, x: left, y: top }
250
+ }
251
+
252
+ // A fallback rect that is far away from the screen. It should not be used through.
253
+ const fallbackRect = Object.freeze({
254
+ top: -9999,
255
+ right: -9999,
256
+ bottom: -9999,
257
+ left: -9999,
258
+ width: 0,
259
+ height: 0,
260
+ x: -9999,
261
+ y: -9999,
262
+ })
@@ -0,0 +1,88 @@
1
+ import {
2
+ createComputed,
3
+ createSignal,
4
+ useAttribute,
5
+ type ConnectableElement,
6
+ type ReadonlySignal,
7
+ type SetupOptions,
8
+ } from '@aria-ui/core'
9
+ import { useOverlayPositionerState } from '@aria-ui/overlay/elements'
10
+ import { usePresence } from '@aria-ui/presence'
11
+ import type { VirtualElement } from '@floating-ui/dom'
12
+ import type { Editor } from '@prosekit/core'
13
+
14
+ import { useEditorExtension } from '../../../hooks/use-editor-extension'
15
+ import { useScrolling } from '../../../hooks/use-scrolling'
16
+ import {
17
+ blockPopoverContext,
18
+ draggingContext,
19
+ type BlockPopoverContext,
20
+ type HoverState,
21
+ } from '../context'
22
+
23
+ import {
24
+ defineElementHoverHandler,
25
+ type ElementHoverHandler,
26
+ } from './pointer-move'
27
+ import type {
28
+ BlockHandlePopoverEvents,
29
+ BlockHandlePopoverProps,
30
+ } from './types'
31
+
32
+ /**
33
+ * @internal
34
+ */
35
+ export function useBlockHandlePopover(
36
+ host: ConnectableElement,
37
+ { state, emit }: SetupOptions<BlockHandlePopoverProps, BlockHandlePopoverEvents>,
38
+ ): void {
39
+ const { editor, ...overlayState } = state
40
+ const reference = createSignal<VirtualElement | null>(null)
41
+ useOverlayPositionerState(host, overlayState, { reference })
42
+
43
+ const context = createSignal<BlockPopoverContext>(null)
44
+ blockPopoverContext.provide(host, context)
45
+
46
+ const dragging = createSignal(false)
47
+ draggingContext.provide(host, dragging)
48
+
49
+ const scrolling = useScrolling(host)
50
+ const open = createComputed(() => {
51
+ return !!context.get() && !scrolling.get()
52
+ })
53
+
54
+ useHoverExtension(host, editor, (referenceValue, hoverState) => {
55
+ reference.set(referenceValue)
56
+ context.set(hoverState)
57
+ const stateChangeDetails = hoverState ? { node: hoverState.node, pos: hoverState.pos } : null
58
+ emit('stateChange', stateChangeDetails)
59
+ })
60
+
61
+ useAttribute(host, 'data-state', () => (open.get() ? 'open' : 'closed'))
62
+ usePresence(host, open)
63
+ }
64
+
65
+ function useHoverExtension(
66
+ host: ConnectableElement,
67
+ editor: ReadonlySignal<Editor | null>,
68
+ handler: ElementHoverHandler,
69
+ ) {
70
+ let prevHoverState: HoverState | null = null
71
+
72
+ const extension = defineElementHoverHandler((reference, hoverState) => {
73
+ if (isHoverStateEqual(prevHoverState, hoverState)) {
74
+ return
75
+ }
76
+
77
+ prevHoverState = hoverState
78
+ handler(reference, hoverState)
79
+ })
80
+
81
+ useEditorExtension(host, editor, extension)
82
+ }
83
+
84
+ function isHoverStateEqual(a: HoverState | null, b: HoverState | null) {
85
+ if (!a && !b) return true
86
+ if (!a || !b) return false
87
+ return a.pos === b.pos && a.node.eq(b.node)
88
+ }
@@ -0,0 +1,84 @@
1
+ import type {
2
+ EventDeclarations,
3
+ PropDeclarations,
4
+ } from '@aria-ui/core'
5
+ import {
6
+ overlayPositionerEvents,
7
+ overlayPositionerProps,
8
+ type OverlayPositionerEvents,
9
+ type OverlayPositionerProps,
10
+ } from '@aria-ui/overlay/elements'
11
+ import type { Placement } from '@floating-ui/dom'
12
+ import type { Editor } from '@prosekit/core'
13
+ import type { ProseMirrorNode } from '@prosekit/pm/model'
14
+
15
+ export interface BlockHandlePopoverProps extends Omit<OverlayPositionerProps, 'placement' | 'hoist' | 'flip' | 'shift' | 'hide'> {
16
+ /**
17
+ * The ProseKit editor instance.
18
+ *
19
+ * @default null
20
+ * @hidden
21
+ */
22
+ editor: Editor | null
23
+
24
+ /**
25
+ * The placement of the popover, relative to the hovered block.
26
+ *
27
+ * @default "left"
28
+ */
29
+ placement: Placement
30
+
31
+ /**
32
+ * Whether to use the browser [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API)
33
+ * to place the floating element on top of other page content.
34
+ *
35
+ * @default false
36
+ */
37
+ hoist: boolean
38
+
39
+ /**
40
+ * @default false
41
+ * @hidden
42
+ */
43
+ flip: boolean
44
+
45
+ /**
46
+ * @default false
47
+ * @hidden
48
+ */
49
+ shift: boolean
50
+
51
+ /**
52
+ * @default true
53
+ * @hidden
54
+ */
55
+ hide: boolean
56
+ }
57
+
58
+ /** @internal */
59
+ export const blockHandlePopoverProps: PropDeclarations<BlockHandlePopoverProps> = {
60
+ ...overlayPositionerProps,
61
+ editor: { default: null },
62
+ placement: { default: 'left' },
63
+
64
+ // Enabling `hoist` will cause the popover to have a small delay when
65
+ // scrolling the page.
66
+ hoist: { default: false },
67
+
68
+ flip: { default: false },
69
+ shift: { default: false },
70
+ hide: { default: true },
71
+ }
72
+
73
+ export interface BlockHandlePopoverEvents extends OverlayPositionerEvents {
74
+ /**
75
+ * Fired when the hovered block changes.
76
+ */
77
+ stateChange: CustomEvent<{ node: ProseMirrorNode; pos: number } | null>
78
+ }
79
+
80
+ /** @internal */
81
+ export const blockHandlePopoverEvents: EventDeclarations<BlockHandlePopoverEvents> = {
82
+ ...overlayPositionerEvents,
83
+ stateChange: {},
84
+ }
@@ -0,0 +1,34 @@
1
+ import {
2
+ createContext,
3
+ type Context,
4
+ } from '@aria-ui/core'
5
+ import type { ProseMirrorNode } from '@prosekit/pm/model'
6
+
7
+ /**
8
+ * @internal
9
+ */
10
+ export interface HoverState {
11
+ node: ProseMirrorNode
12
+ pos: number
13
+ }
14
+
15
+ /**
16
+ * @internal
17
+ */
18
+ export type BlockPopoverContext = HoverState | null
19
+
20
+ /**
21
+ * @internal
22
+ */
23
+ export const blockPopoverContext: Context<BlockPopoverContext> = createContext(
24
+ 'prosekit-block-popover-context',
25
+ null,
26
+ )
27
+
28
+ /**
29
+ * @internal
30
+ */
31
+ export const draggingContext: Context<boolean> = createContext(
32
+ 'prosekit-block-handle-dragging-context',
33
+ false,
34
+ )
@@ -0,0 +1,13 @@
1
+ // This file is generated by packages/dev/src/gen-components.ts
2
+
3
+ export { BlockHandleAddElement } from './block-handle-add/element.gen'
4
+ export { blockHandleAddEvents, blockHandleAddProps, type BlockHandleAddEvents, type BlockHandleAddProps } from './block-handle-add/types'
5
+ export { useBlockHandleAdd } from './block-handle-add/setup'
6
+
7
+ export { BlockHandleDraggableElement } from './block-handle-draggable/element.gen'
8
+ export { blockHandleDraggableEvents, blockHandleDraggableProps, type BlockHandleDraggableEvents, type BlockHandleDraggableProps } from './block-handle-draggable/types'
9
+ export { useBlockHandleDraggable } from './block-handle-draggable/setup'
10
+
11
+ export { BlockHandlePopoverElement } from './block-handle-popover/element.gen'
12
+ export { blockHandlePopoverEvents, blockHandlePopoverProps, type BlockHandlePopoverEvents, type BlockHandlePopoverProps } from './block-handle-popover/types'
13
+ export { useBlockHandlePopover } from './block-handle-popover/setup'
@@ -0,0 +1,18 @@
1
+ import { defineCustomElement, registerCustomElement, type BaseElementConstructor } from "@aria-ui/core"
2
+
3
+ import { useDropIndicator } from "./setup"
4
+ import { dropIndicatorEvents, dropIndicatorProps, type DropIndicatorEvents, type DropIndicatorProps } from "./types"
5
+
6
+ const DropIndicatorElementBase: BaseElementConstructor<DropIndicatorProps> = defineCustomElement<
7
+ DropIndicatorProps,
8
+ DropIndicatorEvents
9
+ >({
10
+ props: dropIndicatorProps,
11
+ events: dropIndicatorEvents,
12
+ setup: useDropIndicator,
13
+ })
14
+ class DropIndicatorElement extends DropIndicatorElementBase {}
15
+
16
+ registerCustomElement('prosekit-drop-indicator', DropIndicatorElement)
17
+
18
+ export { DropIndicatorElement }