@bloklabs/react 1.4.2 → 1.4.4
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/README.md +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +328 -213
- package/package.json +2 -2
- package/src/BlokContent.tsx +44 -0
- package/src/BlokEditor.tsx +26 -4
- package/src/BlokView.tsx +34 -14
- package/src/config-keys.ts +2 -0
- package/src/createReactBlock.tsx +134 -20
- package/src/createReactInlineTool.tsx +26 -3
- package/src/index.ts +6 -0
- package/src/useBlok.ts +32 -2
- package/src/useBlokHandle.ts +101 -0
- package/src/useBlokView.ts +11 -6
- package/types/index.d.ts +100 -21
package/src/useBlokView.ts
CHANGED
|
@@ -14,11 +14,11 @@ import { viewNodesToReact } from './view-nodes-to-react';
|
|
|
14
14
|
* Synchronous and effect-free (SSR-safe); memoized on the data reference and
|
|
15
15
|
* the individual option values.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
* `@
|
|
19
|
-
*
|
|
17
|
+
* The props-based signature is semver-stable. The raw `ViewNode` tree the
|
|
18
|
+
* bindings map from remains `@experimental` — but consumers of this hook never
|
|
19
|
+
* touch it directly.
|
|
20
20
|
* @param data - saved document (strict or loose wire shape; nullish tolerated)
|
|
21
|
-
* @param options - schema /
|
|
21
|
+
* @param options - schema / renderers / unknown-block policy / toolAttributes / blockIds / transformUrl
|
|
22
22
|
*/
|
|
23
23
|
export const useBlokView = (
|
|
24
24
|
data: OutputData | LooseOutputData | null | undefined,
|
|
@@ -27,12 +27,17 @@ export const useBlokView = (
|
|
|
27
27
|
const schema = options?.schema;
|
|
28
28
|
const renderers = options?.renderers;
|
|
29
29
|
const onUnknownBlock = options?.onUnknownBlock;
|
|
30
|
+
const toolAttributes = options?.toolAttributes;
|
|
31
|
+
const blockIds = options?.blockIds;
|
|
32
|
+
const transformUrl = options?.transformUrl;
|
|
30
33
|
|
|
31
34
|
return useMemo(() => {
|
|
32
35
|
return createElement(
|
|
33
36
|
Fragment,
|
|
34
37
|
null,
|
|
35
|
-
...viewNodesToReact(
|
|
38
|
+
...viewNodesToReact(
|
|
39
|
+
blocksToViewNodes(data, { schema, renderers, onUnknownBlock, toolAttributes, blockIds, transformUrl })
|
|
40
|
+
)
|
|
36
41
|
);
|
|
37
|
-
}, [data, schema, renderers, onUnknownBlock]);
|
|
42
|
+
}, [data, schema, renderers, onUnknownBlock, toolAttributes, blockIds, transformUrl]);
|
|
38
43
|
};
|
package/types/index.d.ts
CHANGED
|
@@ -8,6 +8,11 @@ import type { BlockTuneData } from '@bloklabs/core';
|
|
|
8
8
|
import type { MarkdownImportConfig } from '@bloklabs/core/markdown';
|
|
9
9
|
import type React from 'react';
|
|
10
10
|
|
|
11
|
+
// Host-i18n bridge (#41): the direction/normalization helpers re-exported by
|
|
12
|
+
// the runtime index. Bare `@bloklabs/core/locales` resolves to the core's own
|
|
13
|
+
// self-contained declaration, so this stays free of any `../src` reference.
|
|
14
|
+
export { getDirection, normalizeLocale } from '@bloklabs/core/locales';
|
|
15
|
+
|
|
11
16
|
/**
|
|
12
17
|
* Configuration for the useBlok hook.
|
|
13
18
|
* Accepts all BlokConfig properties except `holder`, plus a React-only `width` prop.
|
|
@@ -102,6 +107,13 @@ export interface BlokEditorProps
|
|
|
102
107
|
'data-testid'?: string;
|
|
103
108
|
/** Called once the editor is ready, with the live Blok instance (ref is also committed). */
|
|
104
109
|
onReady?: (editor: Blok) => void;
|
|
110
|
+
/**
|
|
111
|
+
* Library-neutral shorthand for the active locale — a plain BCP-47 string the
|
|
112
|
+
* host owns and updates. Normalized and routed to `editor.i18n.update` in
|
|
113
|
+
* place. Shorthand for `i18n={{ locale }}` (wins if both are set); pair with
|
|
114
|
+
* the re-exported `getDirection` / `normalizeLocale` to compute `dir`.
|
|
115
|
+
*/
|
|
116
|
+
locale?: string;
|
|
105
117
|
/**
|
|
106
118
|
* Called after a batch render completes (core `blocks:rendered` event). The
|
|
107
119
|
* declarative analog of `ref.current.on('blocks:rendered', …)` — mirrors the
|
|
@@ -131,6 +143,47 @@ export declare const BlokEditor: React.ForwardRefExoticComponent<
|
|
|
131
143
|
BlokEditorProps & React.RefAttributes<Blok | null>
|
|
132
144
|
>;
|
|
133
145
|
|
|
146
|
+
/**
|
|
147
|
+
* A typed, null-safe imperative handle for a `<BlokEditor>`. Its methods safely
|
|
148
|
+
* no-op until the editor is ready, so consumers don't re-declare a
|
|
149
|
+
* `forwardRef`/`useImperativeHandle` with `?.` guards. Attach via
|
|
150
|
+
* `<BlokEditor ref={handle.ref} />`.
|
|
151
|
+
*
|
|
152
|
+
* `current` / `isReady` are imperative reads (they do NOT trigger a re-render
|
|
153
|
+
* when readiness flips — use `useBlokReady` for that); `current` is the escape
|
|
154
|
+
* hatch to the full editor API beyond the shortcuts below.
|
|
155
|
+
*/
|
|
156
|
+
export interface BlokEditorHandle {
|
|
157
|
+
/** Ref callback to attach to `<BlokEditor ref={handle.ref} />`. Stable across renders. */
|
|
158
|
+
readonly ref: React.RefCallback<Blok | null>;
|
|
159
|
+
/** The live editor instance, or null before ready / after unmount. */
|
|
160
|
+
readonly current: Blok | null;
|
|
161
|
+
/** True once the editor is ready (the ref has been populated). */
|
|
162
|
+
readonly isReady: boolean;
|
|
163
|
+
/** Focus the editor. Returns whether focus landed; `false` (no-op) before ready. */
|
|
164
|
+
focus(atEnd?: boolean): boolean;
|
|
165
|
+
/** Clear all content. Resolves immediately (no-op) before ready. */
|
|
166
|
+
clear(): Promise<void>;
|
|
167
|
+
/** Serialize the current content, or resolve to `null` before ready. */
|
|
168
|
+
save(): Promise<OutputData | null>;
|
|
169
|
+
/** Render new content in place. Resolves immediately (no-op) before ready. */
|
|
170
|
+
render(data: OutputData | LooseOutputData): Promise<void>;
|
|
171
|
+
/** Set or toggle read-only mode. Resolves to the resulting state; `false` (no-op) before ready. */
|
|
172
|
+
setReadOnly(state?: boolean): Promise<boolean>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Returns a stable, null-safe {@link BlokEditorHandle} for a `<BlokEditor>`.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```tsx
|
|
180
|
+
* const blok = useBlokHandle();
|
|
181
|
+
* <BlokEditor ref={blok.ref} tools={tools} data={data} />;
|
|
182
|
+
* <button onClick={() => blok.focus()}>Focus</button>;
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
export declare function useBlokHandle(): BlokEditorHandle;
|
|
186
|
+
|
|
134
187
|
/**
|
|
135
188
|
* Registers app-wide Blok defaults for every `useBlok` / `<BlokEditor>` rendered
|
|
136
189
|
* beneath it. Per-instance config overrides these, and the `tools` registry is
|
|
@@ -645,8 +698,8 @@ export type ReactBlockViewProps<Data, Config = Record<string, unknown>> = Omit<
|
|
|
645
698
|
'commit' | 'readOnly'
|
|
646
699
|
>;
|
|
647
700
|
|
|
648
|
-
/** A toolbox entry whose `icon` may be a React element instead of
|
|
649
|
-
export type ReactToolboxConfigEntry = Omit<ToolboxConfigEntry, 'icon'> & {
|
|
701
|
+
/** A toolbox entry whose `icon`/`title` may be a React element instead of a string. */
|
|
702
|
+
export type ReactToolboxConfigEntry = Omit<ToolboxConfigEntry, 'icon' | 'title'> & {
|
|
650
703
|
/**
|
|
651
704
|
* Toolbox icon. Accepts the markup string core consumes directly, or the same
|
|
652
705
|
* React element rendered in the block body — the factory serializes elements
|
|
@@ -655,6 +708,16 @@ export type ReactToolboxConfigEntry = Omit<ToolboxConfigEntry, 'icon'> & {
|
|
|
655
708
|
* serialization retries on the next browser-side access.
|
|
656
709
|
*/
|
|
657
710
|
icon?: string | React.ReactElement;
|
|
711
|
+
/**
|
|
712
|
+
* Toolbox label. A plain string is translated by core through the
|
|
713
|
+
* `toolNames.*` namespace. A ReactElement instead renders live in the host's
|
|
714
|
+
* React tree (via the editor's shared BlockPortalHost), so the label tracks
|
|
715
|
+
* the host's OWN i18n — no mirroring strings into Blok's dictionary. Pair a
|
|
716
|
+
* ReactElement title with `titleKey` so multilingual search still resolves an
|
|
717
|
+
* English term. Requires `useBlok`/`BlokEditor` (which wires the portal
|
|
718
|
+
* registry); with vanilla core a ReactElement title is dropped.
|
|
719
|
+
*/
|
|
720
|
+
title?: string | React.ReactElement;
|
|
658
721
|
};
|
|
659
722
|
|
|
660
723
|
/** Toolbox config for React blocks: single entry or several variants. */
|
|
@@ -771,9 +834,9 @@ export interface CreateReactInlineToolSpec<Config = Record<string, unknown>, Sta
|
|
|
771
834
|
title?: string;
|
|
772
835
|
/**
|
|
773
836
|
* Translation key for the toolbar label, resolved by core i18n as
|
|
774
|
-
* `toolNames.{titleKey}` (or used verbatim when it contains a dot).
|
|
775
|
-
*
|
|
776
|
-
*
|
|
837
|
+
* `toolNames.{titleKey}` (or used verbatim when it contains a dot). Defaults
|
|
838
|
+
* to `type`, so a custom tool localizes through the conventional
|
|
839
|
+
* `toolNames.{type}` key (as built-in tools do) with no extra wiring.
|
|
777
840
|
*/
|
|
778
841
|
titleKey?: string;
|
|
779
842
|
/** The component rendered as the tool's toolbar icon/UI. */
|
|
@@ -831,7 +894,20 @@ export interface CreateReactInlineToolSpec<Config = Record<string, unknown>, Sta
|
|
|
831
894
|
*/
|
|
832
895
|
export declare function createReactInlineTool<Config = Record<string, unknown>, State = void>(
|
|
833
896
|
spec: CreateReactInlineToolSpec<Config, State>
|
|
834
|
-
): InlineToolConstructable
|
|
897
|
+
): InlineToolConstructable & {
|
|
898
|
+
readonly __isBlokReactInlineTool: true;
|
|
899
|
+
readonly isInline: true;
|
|
900
|
+
/** The tool's type name (also the default `titleKey`). */
|
|
901
|
+
readonly type: string;
|
|
902
|
+
readonly title: string | undefined;
|
|
903
|
+
/** Translation key core resolves as `toolNames.{titleKey}`; defaults to `type`. */
|
|
904
|
+
readonly titleKey: string;
|
|
905
|
+
readonly shortcut: string | undefined;
|
|
906
|
+
/** The declared mark spec, exposed so tools can be unit-tested without mocking the factory. */
|
|
907
|
+
readonly mark: MarkSpec<State> | undefined;
|
|
908
|
+
readonly sanitize: SanitizerConfig | undefined;
|
|
909
|
+
readonly isReadOnlySupported: boolean;
|
|
910
|
+
};
|
|
835
911
|
|
|
836
912
|
/**
|
|
837
913
|
* Tool-config key carrying the editor's portal registry into a
|
|
@@ -891,13 +967,11 @@ export interface BlockPortalHostProps {
|
|
|
891
967
|
export declare function BlockPortalHost(props: BlockPortalHostProps): React.ReactElement;
|
|
892
968
|
|
|
893
969
|
/**
|
|
894
|
-
* Props for {@link BlokView}.
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
* `@bloklabs/core/view` — not frozen until a second framework adapter
|
|
898
|
-
* consumes it.
|
|
970
|
+
* Props for {@link BlokView}. Beyond the render options below, any standard
|
|
971
|
+
* `<div>` attribute (`id`, `style`, `data-*`, `aria-*`, event handlers, …) is
|
|
972
|
+
* forwarded onto the single wrapper element.
|
|
899
973
|
*/
|
|
900
|
-
export interface BlokViewProps {
|
|
974
|
+
export interface BlokViewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
901
975
|
/** Saved document to display (strict or loose wire shape; nullish tolerated). */
|
|
902
976
|
data: OutputData | LooseOutputData | null | undefined;
|
|
903
977
|
/** View schema from `defineBlokSchema` — display under the composition that produced the document. */
|
|
@@ -906,18 +980,23 @@ export interface BlokViewProps {
|
|
|
906
980
|
renderers?: BlocksToHtmlOptions['renderers'];
|
|
907
981
|
/** Unknown-tool policy (default 'skip'). */
|
|
908
982
|
onUnknownBlock?: BlocksToHtmlOptions['onUnknownBlock'];
|
|
909
|
-
/**
|
|
910
|
-
|
|
983
|
+
/** Stamp `data-blok-tool="<type>"` on each block root as a styling hook (pairs with the opt-in view stylesheet). */
|
|
984
|
+
toolAttributes?: BlocksToHtmlOptions['toolAttributes'];
|
|
985
|
+
/** Stamp `data-blok-id="<id>"` on each block root (list items on their `<li>`) for "copy link to block" deep links. */
|
|
986
|
+
blockIds?: BlocksToHtmlOptions['blockIds'];
|
|
987
|
+
/** Pure URL rewrite hook for block URLs + inline anchors, run before the unsafe-scheme strip. */
|
|
988
|
+
transformUrl?: BlocksToHtmlOptions['transformUrl'];
|
|
911
989
|
}
|
|
912
990
|
|
|
913
991
|
/**
|
|
914
992
|
* Display a saved Blok document synchronously — one `<div>` wrapper, no
|
|
915
|
-
* editor chrome, no
|
|
916
|
-
*
|
|
917
|
-
* under SSR.
|
|
993
|
+
* editor chrome, no async, no effects. Content is mapped from the sanitized
|
|
994
|
+
* view tree to real React elements, so it renders identically under SSR.
|
|
918
995
|
*
|
|
919
|
-
*
|
|
920
|
-
*
|
|
996
|
+
* The intended read-only path — reach for it instead of `BlokEditor readOnly`,
|
|
997
|
+
* which ships the full editing runtime to every viewer. The props API is
|
|
998
|
+
* semver-stable; only the raw `ViewNode` tree the bindings map from remains
|
|
999
|
+
* `@experimental`, and using `BlokView` never exposes you to it.
|
|
921
1000
|
*/
|
|
922
1001
|
export declare function BlokView(props: BlokViewProps): React.ReactNode;
|
|
923
1002
|
|
|
@@ -928,8 +1007,8 @@ export declare function BlokView(props: BlokViewProps): React.ReactNode;
|
|
|
928
1007
|
* Synchronous, effect-free (SSR-safe), memoized on the data reference and
|
|
929
1008
|
* the individual option values.
|
|
930
1009
|
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
1010
|
+
* The props-based signature is semver-stable; only the raw `ViewNode` tree the
|
|
1011
|
+
* bindings map from remains `@experimental`.
|
|
933
1012
|
*/
|
|
934
1013
|
export declare function useBlokView(
|
|
935
1014
|
data: OutputData | LooseOutputData | null | undefined,
|