@tessera-editor/react 0.1.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/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export { Tessera } from './Tessera'
2
+ export type { TesseraProps } from './Tessera'
3
+ export { EmptyLineToolbar } from './EmptyLineToolbar'
4
+ export { SelectionToolbar } from './SelectionToolbar'
5
+ export { SlashMenuView, createSlashRenderer } from './SlashMenu'
6
+ export { FindReplacePanel, AskPanel, SuggestionBar } from './Panels'
7
+ export { HistoryPanel } from './HistoryPanel'
8
+ export { CommentPanel, CommentComposer } from './CommentPanel'
9
+ export { BlockContextMenuUI } from './BlockMenu'
10
+ export { ImageBlockView } from './ImageNodeView'
11
+ export { AiTableCellViewExtension, AiTableHeaderViewExtension } from './TableNodeViews'
12
+ export { EmbedBlockView, TocBlockView } from './EmbedTocViews'
13
+ export { TesseraContext, useTesseraContext } from './context'
14
+ export type { TesseraContextValue } from './context'
package/src/portal.ts ADDED
@@ -0,0 +1,36 @@
1
+ import { useEffect, useState } from 'react'
2
+ import type { Editor } from '@tiptap/react'
3
+
4
+ /**
5
+ * Floating UI (toolbars, menus, panels) must portal into the component root,
6
+ * never document.body: the --te-* theme tokens — including host overrides and
7
+ * the dark-theme hook — live on .tessera-root, so a body portal renders its
8
+ * chrome with unresolved variables (transparent background, no shadow) and
9
+ * outside the border-box reset.
10
+ *
11
+ * Mount-time resolution can run before EditorContent has inserted the
12
+ * ProseMirror view into the document (closest() on a detached node = null),
13
+ * so resolution is retried on the first editor events — which always precede
14
+ * any floating UI being shown.
15
+ */
16
+ export function useTesseraPortalRoot(editor: Editor): HTMLElement | null {
17
+ const [target, setTarget] = useState<HTMLElement | null>(null)
18
+ useEffect(() => {
19
+ const resolve = () => {
20
+ setTarget(prev => {
21
+ if (prev && prev !== document.body) {
22
+ return prev
23
+ }
24
+ return (editor.view.dom.closest('.tessera-root') as HTMLElement | null) ?? document.body
25
+ })
26
+ }
27
+ resolve()
28
+ editor.on('focus', resolve)
29
+ editor.on('selectionUpdate', resolve)
30
+ return () => {
31
+ editor.off('focus', resolve)
32
+ editor.off('selectionUpdate', resolve)
33
+ }
34
+ }, [editor])
35
+ return target
36
+ }