@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 +5 -5
- package/src/Context.tsrx +27 -8
- package/src/Context.tsrx.d.ts +9 -6
- package/src/EditorContent.tsrx +38 -17
- package/src/EditorContent.tsrx.d.ts +5 -3
- package/src/Tiptap.tsrx +49 -13
- package/src/Tiptap.tsrx.d.ts +5 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/tiptap",
|
|
3
|
-
"version": "0.0.
|
|
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.
|
|
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.
|
|
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.
|
|
72
|
-
"octane": "0.1.
|
|
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
|
|
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}
|
|
55
|
+
{({ editor: currentEditor }: EditorContextValue) =>
|
|
56
|
+
<EditorContent {...editorContainerProps} editor={currentEditor} />}
|
|
38
57
|
</EditorConsumer>
|
|
39
58
|
{children}
|
|
40
59
|
{slotAfter}
|
package/src/Context.tsrx.d.ts
CHANGED
|
@@ -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) =>
|
|
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?:
|
|
17
|
-
slotBefore?:
|
|
18
|
-
slotAfter?:
|
|
19
|
+
children?: OctaneNode;
|
|
20
|
+
slotBefore?: OctaneNode;
|
|
21
|
+
slotAfter?: OctaneNode;
|
|
19
22
|
editorContainerProps?: EditorContainerProps;
|
|
20
23
|
};
|
|
21
24
|
|
|
22
|
-
export declare function EditorConsumer(props: EditorConsumerProps):
|
|
23
|
-
export declare function EditorProvider(props: EditorProviderProps):
|
|
25
|
+
export declare function EditorConsumer(props: EditorConsumerProps): OctaneNode;
|
|
26
|
+
export declare function EditorProvider(props: EditorProviderProps): Octane.JSX.Element | null;
|
package/src/EditorContent.tsrx
CHANGED
|
@@ -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
|
-
|
|
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
|
|
83
|
-
const
|
|
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?:
|
|
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):
|
|
22
|
-
export declare const EditorContent: (props: EditorContentProps) =>
|
|
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 {
|
|
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 {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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(
|
|
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}
|
|
100
|
+
<EditorContent {...props} editor={editor} />
|
|
65
101
|
}
|
|
66
102
|
|
|
67
103
|
TiptapContent.displayName = 'Tiptap.Content';
|
package/src/Tiptap.tsrx.d.ts
CHANGED
|
@@ -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:
|
|
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):
|
|
42
|
-
export declare function TiptapContent(props: TiptapContentProps):
|
|
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;
|