@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.
@@ -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
- * @experimental Built on the `@experimental` ViewNode tree of
18
- * `@bloklabs/core/view` — not frozen until a second framework adapter
19
- * consumes it.
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 / custom renderers / unknown-block policy
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(blocksToViewNodes(data, { schema, renderers, onUnknownBlock }))
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 an SVG string. */
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). Without
775
- * it a custom tool's label is only localizable through the legacy
776
- * capitalized-tool-name fallback.
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
- * @experimental Built on the `@experimental` view tree of
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
- /** Class for the single wrapper div. */
910
- className?: string;
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 ids, no async, no effects. Content is mapped from the
916
- * sanitized view tree to real React elements, so it renders identically
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
- * @experimental Not frozen until a second framework adapter consumes the
920
- * underlying view tree.
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
- * @experimental Not frozen until a second framework adapter consumes the
932
- * underlying view tree.
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,