@bloklabs/react 1.15.1 → 1.16.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/dist/index.cjs +1 -1
- package/dist/index.mjs +276 -256
- package/package.json +2 -2
- package/src/BlokView.tsx +13 -1
- package/src/config-keys.ts +6 -2
- package/src/createReactBlock.tsx +22 -1
- package/src/useBlok.ts +15 -8
- package/src/useBlokHandle.ts +4 -0
- package/src/useBlokView.ts +8 -2
- package/src/view-nodes-to-react.ts +20 -5
- package/types/index.d.ts +22 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloklabs/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "React block editor — official React bindings for Blok, the headless Notion-like rich text editor. Hooks and components that render blocks and save to JSON.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "JackUait",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"test": "yarn run -T vitest run"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@bloklabs/core": "^1.
|
|
57
|
+
"@bloklabs/core": "^1.16.0",
|
|
58
58
|
"react": "^18.0.0 || ^19.0.0",
|
|
59
59
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
60
60
|
}
|
package/src/BlokView.tsx
CHANGED
|
@@ -26,6 +26,10 @@ export interface BlokViewProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chi
|
|
|
26
26
|
transformUrl?: BlocksToHtmlOptions['transformUrl'];
|
|
27
27
|
/** Custom renderers for inline elements, keyed by lowercase tag name (equations, mentions). */
|
|
28
28
|
inlineRenderers?: BlocksToHtmlOptions['inlineRenderers'];
|
|
29
|
+
/** Build the link for a `page` block from its `pageId`. A page card is a link only with this and an allowing {@link pageInfo}. */
|
|
30
|
+
pageHref?: BlocksToHtmlOptions['pageHref'];
|
|
31
|
+
/** Host metadata (title, icon, access) for a `page` block. Without it a page card shows a neutral "Page" label and no link. */
|
|
32
|
+
pageInfo?: BlocksToHtmlOptions['pageInfo'];
|
|
29
33
|
/**
|
|
30
34
|
* Render with the editor's presentational classes and per-block scaffolding
|
|
31
35
|
* so the output matches a read-only editor render (default `true`). Needs
|
|
@@ -33,6 +37,8 @@ export interface BlokViewProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chi
|
|
|
33
37
|
* semantic markup.
|
|
34
38
|
*/
|
|
35
39
|
classes?: BlocksToHtmlOptions['classes'];
|
|
40
|
+
/** Document direction: set as `dir` on the wrapper, and each block follows its own text. */
|
|
41
|
+
direction?: BlocksToHtmlOptions['direction'];
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
/**
|
|
@@ -59,7 +65,10 @@ export const BlokView = ({
|
|
|
59
65
|
blockIds,
|
|
60
66
|
transformUrl,
|
|
61
67
|
inlineRenderers,
|
|
68
|
+
pageHref,
|
|
69
|
+
pageInfo,
|
|
62
70
|
classes,
|
|
71
|
+
direction,
|
|
63
72
|
...divProps
|
|
64
73
|
}: BlokViewProps): ReactNode => {
|
|
65
74
|
/**
|
|
@@ -77,7 +86,10 @@ export const BlokView = ({
|
|
|
77
86
|
blockIds,
|
|
78
87
|
transformUrl,
|
|
79
88
|
inlineRenderers,
|
|
89
|
+
pageHref,
|
|
90
|
+
pageInfo,
|
|
80
91
|
classes: classes ?? true,
|
|
92
|
+
direction,
|
|
81
93
|
});
|
|
82
94
|
|
|
83
95
|
/**
|
|
@@ -90,5 +102,5 @@ export const BlokView = ({
|
|
|
90
102
|
*
|
|
91
103
|
* Written BEFORE the `divProps` spread so a caller can still override it.
|
|
92
104
|
*/
|
|
93
|
-
return <div data-blok-interface="view" {...divProps}>{content}</div>;
|
|
105
|
+
return <div data-blok-interface="view" dir={direction} {...divProps}>{content}</div>;
|
|
94
106
|
};
|
package/src/config-keys.ts
CHANGED
|
@@ -23,6 +23,7 @@ export const USE_BLOK_CONFIG_KEYS = [
|
|
|
23
23
|
'tools',
|
|
24
24
|
'data',
|
|
25
25
|
'minHeight',
|
|
26
|
+
'loader',
|
|
26
27
|
'logLevel',
|
|
27
28
|
'readOnly',
|
|
28
29
|
'i18n',
|
|
@@ -33,12 +34,14 @@ export const USE_BLOK_CONFIG_KEYS = [
|
|
|
33
34
|
'onChange',
|
|
34
35
|
'onSave',
|
|
35
36
|
'onError',
|
|
37
|
+
'onImageFailure',
|
|
36
38
|
'onEnter',
|
|
37
39
|
'onSubmit',
|
|
38
40
|
'onBeforeRender',
|
|
39
41
|
'onAfterRender',
|
|
40
42
|
'inlineToolbar',
|
|
41
43
|
'inlineEmoji',
|
|
44
|
+
'find',
|
|
42
45
|
'tunes',
|
|
43
46
|
'style',
|
|
44
47
|
'theme',
|
|
@@ -51,10 +54,13 @@ export const USE_BLOK_CONFIG_KEYS = [
|
|
|
51
54
|
'width',
|
|
52
55
|
'migrations',
|
|
53
56
|
'uploader',
|
|
57
|
+
'media',
|
|
54
58
|
'server',
|
|
55
59
|
'ticket',
|
|
56
60
|
'persistence',
|
|
57
61
|
'collaboration',
|
|
62
|
+
'documentId',
|
|
63
|
+
'tabSync',
|
|
58
64
|
'captureClicksBelowEditor',
|
|
59
65
|
] as const satisfies readonly (keyof UseBlokConfig)[];
|
|
60
66
|
|
|
@@ -64,5 +70,3 @@ export const USE_BLOK_CONFIG_KEYS = [
|
|
|
64
70
|
*/
|
|
65
71
|
type _MissingConfigKey = Exclude<keyof UseBlokConfig, (typeof USE_BLOK_CONFIG_KEYS)[number]>;
|
|
66
72
|
const _exhaustive: _MissingConfigKey extends never ? true : never = true;
|
|
67
|
-
|
|
68
|
-
void _exhaustive;
|
package/src/createReactBlock.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
fillDefaults,
|
|
11
11
|
mountChildBlocks,
|
|
12
12
|
type PropSchema,
|
|
13
|
+
withSlotlessDescendants,
|
|
13
14
|
} from '@bloklabs/core/adapters';
|
|
14
15
|
import {
|
|
15
16
|
BLOK_PORTAL_REGISTRY_CONFIG_KEY,
|
|
@@ -330,6 +331,15 @@ export interface CreateReactBlockSpec<Data = BlockToolData, Config = Record<stri
|
|
|
330
331
|
* @param block - this block's per-block API
|
|
331
332
|
*/
|
|
332
333
|
getToolbarAnchorElement?: (host: HTMLElement, block: BlockAPI) => HTMLElement | null | undefined;
|
|
334
|
+
/**
|
|
335
|
+
* Enter (or Cmd/Ctrl+Enter) on this block while it is the keyboard
|
|
336
|
+
* navigation target — core's `onNavigationEnter` hook. Return true when the
|
|
337
|
+
* block handled it (e.g. opened a link); omit it, or return false, to let
|
|
338
|
+
* core put the caret in the block.
|
|
339
|
+
* @param event - the Enter keydown
|
|
340
|
+
* @param block - this block's per-block API
|
|
341
|
+
*/
|
|
342
|
+
onNavigationEnter?: (event: KeyboardEvent, block: BlockAPI) => boolean;
|
|
333
343
|
/** Optional lifecycle callbacks mapped from Blok's block hooks. */
|
|
334
344
|
onRendered?: (block: BlockAPI) => void;
|
|
335
345
|
/**
|
|
@@ -412,6 +422,7 @@ export function createReactBlock<Data = BlockToolData, Config = Record<string, u
|
|
|
412
422
|
setData(newData: BlockToolData): Promise<boolean>;
|
|
413
423
|
setReadOnly(state: boolean): void;
|
|
414
424
|
getToolbarAnchorElement(): HTMLElement | undefined;
|
|
425
|
+
onNavigationEnter(event: KeyboardEvent): boolean;
|
|
415
426
|
rendered(): void;
|
|
416
427
|
moved(): void;
|
|
417
428
|
removed(): void;
|
|
@@ -758,7 +769,9 @@ export function createReactBlock<Data = BlockToolData, Config = Record<string, u
|
|
|
758
769
|
|
|
759
770
|
const children = blockApi.getChildren();
|
|
760
771
|
|
|
761
|
-
|
|
772
|
+
// A slotless child's own children live in this slot too. Decoration and
|
|
773
|
+
// the mounted event stay per DIRECT child.
|
|
774
|
+
mountChildBlocks(slotRef.current, withSlotlessDescendants(children, id => api.blocks.getChildren(id)));
|
|
762
775
|
applyChildDecoration(ledgerRef.current, children, { childAttributes,
|
|
763
776
|
childContentAttributes });
|
|
764
777
|
emitChildrenMounted(api, blockApi.id, children);
|
|
@@ -910,6 +923,14 @@ export function createReactBlock<Data = BlockToolData, Config = Record<string, u
|
|
|
910
923
|
return spec.getToolbarAnchorElement(host, this.blockApi) ?? undefined;
|
|
911
924
|
}
|
|
912
925
|
|
|
926
|
+
/**
|
|
927
|
+
* Core's navigation-mode Enter hook. Only a real `true` from the spec
|
|
928
|
+
* counts as handled.
|
|
929
|
+
*/
|
|
930
|
+
public onNavigationEnter(event: KeyboardEvent): boolean {
|
|
931
|
+
return spec.onNavigationEnter?.(event, this.blockApi) === true;
|
|
932
|
+
}
|
|
933
|
+
|
|
913
934
|
public moved(): void {
|
|
914
935
|
// No remount: Blok relocates the host element; the portal follows it.
|
|
915
936
|
spec.onMoved?.(this.blockApi);
|
package/src/useBlok.ts
CHANGED
|
@@ -186,8 +186,11 @@ export function useBlok(configInput: UseBlokConfig, deps?: DependencyList): Blok
|
|
|
186
186
|
// surfaced anywhere.
|
|
187
187
|
const constructedCollaborationRef = useRef(config.collaboration);
|
|
188
188
|
const renderChainRef = useRef<Promise<void>>(Promise.resolve());
|
|
189
|
-
//
|
|
190
|
-
|
|
189
|
+
// Editor the single collaboration warning has already been emitted for (see
|
|
190
|
+
// the data effect). Keyed by the editor, not a boolean, so the editor a `deps`
|
|
191
|
+
// change creates warns on its own first ignored `data` change instead of
|
|
192
|
+
// inheriting the previous editor's "already warned" flag and going silent.
|
|
193
|
+
const collaborationWarnedForRef = useRef<Blok | null>(null);
|
|
191
194
|
// The content of the render currently queued/in-flight (distinct from
|
|
192
195
|
// `lastRenderedDataRef`, which tracks the last SUCCESSFULLY rendered content).
|
|
193
196
|
// Used to dedupe a re-queue of the same in-flight content without advancing the
|
|
@@ -716,22 +719,26 @@ export function useBlok(configInput: UseBlokConfig, deps?: DependencyList): Blok
|
|
|
716
719
|
const appliedI18nRef = useRef<UseBlokConfig['i18n']>(config.i18n);
|
|
717
720
|
|
|
718
721
|
useEffect(() => {
|
|
719
|
-
|
|
722
|
+
const previous = appliedI18nRef.current;
|
|
723
|
+
|
|
724
|
+
if (editor === null || (i18n === undefined && previous?.direction === undefined)) {
|
|
720
725
|
return;
|
|
721
726
|
}
|
|
722
727
|
|
|
723
|
-
if (deepEqual(i18n,
|
|
728
|
+
if (deepEqual(i18n, previous)) {
|
|
724
729
|
return;
|
|
725
730
|
}
|
|
726
731
|
|
|
727
732
|
appliedI18nRef.current = i18n;
|
|
728
733
|
|
|
729
|
-
const { locale, messages, direction } = i18n;
|
|
734
|
+
const { locale, messages, direction } = i18n ?? {};
|
|
735
|
+
// A dropped direction must be cleared, or the editor keeps the old one.
|
|
736
|
+
const nextDirection = direction ?? (previous?.direction === undefined ? undefined : null);
|
|
730
737
|
|
|
731
738
|
void editor.i18n.update({
|
|
732
739
|
...(locale === undefined ? {} : { locale }),
|
|
733
740
|
...(messages === undefined ? {} : { messages }),
|
|
734
|
-
...(
|
|
741
|
+
...(nextDirection === undefined ? {} : { direction: nextDirection }),
|
|
735
742
|
});
|
|
736
743
|
}, [editor, i18n]);
|
|
737
744
|
|
|
@@ -829,8 +836,8 @@ export function useBlok(configInput: UseBlokConfig, deps?: DependencyList): Blok
|
|
|
829
836
|
const mountedCollaboration = constructedCollaborationRef.current;
|
|
830
837
|
|
|
831
838
|
if (mountedCollaboration !== undefined) {
|
|
832
|
-
if (
|
|
833
|
-
|
|
839
|
+
if (collaborationWarnedForRef.current !== editor) {
|
|
840
|
+
collaborationWarnedForRef.current = editor;
|
|
834
841
|
console.warn(collaborationDataIgnoredMessage(mountedCollaboration.doc));
|
|
835
842
|
}
|
|
836
843
|
|
package/src/useBlokHandle.ts
CHANGED
|
@@ -44,6 +44,8 @@ export interface BlokEditorHandle {
|
|
|
44
44
|
clear(): Promise<void>;
|
|
45
45
|
/** Serialize the current content, or resolve to `null` before ready. */
|
|
46
46
|
save(): Promise<OutputData | null>;
|
|
47
|
+
/** Ask before an in-app navigation (see `Blok.confirmLeave`). Resolves `true` before ready. */
|
|
48
|
+
confirmLeave(): Promise<boolean>;
|
|
47
49
|
/**
|
|
48
50
|
* Render new content in place. Resolves immediately (no-op) before ready.
|
|
49
51
|
*
|
|
@@ -111,6 +113,8 @@ export function useBlokHandle(): BlokEditorHandle {
|
|
|
111
113
|
},
|
|
112
114
|
save: (): Promise<OutputData | null> =>
|
|
113
115
|
instanceRef.current !== null ? instanceRef.current.save() : Promise.resolve(null),
|
|
116
|
+
confirmLeave: (): Promise<boolean> =>
|
|
117
|
+
instanceRef.current !== null ? instanceRef.current.confirmLeave() : Promise.resolve(true),
|
|
114
118
|
render: (data: OutputData | LooseOutputData): Promise<void> => {
|
|
115
119
|
const instance = instanceRef.current;
|
|
116
120
|
|
package/src/useBlokView.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { viewNodesToReact } from './view-nodes-to-react';
|
|
|
18
18
|
* bindings map from remains `@experimental` — but consumers of this hook never
|
|
19
19
|
* touch it directly.
|
|
20
20
|
* @param data - saved document (strict or loose wire shape; nullish tolerated)
|
|
21
|
-
* @param options - schema / renderers / inlineRenderers / unknown-block policy / toolAttributes / blockIds / transformUrl
|
|
21
|
+
* @param options - schema / renderers / inlineRenderers / unknown-block policy / toolAttributes / blockIds / transformUrl / pageHref / pageInfo
|
|
22
22
|
*/
|
|
23
23
|
export const useBlokView = (
|
|
24
24
|
data: OutputData | LooseOutputData | null | undefined,
|
|
@@ -31,7 +31,10 @@ export const useBlokView = (
|
|
|
31
31
|
const blockIds = options?.blockIds;
|
|
32
32
|
const transformUrl = options?.transformUrl;
|
|
33
33
|
const inlineRenderers = options?.inlineRenderers;
|
|
34
|
+
const pageHref = options?.pageHref;
|
|
35
|
+
const pageInfo = options?.pageInfo;
|
|
34
36
|
const classes = options?.classes;
|
|
37
|
+
const direction = options?.direction;
|
|
35
38
|
|
|
36
39
|
return useMemo(() => {
|
|
37
40
|
return createElement(
|
|
@@ -46,6 +49,8 @@ export const useBlokView = (
|
|
|
46
49
|
blockIds,
|
|
47
50
|
transformUrl,
|
|
48
51
|
inlineRenderers,
|
|
52
|
+
pageHref,
|
|
53
|
+
pageInfo,
|
|
49
54
|
/**
|
|
50
55
|
* Opt-in, NOT defaulted on. Parity rendering wraps every block in the
|
|
51
56
|
* core's holder → content scaffolding, which would contradict this
|
|
@@ -57,8 +62,9 @@ export const useBlokView = (
|
|
|
57
62
|
* already owns a wrapper and its job IS to look like the editor.
|
|
58
63
|
*/
|
|
59
64
|
classes,
|
|
65
|
+
direction,
|
|
60
66
|
})
|
|
61
67
|
)
|
|
62
68
|
);
|
|
63
|
-
}, [data, schema, renderers, onUnknownBlock, toolAttributes, blockIds, transformUrl, inlineRenderers, classes]);
|
|
69
|
+
}, [data, schema, renderers, onUnknownBlock, toolAttributes, blockIds, transformUrl, inlineRenderers, pageHref, pageInfo, classes, direction]);
|
|
64
70
|
};
|
|
@@ -48,6 +48,18 @@ const ATTRIBUTE_NAME_MAP: Record<string, string> = {
|
|
|
48
48
|
itemscope: 'itemScope',
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* React names SVG attributes in camelCase (`stroke-width` → `strokeWidth`) and
|
|
53
|
+
* warns on the raw form. Only inside `<svg>`: elsewhere a hyphenated name (a
|
|
54
|
+
* custom element's attribute) must reach the DOM as written.
|
|
55
|
+
* @param name - SVG attribute name
|
|
56
|
+
*/
|
|
57
|
+
const svgPropName = (name: string): string => (
|
|
58
|
+
name.startsWith('aria-') || name.startsWith('data-')
|
|
59
|
+
? name
|
|
60
|
+
: name.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase())
|
|
61
|
+
);
|
|
62
|
+
|
|
51
63
|
/**
|
|
52
64
|
* Convert a sanitized `style` attribute string to a React style object
|
|
53
65
|
* (React only accepts object styles). Custom properties keep their raw name;
|
|
@@ -85,8 +97,9 @@ const styleStringToObject = (css: string): CSSProperties => {
|
|
|
85
97
|
* Map one ViewNode attribute record to React props.
|
|
86
98
|
* @param attrs - sanitized attributes from the view tree
|
|
87
99
|
* @param key - sibling index used as the React key
|
|
100
|
+
* @param inSvg - whether the element sits in an `<svg>` subtree
|
|
88
101
|
*/
|
|
89
|
-
const attrsToProps = (attrs: Record<string, string>, key: number): Record<string, unknown> => {
|
|
102
|
+
const attrsToProps = (attrs: Record<string, string>, key: number, inSvg: boolean): Record<string, unknown> => {
|
|
90
103
|
const props: Record<string, unknown> = { key };
|
|
91
104
|
|
|
92
105
|
for (const [name, value] of Object.entries(attrs)) {
|
|
@@ -110,7 +123,7 @@ const attrsToProps = (attrs: Record<string, string>, key: number): Record<string
|
|
|
110
123
|
continue;
|
|
111
124
|
}
|
|
112
125
|
|
|
113
|
-
props[ATTRIBUTE_NAME_MAP[name] ?? name] = value;
|
|
126
|
+
props[ATTRIBUTE_NAME_MAP[name] ?? (inSvg ? svgPropName(name) : name)] = value;
|
|
114
127
|
}
|
|
115
128
|
|
|
116
129
|
return props;
|
|
@@ -120,19 +133,21 @@ const attrsToProps = (attrs: Record<string, string>, key: number): Record<string
|
|
|
120
133
|
* Map a list of ViewNodes to React nodes. Element keys come from the sibling
|
|
121
134
|
* index (the tree is static per render).
|
|
122
135
|
* @param nodes - view tree siblings
|
|
136
|
+
* @param inSvg - whether the siblings sit in an `<svg>` subtree
|
|
123
137
|
*/
|
|
124
|
-
export const viewNodesToReact = (nodes: ViewNode[]): ReactNode[] => {
|
|
138
|
+
export const viewNodesToReact = (nodes: ViewNode[], inSvg = false): ReactNode[] => {
|
|
125
139
|
return nodes.map((node, index): ReactNode => {
|
|
126
140
|
if ('text' in node) {
|
|
127
141
|
return node.text;
|
|
128
142
|
}
|
|
129
143
|
|
|
130
|
-
const
|
|
144
|
+
const svg = inSvg || node.tag === 'svg';
|
|
145
|
+
const props = attrsToProps(node.attrs, index, svg);
|
|
131
146
|
|
|
132
147
|
if (VOID_ELEMENTS.has(node.tag) || node.children.length === 0) {
|
|
133
148
|
return createElement(node.tag, props);
|
|
134
149
|
}
|
|
135
150
|
|
|
136
|
-
return createElement(node.tag, props, ...viewNodesToReact(node.children));
|
|
151
|
+
return createElement(node.tag, props, ...viewNodesToReact(node.children, svg));
|
|
137
152
|
});
|
|
138
153
|
};
|
package/types/index.d.ts
CHANGED
|
@@ -192,6 +192,8 @@ export interface BlokEditorHandle {
|
|
|
192
192
|
clear(): Promise<void>;
|
|
193
193
|
/** Serialize the current content, or resolve to `null` before ready. */
|
|
194
194
|
save(): Promise<OutputData | null>;
|
|
195
|
+
/** Ask before an in-app navigation (see `Blok.confirmLeave`). Resolves `true` before ready. */
|
|
196
|
+
confirmLeave(): Promise<boolean>;
|
|
195
197
|
/**
|
|
196
198
|
* Render new content in place. Resolves immediately (no-op) before ready. Safe
|
|
197
199
|
* to mix with a controlled `data` prop: the rendered document becomes the
|
|
@@ -521,8 +523,12 @@ export interface UseBlocksApi {
|
|
|
521
523
|
* the React-surface counterpart of core's `transactWithoutCapture`. Use for
|
|
522
524
|
* silent auto-repair/normalization that a user's undo should never step
|
|
523
525
|
* through. Mutations inside still emit reactively. Pre-ready it just runs `fn`.
|
|
526
|
+
*
|
|
527
|
+
* With `options`, the operations are data worked out from the `from` keys of
|
|
528
|
+
* block `derivedFrom` (a finished upload, for example): they join the undo
|
|
529
|
+
* entry that wrote those values, as in core.
|
|
524
530
|
*/
|
|
525
|
-
transactWithoutCapture(fn: () => void): void;
|
|
531
|
+
transactWithoutCapture(fn: () => void, options?: { derivedFrom?: string; from?: readonly string[] }): void;
|
|
526
532
|
/** The current block count. Reactive (re-reads on 'block changed'). Pre-ready: 0. */
|
|
527
533
|
getBlocksCount(): number;
|
|
528
534
|
/** The flat index of the block holding the caret, or -1 when none. Pre-ready: -1. */
|
|
@@ -908,6 +914,15 @@ export interface CreateReactBlockSpec<
|
|
|
908
914
|
* `null`/`undefined` (or omit) to keep core's default.
|
|
909
915
|
*/
|
|
910
916
|
getToolbarAnchorElement?: (host: HTMLElement, block: BlockAPI) => HTMLElement | null | undefined;
|
|
917
|
+
/**
|
|
918
|
+
* Enter (or Cmd/Ctrl+Enter) on this block while it is the keyboard
|
|
919
|
+
* navigation target — core's `onNavigationEnter` hook. Return true when the
|
|
920
|
+
* block handled it (e.g. opened a link); omit it, or return false, to let
|
|
921
|
+
* core put the caret in the block.
|
|
922
|
+
* @param event - the Enter keydown
|
|
923
|
+
* @param block - this block's per-block API
|
|
924
|
+
*/
|
|
925
|
+
onNavigationEnter?: (event: KeyboardEvent, block: BlockAPI) => boolean;
|
|
911
926
|
onRendered?: (block: BlockAPI) => void;
|
|
912
927
|
/**
|
|
913
928
|
* Fired ONCE per block instance, after the portal's FIRST commit — the first
|
|
@@ -1219,6 +1234,10 @@ export interface BlokViewProps extends Omit<React.HTMLAttributes<HTMLDivElement>
|
|
|
1219
1234
|
transformUrl?: BlocksToHtmlOptions['transformUrl'];
|
|
1220
1235
|
/** Custom renderers for inline elements, keyed by lowercase tag name (equations, mentions). */
|
|
1221
1236
|
inlineRenderers?: BlocksToHtmlOptions['inlineRenderers'];
|
|
1237
|
+
/** Build the link for a `page` block from its `pageId`. A page card is a link only with this and an allowing {@link pageInfo}. */
|
|
1238
|
+
pageHref?: BlocksToHtmlOptions['pageHref'];
|
|
1239
|
+
/** Host metadata (title, icon, access) for a `page` block. Without it a page card shows a neutral "Page" label and no link. */
|
|
1240
|
+
pageInfo?: BlocksToHtmlOptions['pageInfo'];
|
|
1222
1241
|
/**
|
|
1223
1242
|
* Render with the editor's presentational classes and per-block scaffolding
|
|
1224
1243
|
* so the output matches a read-only editor render (default `true`). Needs
|
|
@@ -1226,6 +1245,8 @@ export interface BlokViewProps extends Omit<React.HTMLAttributes<HTMLDivElement>
|
|
|
1226
1245
|
* semantic markup.
|
|
1227
1246
|
*/
|
|
1228
1247
|
classes?: BlocksToHtmlOptions['classes'];
|
|
1248
|
+
/** Document direction: set as `dir` on the wrapper, and each block follows its own text. */
|
|
1249
|
+
direction?: BlocksToHtmlOptions['direction'];
|
|
1229
1250
|
}
|
|
1230
1251
|
|
|
1231
1252
|
/**
|