@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,18 @@
1
+ import { defineCustomElement, registerCustomElement, type BaseElementConstructor } from "@aria-ui/core"
2
+
3
+ import { useTooltipTrigger } from "./setup"
4
+ import { tooltipTriggerEvents, tooltipTriggerProps, type TooltipTriggerEvents, type TooltipTriggerProps } from "./types"
5
+
6
+ const TooltipTriggerElementBase: BaseElementConstructor<TooltipTriggerProps> = defineCustomElement<
7
+ TooltipTriggerProps,
8
+ TooltipTriggerEvents
9
+ >({
10
+ props: tooltipTriggerProps,
11
+ events: tooltipTriggerEvents,
12
+ setup: useTooltipTrigger,
13
+ })
14
+ class TooltipTriggerElement extends TooltipTriggerElementBase {}
15
+
16
+ registerCustomElement('prosekit-tooltip-trigger', TooltipTriggerElement)
17
+
18
+ export { TooltipTriggerElement }
@@ -0,0 +1 @@
1
+ export { useTooltipTrigger } from '@aria-ui/tooltip/elements'
@@ -0,0 +1,12 @@
1
+ import type {
2
+ TooltipTriggerEvents as Events,
3
+ TooltipTriggerProps as Props,
4
+ } from '@aria-ui/tooltip/elements'
5
+
6
+ export {
7
+ tooltipTriggerEvents,
8
+ tooltipTriggerProps,
9
+ } from '@aria-ui/tooltip/elements'
10
+
11
+ export interface TooltipTriggerProps extends Props {}
12
+ export interface TooltipTriggerEvents extends Events {}
@@ -0,0 +1,19 @@
1
+ import {
2
+ useEffect,
3
+ type ConnectableElement,
4
+ type ReadonlySignal,
5
+ } from '@aria-ui/core'
6
+ import type {
7
+ Editor,
8
+ Extension,
9
+ } from '@prosekit/core'
10
+
11
+ export function useEditorExtension(
12
+ host: ConnectableElement,
13
+ editor: ReadonlySignal<Editor | null>,
14
+ extension: Extension,
15
+ ): void {
16
+ useEffect(host, () => {
17
+ return editor.get()?.use(extension)
18
+ })
19
+ }
@@ -0,0 +1,23 @@
1
+ import type {
2
+ ConnectableElement,
3
+ ReadonlySignal,
4
+ } from '@aria-ui/core'
5
+ import {
6
+ defineFocusChangeHandler,
7
+ type Editor,
8
+ type FocusChangeHandler,
9
+ } from '@prosekit/core'
10
+
11
+ import { useEditorExtension } from './use-editor-extension'
12
+
13
+ /**
14
+ * @internal
15
+ */
16
+ export function useEditorFocusChangeEvent(
17
+ host: ConnectableElement,
18
+ editor: ReadonlySignal<Editor | null>,
19
+ handler: FocusChangeHandler,
20
+ ): void {
21
+ const extension = defineFocusChangeHandler(handler)
22
+ useEditorExtension(host, editor, extension)
23
+ }
@@ -0,0 +1,36 @@
1
+ import {
2
+ createSignal,
3
+ type ConnectableElement,
4
+ type ReadonlySignal,
5
+ } from '@aria-ui/core'
6
+ import {
7
+ defineDOMEventHandler,
8
+ union,
9
+ type Editor,
10
+ } from '@prosekit/core'
11
+
12
+ import { useEditorExtension } from './use-editor-extension'
13
+
14
+ export function useEditorTyping(
15
+ host: ConnectableElement,
16
+ editor: ReadonlySignal<Editor | null>,
17
+ ): ReadonlySignal<boolean> {
18
+ const typing = createSignal(false)
19
+
20
+ const handleKeypress = () => {
21
+ typing.set(true)
22
+ }
23
+
24
+ const handlePointerMove = () => {
25
+ typing.set(false)
26
+ }
27
+
28
+ const extension = union(
29
+ defineDOMEventHandler('keypress', handleKeypress),
30
+ defineDOMEventHandler('pointermove', handlePointerMove),
31
+ )
32
+
33
+ useEditorExtension(host, editor, extension)
34
+
35
+ return typing
36
+ }
@@ -0,0 +1,23 @@
1
+ import type {
2
+ ConnectableElement,
3
+ ReadonlySignal,
4
+ } from '@aria-ui/core'
5
+ import {
6
+ defineUpdateHandler,
7
+ type Editor,
8
+ type UpdateHandler,
9
+ } from '@prosekit/core'
10
+
11
+ import { useEditorExtension } from './use-editor-extension'
12
+
13
+ /**
14
+ * @internal
15
+ */
16
+ export function useEditorUpdateEvent(
17
+ host: ConnectableElement,
18
+ editor: ReadonlySignal<Editor | null>,
19
+ handler: UpdateHandler,
20
+ ): void {
21
+ const extension = defineUpdateHandler(handler)
22
+ useEditorExtension(host, editor, extension)
23
+ }
@@ -0,0 +1,20 @@
1
+ import {
2
+ createSignal,
3
+ useEffect,
4
+ type ConnectableElement,
5
+ type ReadonlySignal,
6
+ } from '@aria-ui/core'
7
+
8
+ export function useFirstRendering(
9
+ host: ConnectableElement,
10
+ ): ReadonlySignal<boolean> {
11
+ const firstRendering = createSignal(true)
12
+
13
+ useEffect(host, () => {
14
+ requestAnimationFrame(() => {
15
+ firstRendering.set(false)
16
+ })
17
+ })
18
+
19
+ return firstRendering
20
+ }
@@ -0,0 +1,20 @@
1
+ import type {
2
+ ConnectableElement,
3
+ ReadonlySignal,
4
+ } from '@aria-ui/core'
5
+ import {
6
+ defineKeymap,
7
+ type Editor,
8
+ type Keymap,
9
+ } from '@prosekit/core'
10
+
11
+ import { useEditorExtension } from './use-editor-extension'
12
+
13
+ export function useKeymap(
14
+ host: ConnectableElement,
15
+ editor: ReadonlySignal<Editor | null>,
16
+ keymap: Keymap,
17
+ ): void {
18
+ const extension = defineKeymap(keymap)
19
+ useEditorExtension(host, editor, extension)
20
+ }
@@ -0,0 +1,33 @@
1
+ import {
2
+ createSignal,
3
+ useEffect,
4
+ type ConnectableElement,
5
+ type ReadonlySignal,
6
+ } from '@aria-ui/core'
7
+ import { getNearestOverflowAncestor } from '@zag-js/dom-query'
8
+
9
+ export function useScrolling(host: ConnectableElement): ReadonlySignal<boolean> {
10
+ const scrolling = createSignal(false)
11
+
12
+ useEffect(host, () => {
13
+ const scrollableParent = getNearestOverflowAncestor(host)
14
+
15
+ const handleScroll = () => {
16
+ scrolling.set(true)
17
+ }
18
+
19
+ const handleMouseMove = () => {
20
+ scrolling.set(false)
21
+ }
22
+
23
+ scrollableParent.addEventListener('scroll', handleScroll, { passive: true })
24
+ window.addEventListener('mousemove', handleMouseMove, { passive: true })
25
+
26
+ return () => {
27
+ scrollableParent.removeEventListener('scroll', handleScroll)
28
+ window.removeEventListener('mousemove', handleMouseMove)
29
+ }
30
+ })
31
+
32
+ return scrolling
33
+ }
@@ -0,0 +1,63 @@
1
+ import type { ReadonlySignal } from '@aria-ui/core'
2
+ import {
3
+ createSignal,
4
+ useEffect,
5
+ type ConnectableElement,
6
+ } from '@aria-ui/core'
7
+ import type { Editor } from '@prosekit/core'
8
+
9
+ import { getSafeEditorView } from '../utils/get-safe-editor-view'
10
+
11
+ /**
12
+ * Detect if the user is selecting text inside the editor, in which case some
13
+ * components should be disabled or hidden.
14
+ */
15
+ export function useSelecting(
16
+ host: ConnectableElement,
17
+ editor: ReadonlySignal<Editor | null>,
18
+ enabled: ReadonlySignal<boolean>,
19
+ ): ReadonlySignal<boolean> {
20
+ const selecting = createSignal(false)
21
+ const isPointerDown = createSignal(false)
22
+
23
+ useEffect(host, () => {
24
+ if (!enabled.get()) {
25
+ return
26
+ }
27
+
28
+ const view = getSafeEditorView(editor.peek())
29
+ if (!view) return
30
+
31
+ const { dom, root } = view
32
+ if (!root) return
33
+
34
+ // When the user starts selecting text, we set the selecting signal to true.
35
+ const handlePointerDown = () => {
36
+ selecting.set(true)
37
+ isPointerDown.set(true)
38
+ }
39
+ const handlePointerUp = () => {
40
+ isPointerDown.set(false)
41
+ }
42
+ // When the user moves the pointer and the pointer is not down, we set the
43
+ // selecting signal to false again.
44
+ const handleMouseMove = () => {
45
+ if (!isPointerDown.get()) {
46
+ selecting.set(false)
47
+ }
48
+ }
49
+
50
+ // Only listen to pointer down events on the editor
51
+ dom.addEventListener('pointerdown', handlePointerDown)
52
+ root.addEventListener('pointerup', handlePointerUp)
53
+ root.addEventListener('pointermove', handleMouseMove)
54
+
55
+ return () => {
56
+ dom.removeEventListener('pointerdown', handlePointerDown)
57
+ root.removeEventListener('pointerup', handlePointerUp)
58
+ root.removeEventListener('pointermove', handleMouseMove)
59
+ }
60
+ })
61
+
62
+ return selecting
63
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,14 @@
1
+ import type { ConditionalPick } from 'type-fest'
2
+
3
+ // Only include CSS properties whose value type is `string`
4
+ type StringStyleDeclaration = Partial<ConditionalPick<CSSStyleDeclaration, string>>
5
+
6
+ /**
7
+ * A type-safe version of `Object.assign` for `element.style`.
8
+ */
9
+ export function assignStyles(
10
+ element: HTMLElement | SVGElement | MathMLElement,
11
+ styles: StringStyleDeclaration,
12
+ ): void {
13
+ Object.assign(element.style, styles)
14
+ }
@@ -0,0 +1,110 @@
1
+ import { getId } from '@ocavue/utils'
2
+
3
+ /**
4
+ * Creates a deep clone of an Element, including all computed styles so that
5
+ * it looks almost exactly the same as the original element.
6
+ */
7
+ export function deepCloneElement<T extends Element>(element: T, important = false): [T, string] {
8
+ const clonedElement = element.cloneNode(true) as T
9
+ const style = deepCopyStyles(element, clonedElement, important)
10
+ return [clonedElement, style]
11
+ }
12
+
13
+ /**
14
+ * Creates a clone of an Element, including all computed styles so that
15
+ * it looks similar enough to the original element.
16
+ */
17
+ export function cloneElement<T extends Element>(element: T, important = false): [T, string] {
18
+ const clonedElement = element.cloneNode() as T
19
+ const style = copyStyles(element, clonedElement, important)
20
+ return [clonedElement, style]
21
+ }
22
+
23
+ function deepCopyStyles(source: Element, target: Element, important: boolean): string {
24
+ const sources = [source]
25
+ const targets = [target]
26
+ const styles: string[] = []
27
+
28
+ while (sources.length > 0 && sources.length === targets.length) {
29
+ const source = sources.pop()
30
+ const target = targets.pop()
31
+
32
+ if (!source || !target) {
33
+ break
34
+ }
35
+
36
+ const style = copyStyles(source, target, important)
37
+ if (style) {
38
+ styles.push(style)
39
+ }
40
+
41
+ sources.push(...source.children)
42
+ targets.push(...target.children)
43
+ }
44
+
45
+ return styles.join('\n')
46
+ }
47
+
48
+ function copyStyles(source: Element, target: Element, important: boolean): string {
49
+ if (!source || !target) {
50
+ return ''
51
+ }
52
+
53
+ const view = source.ownerDocument?.defaultView
54
+ if (!view) {
55
+ return ''
56
+ }
57
+
58
+ // Known issue: pseudo styles are not copied.
59
+ const sourceStyle = view.getComputedStyle(source)
60
+ const targetStyle = (target as HTMLElement | SVGElement | MathMLElement).style
61
+
62
+ if (!sourceStyle || !targetStyle) {
63
+ return ''
64
+ }
65
+
66
+ for (const key of sourceStyle) {
67
+ targetStyle.setProperty(
68
+ key,
69
+ sourceStyle.getPropertyValue(key),
70
+ // Enforce important to avoid the style being overridden when the element
71
+ // is connected to the page.
72
+ // See https://github.com/prosekit/prosekit/issues/1185 for more details.
73
+ important ? 'important' : (sourceStyle.getPropertyPriority(key) || ''),
74
+ )
75
+ }
76
+
77
+ const styles: string[] = []
78
+ for (const pseudoSelector of [':before', ':after']) {
79
+ const sourcePseudoStyle = view.getComputedStyle(source, pseudoSelector)
80
+ const targetPseudoStyle = view.getComputedStyle(target, pseudoSelector)
81
+
82
+ if (!sourcePseudoStyle) {
83
+ continue
84
+ }
85
+
86
+ const content = sourcePseudoStyle.getPropertyValue('content')
87
+ const hasPseudoElement = content && content !== 'none' && content !== 'normal'
88
+
89
+ if (!hasPseudoElement) {
90
+ continue
91
+ }
92
+
93
+ const cssProps: string[] = []
94
+ for (const property of sourcePseudoStyle) {
95
+ const sourceValue = sourcePseudoStyle.getPropertyValue(property)
96
+ const sourcePriority = sourcePseudoStyle.getPropertyPriority(property)
97
+ const targetValue = targetPseudoStyle.getPropertyValue(property)
98
+ const targetPriority = targetPseudoStyle.getPropertyPriority(property)
99
+ if (sourceValue !== targetValue || sourcePriority !== targetPriority) {
100
+ cssProps.push(`${property}: ${sourceValue}${sourcePriority ? ' !important' : ''};`)
101
+ }
102
+ }
103
+
104
+ const uniqueClassName = `clone-pseudo-element-${getId()}`
105
+ target.classList.add(uniqueClassName)
106
+ styles.push(`.${uniqueClassName}${pseudoSelector} { ${cssProps.join(' ')} }`)
107
+ }
108
+
109
+ return styles.join('\n')
110
+ }
@@ -0,0 +1,9 @@
1
+ import { once } from '@ocavue/utils'
2
+
3
+ export const isColorMixSupported: () => boolean = once(() => {
4
+ try {
5
+ return CSS.supports('background-color', 'color-mix(in srgb, red, blue)')
6
+ } catch {
7
+ return false
8
+ }
9
+ })
@@ -0,0 +1,15 @@
1
+ import { isColorMixSupported } from './css-feature-detection'
2
+
3
+ /**
4
+ * Convert a color to a color with opacity
5
+ * @param color - The color to convert
6
+ * @param opacity - The opacity to apply
7
+ * @returns The converted color if color-mix is supported, otherwise undefined
8
+ */
9
+ export function fadeColor(color: CSSStyleValue, opacity: number): string | undefined {
10
+ if (isColorMixSupported()) {
11
+ const transparentWeight = (1 - opacity) * 100
12
+ const colorWeight = opacity * 100
13
+ return `color-mix(in srgb, ${color} ${colorWeight}%, transparent ${transparentWeight}%)`
14
+ }
15
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Returns the element that has a box.
3
+ */
4
+ export function getBoxElement(element: Element): Element | null | undefined {
5
+ const window = element.ownerDocument.defaultView
6
+ if (!window) {
7
+ return
8
+ }
9
+
10
+ const style = window.getComputedStyle(element)
11
+ const display = style.display
12
+
13
+ if (display === 'contents' && element.childElementCount === 1) {
14
+ return element.firstElementChild
15
+ } else if (display === 'none') {
16
+ return
17
+ }
18
+
19
+ return element
20
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Similar to `element.getBoundingClientRect`, but handles `display: contents` elements.
3
+ */
4
+ export function getClientRect(element: Element): {
5
+ top: number
6
+ right: number
7
+ bottom: number
8
+ left: number
9
+ } {
10
+ const rect = element.getBoundingClientRect()
11
+ if (rect.width === 0 && rect.height === 0 && rect.x === 0 && rect.y === 0) {
12
+ // Suspiciously rect, probably an element with `display: contents`, in
13
+ // which case `element.getClientRects()` will return an empty array.
14
+ if (element.getClientRects().length === 0) {
15
+ const children = Array.from(element.children)
16
+ const rects = children.map(child => getClientRect(child))
17
+ if (rects.length === 0) {
18
+ return rect
19
+ }
20
+ if (rects.length === 1) {
21
+ return rects[0]
22
+ }
23
+ let { top, bottom, left, right } = rects[0]
24
+ for (let i = 1; i < rects.length; i++) {
25
+ const r = rects[i]
26
+ if (r.top < top) top = r.top
27
+ if (r.bottom > bottom) bottom = r.bottom
28
+ if (r.left < left) left = r.left
29
+ if (r.right > right) right = r.right
30
+ }
31
+ return { top, bottom, left, right }
32
+ }
33
+ }
34
+ return rect
35
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ createSignal,
3
+ type PropDeclarations,
4
+ type SignalState,
5
+ } from '@aria-ui/core'
6
+ import {
7
+ describe,
8
+ expect,
9
+ it,
10
+ } from 'vitest'
11
+
12
+ import { getStateWithDefaults } from './get-default-state'
13
+
14
+ type TestProps = {
15
+ bool: boolean
16
+ num: number
17
+ }
18
+
19
+ describe('getStateWithDefaults', () => {
20
+ it('keeps falsy signal values from state', () => {
21
+ const props: PropDeclarations<TestProps> = {
22
+ bool: { default: true },
23
+ num: { default: 1 },
24
+ }
25
+
26
+ const state: Partial<SignalState<TestProps>> = {
27
+ bool: createSignal(false),
28
+ num: createSignal(0),
29
+ }
30
+
31
+ const merged = getStateWithDefaults(state, props)
32
+ expect(merged.bool.get()).toBe(false)
33
+ expect(merged.num.get()).toBe(0)
34
+ })
35
+
36
+ it('uses default value when state property is undefined', () => {
37
+ const props: PropDeclarations<TestProps> = {
38
+ bool: { default: true },
39
+ num: { default: 1 },
40
+ }
41
+
42
+ const state: Partial<SignalState<TestProps>> = {
43
+ bool: undefined,
44
+ }
45
+
46
+ const merged = getStateWithDefaults(state, props)
47
+ expect(merged.bool.get()).toBe(true)
48
+ expect(merged.num.get()).toBe(1)
49
+ })
50
+ })
@@ -0,0 +1,22 @@
1
+ import {
2
+ createSignal,
3
+ type PropDeclarations,
4
+ type SignalState,
5
+ } from '@aria-ui/core'
6
+
7
+ export function getStateWithDefaults<
8
+ Props extends Record<string, any> = Record<string, any>,
9
+ >(
10
+ state: Partial<SignalState<Props>>,
11
+ props: PropDeclarations<Props>,
12
+ ): SignalState<Props> {
13
+ const merged = { ...state } as SignalState<Props>
14
+
15
+ for (const key of Object.keys(props) as (keyof Props)[]) {
16
+ if (!merged[key]) {
17
+ merged[key] = createSignal(props[key].default)
18
+ }
19
+ }
20
+
21
+ return merged
22
+ }
@@ -0,0 +1,21 @@
1
+ export function getEffectiveBackgroundColor(element: HTMLElement): string | undefined {
2
+ let current: HTMLElement | null = element
3
+
4
+ while (current) {
5
+ const style = current.ownerDocument.defaultView?.getComputedStyle(current)
6
+ const backgroundColor = style?.backgroundColor
7
+
8
+ if (
9
+ backgroundColor
10
+ && backgroundColor !== 'transparent'
11
+ // Chrome returns `rgba(0, 0, 0, 0)` for transparent colors.
12
+ && backgroundColor !== 'rgba(0, 0, 0, 0)'
13
+ ) {
14
+ return backgroundColor
15
+ }
16
+
17
+ current = current.parentElement
18
+ }
19
+
20
+ return undefined
21
+ }
@@ -0,0 +1,10 @@
1
+ import type { Editor } from '@prosekit/core'
2
+ import type { EditorView } from '@prosekit/pm/view'
3
+
4
+ /**
5
+ * @internal
6
+ */
7
+ export function getSafeEditorView(editor?: Editor | null): EditorView | undefined {
8
+ if (!editor || !editor.mounted) return
9
+ return editor.view
10
+ }
@@ -0,0 +1,11 @@
1
+ import { getDocument } from '@ocavue/utils'
2
+
3
+ export function injectStyle(container: HTMLElement, styleText: string): void {
4
+ if (!styleText) {
5
+ return
6
+ }
7
+ const document = getDocument(container)
8
+ const style = document.createElement('style')
9
+ style.textContent = styleText
10
+ container.appendChild(style)
11
+ }
@@ -0,0 +1,3 @@
1
+ export function isFinitePositiveNumber(value: unknown): value is number {
2
+ return typeof value === 'number' && Number.isFinite(value) && value > 0
3
+ }
@@ -0,0 +1,3 @@
1
+ // Maximum possible z-index
2
+ // https://stackoverflow.com/questions/491052/minimum-and-maximum-value-of-z-index
3
+ export const maxZIndex = '2147483647'
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ export function throttle<Args extends any[]>(
5
+ callback: (...args: Args) => void,
6
+ wait: number,
7
+ ): (...args: Args) => void {
8
+ let lastTime = 0
9
+
10
+ return (...args: Args) => {
11
+ const now = Date.now()
12
+ if (now - lastTime >= wait) {
13
+ callback(...args)
14
+ lastTime = now
15
+ }
16
+ }
17
+ }