@octanejs/tiptap 0.0.10 → 0.0.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octanejs/tiptap",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -55,21 +55,21 @@
55
55
  "@tiptap/extension-floating-menu": "3.28.0"
56
56
  },
57
57
  "peerDependencies": {
58
- "octane": "0.1.16"
58
+ "octane": "0.1.18"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@testing-library/jest-dom": "^6.9.1",
62
62
  "@tiptap/react": "3.28.0",
63
63
  "@tiptap/starter-kit": "3.28.0",
64
- "@tsrx/react": "^0.2.50",
64
+ "@tsrx/react": "^0.2.54",
65
65
  "esbuild": "^0.28.1",
66
66
  "playwright": "^1.61.1",
67
67
  "react": "^19.2.7",
68
68
  "react-dom": "^19.2.7",
69
69
  "vite": "^8.1.5",
70
70
  "vitest": "^4.1.10",
71
- "@octanejs/testing-library": "0.1.13",
72
- "octane": "0.1.16"
71
+ "@octanejs/testing-library": "0.1.15",
72
+ "octane": "0.1.18"
73
73
  },
74
74
  "scripts": {
75
75
  "test": "vitest run"
package/src/Context.tsrx CHANGED
@@ -1,13 +1,32 @@
1
1
  // Ported from @tiptap/react 3.28.0 Context.tsx. Octane contexts intentionally
2
2
  // have no `.Consumer`; the compatibility component below implements the same
3
3
  // render-prop contract with useContext instead.
4
- import { isChildrenBlock, useMemo } from 'octane';
4
+ import { isChildrenBlock, useMemo, type OctaneNode } from 'octane';
5
5
 
6
- import { EditorContext, useCurrentEditor } from './Context';
7
- import { EditorContent } from './EditorContent.tsrx';
8
- import { useEditor } from './useEditor';
6
+ import { EditorContext, useCurrentEditor, type EditorContextValue } from './Context';
7
+ import { EditorContent, type EditorContentProps } from './EditorContent.tsrx';
8
+ import { useEditor, type UseEditorOptions } from './useEditor';
9
9
 
10
- export function EditorConsumer(props) {
10
+ export interface EditorConsumerProps {
11
+ children: (value: EditorContextValue) => OctaneNode;
12
+ }
13
+
14
+ export type EditorContainerProps =
15
+ Omit<EditorContentProps, 'editor' | 'innerRef' | 'ref'> & {
16
+ editor?: never;
17
+ innerRef?: never;
18
+ ref?: never;
19
+ };
20
+
21
+ export type EditorProviderProps =
22
+ UseEditorOptions & {
23
+ children?: OctaneNode;
24
+ slotBefore?: OctaneNode;
25
+ slotAfter?: OctaneNode;
26
+ editorContainerProps?: EditorContainerProps;
27
+ };
28
+
29
+ export function EditorConsumer(props: EditorConsumerProps): OctaneNode {
11
30
  const value = useCurrentEditor();
12
31
  const children = props.children;
13
32
 
@@ -21,7 +40,7 @@ export function EditorConsumer(props) {
21
40
  /**
22
41
  * Create an editor and expose it to descendants through the legacy context.
23
42
  */
24
- export function EditorProvider(props) @{
43
+ export function EditorProvider(props: EditorProviderProps) @{
25
44
  const { children, slotAfter, slotBefore, editorContainerProps = {}, ...editorOptions } = props;
26
45
  const editor = useEditor(editorOptions);
27
46
  const contextValue = useMemo(() => ({ editor }), [editor]);
@@ -33,8 +52,8 @@ export function EditorProvider(props) @{
33
52
  <EditorContext.Provider value={contextValue}>
34
53
  {slotBefore}
35
54
  <EditorConsumer>
36
- {({ editor: currentEditor }) =>
37
- <EditorContent editor={currentEditor} {...editorContainerProps} />}
55
+ {({ editor: currentEditor }: EditorContextValue) =>
56
+ <EditorContent {...editorContainerProps} editor={currentEditor} />}
38
57
  </EditorConsumer>
39
58
  {children}
40
59
  {slotAfter}
@@ -1,9 +1,12 @@
1
+ import type { OctaneNode } from 'octane';
2
+ import type { Octane } from 'octane/jsx-runtime';
3
+
1
4
  import type { EditorContentProps } from './EditorContent.tsrx';
2
5
  import type { EditorContextValue } from './Context';
3
6
  import type { UseEditorOptions } from './useEditor';
4
7
 
5
8
  export interface EditorConsumerProps {
6
- children: (value: EditorContextValue) => unknown;
9
+ children: (value: EditorContextValue) => OctaneNode;
7
10
  }
8
11
 
9
12
  export type EditorContainerProps = Omit<EditorContentProps, 'editor' | 'innerRef' | 'ref'> & {
@@ -13,11 +16,11 @@ export type EditorContainerProps = Omit<EditorContentProps, 'editor' | 'innerRef
13
16
  };
14
17
 
15
18
  export type EditorProviderProps = UseEditorOptions & {
16
- children?: unknown;
17
- slotBefore?: unknown;
18
- slotAfter?: unknown;
19
+ children?: OctaneNode;
20
+ slotBefore?: OctaneNode;
21
+ slotAfter?: OctaneNode;
19
22
  editorContainerProps?: EditorContainerProps;
20
23
  };
21
24
 
22
- export declare function EditorConsumer(props: EditorConsumerProps): unknown;
23
- export declare function EditorProvider(props: EditorProviderProps): unknown;
25
+ export declare function EditorConsumer(props: EditorConsumerProps): OctaneNode;
26
+ export declare function EditorProvider(props: EditorProviderProps): Octane.JSX.Element | null;
@@ -1,7 +1,9 @@
1
1
  // DOM lifecycle and portal-registry bridge ported from @tiptap/react 3.28.0.
2
+ import type { Editor } from '@tiptap/core';
2
3
  import {
3
4
  createPortal,
4
5
  memo,
6
+ type OctaneNode,
5
7
  useLayoutEffect,
6
8
  useMemo,
7
9
  useRef,
@@ -9,12 +11,30 @@ import {
9
11
  useSyncExternalStore,
10
12
  } from 'octane';
11
13
 
12
- function PortalEntry({ entry }) {
14
+ import type {
15
+ ContentComponent,
16
+ EditorWithContentComponent,
17
+ RendererPortalEntry,
18
+ RendererPortalSnapshot,
19
+ } from './Editor';
20
+
21
+ export type EditorContentRef<T> =
22
+ { current: T | null } | ((value: T | null) => void | (() => void)) | null;
23
+
24
+ export interface EditorContentProps {
25
+ editor: Editor | null;
26
+ ref?: EditorContentRef<HTMLDivElement>;
27
+ innerRef?: EditorContentRef<HTMLDivElement>;
28
+ children?: OctaneNode;
29
+ [key: string]: unknown;
30
+ }
31
+
32
+ function PortalEntry({ entry }: { entry: RendererPortalEntry }) {
13
33
  return entry.portal;
14
34
  }
15
35
 
16
36
  /** Render every custom view in this EditorContent's logical Octane tree. */
17
- function Portals({ contentComponent }) @{
37
+ function Portals({ contentComponent }: { contentComponent: ContentComponent }) @{
18
38
  const renderers = useSyncExternalStore(
19
39
  contentComponent.subscribe,
20
40
  contentComponent.getSnapshot,
@@ -30,9 +50,9 @@ function Portals({ contentComponent }) @{
30
50
  }
31
51
 
32
52
  /** Create the immutable external-store registry consumed by Portals. */
33
- export function createContentComponent() {
34
- const subscribers = new Set();
35
- let renderers = {};
53
+ export function createContentComponent(): ContentComponent {
54
+ const subscribers = new Set<() => void>();
55
+ let renderers: RendererPortalSnapshot = {};
36
56
  let isNotificationQueued = false;
37
57
 
38
58
  const notifySubscribers = () => {
@@ -48,17 +68,17 @@ export function createContentComponent() {
48
68
  };
49
69
 
50
70
  return {
51
- subscribe(callback) {
71
+ subscribe(callback: () => void): () => void {
52
72
  subscribers.add(callback);
53
73
  return () => subscribers.delete(callback);
54
74
  },
55
- getSnapshot() {
75
+ getSnapshot(): RendererPortalSnapshot {
56
76
  return renderers;
57
77
  },
58
- getServerSnapshot() {
78
+ getServerSnapshot(): RendererPortalSnapshot {
59
79
  return renderers;
60
80
  },
61
- setRenderer(id, renderer) {
81
+ setRenderer(id: string, renderer: Parameters<ContentComponent['setRenderer']>[1]): void {
62
82
  renderers = {
63
83
  ...renderers,
64
84
  [id]: {
@@ -68,8 +88,8 @@ export function createContentComponent() {
68
88
  };
69
89
  notifySubscribers();
70
90
  },
71
- removeRenderer(id) {
72
- const nextRenderers = { ...renderers };
91
+ removeRenderer(id: string): void {
92
+ const nextRenderers: Record<string, RendererPortalEntry> = { ...renderers };
73
93
  delete nextRenderers[id];
74
94
  renderers = nextRenderers;
75
95
  notifySubscribers();
@@ -77,10 +97,11 @@ export function createContentComponent() {
77
97
  };
78
98
  }
79
99
 
80
- export function PureEditorContent(props) @{
81
- const { editor, innerRef, ref, ...rest } = props;
82
- const editorContentRef = useRef(null);
83
- const [contentComponent, setContentComponent] = useState(null);
100
+ export function PureEditorContent(props: EditorContentProps) @{
101
+ const { editor: providedEditor, innerRef, ref, ...rest } = props;
102
+ const editor = providedEditor as EditorWithContentComponent | null;
103
+ const editorContentRef = useRef<HTMLDivElement | null>(null);
104
+ const [contentComponent, setContentComponent] = useState<ContentComponent | null>(null);
84
105
 
85
106
  useLayoutEffect(() => {
86
107
  if (!editor || editor.isDestroyed || editor.isEditorContentInitialized) {
@@ -139,14 +160,14 @@ export function PureEditorContent(props) @{
139
160
  }, [editor]);
140
161
 
141
162
  <>
142
- <div {...rest} ref={[ref, innerRef, editorContentRef]} />
163
+ <div {...rest} ref={[ref ?? null, innerRef ?? null, editorContentRef]} />
143
164
  @if (contentComponent) {
144
165
  <Portals contentComponent={contentComponent} />
145
166
  }
146
167
  </>
147
168
  }
148
169
 
149
- function EditorContentWithKey(props) @{
170
+ function EditorContentWithKey(props: EditorContentProps) @{
150
171
  // Remount the host whenever the Editor identity changes, matching the keyed
151
172
  // wrapper in @tiptap/react and preventing one editor from retaining another
152
173
  // editor's managed DOM.
@@ -1,4 +1,6 @@
1
1
  import type { Editor } from '@tiptap/core';
2
+ import type { OctaneNode } from 'octane';
3
+ import type { Octane } from 'octane/jsx-runtime';
2
4
 
3
5
  import type { ContentComponent } from './Editor';
4
6
 
@@ -13,10 +15,10 @@ export interface EditorContentProps {
13
15
  editor: Editor | null;
14
16
  ref?: EditorContentRef<HTMLDivElement>;
15
17
  innerRef?: EditorContentRef<HTMLDivElement>;
16
- children?: unknown;
18
+ children?: OctaneNode;
17
19
  [key: string]: unknown;
18
20
  }
19
21
 
20
22
  export declare function createContentComponent(): ContentComponent;
21
- export declare function PureEditorContent(props: EditorContentProps): unknown;
22
- export declare const EditorContent: (props: EditorContentProps) => unknown;
23
+ export declare function PureEditorContent(props: EditorContentProps): Octane.JSX.Element;
24
+ export declare const EditorContent: (props: EditorContentProps) => Octane.JSX.Element;
package/src/Tiptap.tsrx CHANGED
@@ -1,14 +1,43 @@
1
1
  // Ported from @tiptap/react 3.28.0 Tiptap.tsx. Refs remain ordinary props in
2
2
  // Octane, and the compound Tiptap.Content component needs no forwardRef layer.
3
- import { createContext, useContext, useMemo } from 'octane';
3
+ import type { Editor } from '@tiptap/core';
4
+ import { createContext, useContext, useMemo, type Context, type OctaneNode } from 'octane';
4
5
 
5
6
  import { EditorContext } from './Context';
6
- import { EditorContent } from './EditorContent.tsrx';
7
+ import { EditorContent, type EditorContentProps } from './EditorContent.tsrx';
7
8
  import { splitSlot, subSlot } from './internal';
8
- import { useEditorState } from './useEditorState';
9
-
10
- export const TiptapContext = createContext({
11
- get editor() {
9
+ import {
10
+ useEditorState,
11
+ type EditorStateSnapshot,
12
+ type UseEditorStateOptions,
13
+ } from './useEditorState';
14
+
15
+ export type TiptapContextType = {
16
+ editor: Editor;
17
+ };
18
+
19
+ export type TiptapWrapperEditorInstanceProps =
20
+ | {
21
+ editor: Editor;
22
+ }
23
+ | {
24
+ /** @deprecated Use `editor` instead. */
25
+ instance: Editor;
26
+ };
27
+
28
+ export type TiptapWrapperProps =
29
+ TiptapWrapperEditorInstanceProps & {
30
+ children: OctaneNode;
31
+ };
32
+
33
+ export type TiptapContentProps =
34
+ Omit<EditorContentProps, 'editor' | 'ref'> & {
35
+ editor?: never;
36
+ ref?: never;
37
+ };
38
+
39
+ export const TiptapContext: Context<TiptapContextType> & { displayName?: string } = createContext({
40
+ get editor(): Editor {
12
41
  throw new Error('useTiptap must be used within a <Tiptap> provider');
13
42
  },
14
43
  });
@@ -16,19 +45,26 @@ export const TiptapContext = createContext({
16
45
  TiptapContext.displayName = 'TiptapContext';
17
46
 
18
47
  /** Read the editor instance provided by the nearest `Tiptap` component. */
19
- export function useTiptap() {
48
+ export function useTiptap(): TiptapContextType {
20
49
  return useContext(TiptapContext);
21
50
  }
22
51
 
23
52
  /** Select a reactive slice of editor state from the context editor. */
24
- export function useTiptapState(selector, ...args) {
53
+ export function useTiptapState<TSelectorResult>(
54
+ selector: (context: EditorStateSnapshot<Editor>) => TSelectorResult,
55
+ ...args: [equalityFn?: (left: TSelectorResult, right: TSelectorResult | null) => boolean]
56
+ ): TSelectorResult {
25
57
  const [userArgs, slot] = splitSlot(args);
26
- const equalityFn = userArgs[0];
58
+ const equalityFn = userArgs[0] as ((left: TSelectorResult, right: | TSelectorResult
59
+ | null) => boolean) | undefined;
27
60
  const { editor } = useTiptap();
28
61
 
29
62
  // Call through a non-hook-named alias because this source is itself compiled:
30
63
  // the public call site's slot is already being forwarded explicitly here.
31
- const selectEditorState = useEditorState;
64
+ const selectEditorState = useEditorState as (options: UseEditorStateOptions<
65
+ TSelectorResult,
66
+ Editor
67
+ >, slot: symbol) => TSelectorResult;
32
68
  return selectEditorState(
33
69
  {
34
70
  editor,
@@ -39,7 +75,7 @@ export function useTiptapState(selector, ...args) {
39
75
  );
40
76
  }
41
77
 
42
- export function TiptapWrapper({ children, ...props }) @{
78
+ export function TiptapWrapper({ children, ...props }: TiptapWrapperProps) @{
43
79
  const resolvedEditor =
44
80
  'editor' in props ? props.editor : props.instance;
45
81
 
@@ -58,10 +94,10 @@ export function TiptapWrapper({ children, ...props }) @{
58
94
  TiptapWrapper.displayName = 'Tiptap';
59
95
 
60
96
  /** Render EditorContent using the editor supplied by the nearest Tiptap root. */
61
- export function TiptapContent(props) @{
97
+ export function TiptapContent(props: TiptapContentProps) @{
62
98
  const { editor } = useTiptap();
63
99
 
64
- <EditorContent editor={editor} {...props} />
100
+ <EditorContent {...props} editor={editor} />
65
101
  }
66
102
 
67
103
  TiptapContent.displayName = 'Tiptap.Content';
@@ -1,5 +1,6 @@
1
1
  import type { Editor } from '@tiptap/core';
2
- import type { Context } from 'octane';
2
+ import type { Context, OctaneNode } from 'octane';
3
+ import type { Octane } from 'octane/jsx-runtime';
3
4
 
4
5
  import type { EditorContentProps } from './EditorContent.tsrx';
5
6
  import type { EditorStateSnapshot } from './useEditorState';
@@ -30,7 +31,7 @@ export type TiptapWrapperEditorInstanceProps =
30
31
  };
31
32
 
32
33
  export type TiptapWrapperProps = TiptapWrapperEditorInstanceProps & {
33
- children: unknown;
34
+ children: OctaneNode;
34
35
  };
35
36
 
36
37
  export type TiptapContentProps = Omit<EditorContentProps, 'editor' | 'ref'> & {
@@ -38,8 +39,8 @@ export type TiptapContentProps = Omit<EditorContentProps, 'editor' | 'ref'> & {
38
39
  ref?: never;
39
40
  };
40
41
 
41
- export declare function TiptapWrapper(props: TiptapWrapperProps): unknown;
42
- export declare function TiptapContent(props: TiptapContentProps): unknown;
42
+ export declare function TiptapWrapper(props: TiptapWrapperProps): Octane.JSX.Element;
43
+ export declare function TiptapContent(props: TiptapContentProps): Octane.JSX.Element;
43
44
 
44
45
  export declare const Tiptap: typeof TiptapWrapper & {
45
46
  Content: typeof TiptapContent;