@bloklabs/react 1.2.6 → 1.4.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/README.md +69 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +485 -239
- package/package.json +25 -4
- package/src/BlokEditor.tsx +1 -1
- package/src/BlokView.tsx +37 -0
- package/src/createReactBlock.tsx +151 -8
- package/src/createReactInlineTool.tsx +197 -28
- package/src/index.ts +11 -1
- package/src/provide-blok.tsx +1 -1
- package/src/types.ts +5 -1
- package/src/useBlok.ts +182 -7
- package/src/useBlokReady.ts +60 -0
- package/src/useBlokView.ts +38 -0
- package/src/view-nodes-to-react.ts +138 -0
- package/types/index.d.ts +204 -14
package/types/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { BlokConfig, BlockToolData, OutputBlockData, OutputData } from '@bloklabs/core';
|
|
2
|
-
import type {
|
|
1
|
+
import type { BlokConfig, BlockToolData, OutputBlockData, OutputData, LooseOutputData } from '@bloklabs/core';
|
|
2
|
+
import type { BlocksToHtmlOptions } from '@bloklabs/core/view';
|
|
3
|
+
import type { BlockAPI, BlockToolConstructable, ToolboxConfigEntry } from '@bloklabs/core';
|
|
3
4
|
import type { InlineToolConstructable, SanitizerConfig } from '@bloklabs/core';
|
|
5
|
+
import type { API, MarkSnapshot, MarkSpec } from '@bloklabs/core';
|
|
4
6
|
import type { Blok, EditorWidth, BlockRenderedPayload, BlocksRenderedPayload } from '@bloklabs/core';
|
|
5
7
|
import type { BlockTuneData } from '@bloklabs/core';
|
|
6
8
|
import type { MarkdownImportConfig } from '@bloklabs/core/markdown';
|
|
@@ -11,11 +13,21 @@ import type React from 'react';
|
|
|
11
13
|
* Accepts all BlokConfig properties except `holder`, plus a React-only `width` prop.
|
|
12
14
|
*
|
|
13
15
|
* Reactive props (sync after mount without recreation):
|
|
14
|
-
* - `readOnly` — calls `editor.readOnly.set(value)
|
|
16
|
+
* - `readOnly` — calls `editor.readOnly.set(value)`; the object form also
|
|
17
|
+
* syncs `hideControls` via `editor.readOnly.set(enabled, { hideControls })`
|
|
18
|
+
* - `hideToolbar` — calls `editor.toolbar.setHidden(value)`
|
|
19
|
+
* - `inlineToolbar` — calls `editor.tools.setInlineToolbar(value)` (arrays
|
|
20
|
+
* are compared by content, so a re-created literal is a no-op)
|
|
15
21
|
* - `autofocus` — calls `editor.focus()` when changed to true
|
|
16
22
|
* - `theme` — calls `editor.theme.set(value)`
|
|
17
23
|
* - `width` — calls `editor.width.set(value)`
|
|
18
24
|
* - `placeholder` — calls `editor.placeholder.set(value)`
|
|
25
|
+
* - `style.tokens` — calls `editor.tokens.set(value)` (deep-equal–deduped;
|
|
26
|
+
* replace semantics, so pass the complete token set on each change)
|
|
27
|
+
* - `i18n` — calls `editor.i18n.update({ locale, messages, direction })`
|
|
28
|
+
* (deep-equal–deduped; relabels the editor chrome in place, so caret, focus
|
|
29
|
+
* and undo history survive a language switch). `defaultLocale` is not
|
|
30
|
+
* forwarded — it only affects the initial locale resolution.
|
|
19
31
|
* - `data` — re-renders via `editor.render(value)` when the content changes
|
|
20
32
|
* (deep-equal–deduped and serialized; seeds the initial content at creation)
|
|
21
33
|
*
|
|
@@ -541,6 +553,43 @@ export interface UseBlocksApi {
|
|
|
541
553
|
*/
|
|
542
554
|
export declare function useBlocks(editor: Blok | null): UseBlocksApi;
|
|
543
555
|
|
|
556
|
+
// ---------------------------------------------------------------------------
|
|
557
|
+
// Scoped readiness (useBlokReady)
|
|
558
|
+
// ---------------------------------------------------------------------------
|
|
559
|
+
|
|
560
|
+
/** Options accepted by {@link useBlokReady}. */
|
|
561
|
+
export interface UseBlokReadyOptions {
|
|
562
|
+
/**
|
|
563
|
+
* Restrict the wait to editors mounted inside this element. Accepts the ref
|
|
564
|
+
* you already hold on the container; it is re-read on every readiness
|
|
565
|
+
* change, so a ref that attaches after the first render is picked up.
|
|
566
|
+
*
|
|
567
|
+
* Omit it to observe every editor on the page.
|
|
568
|
+
*/
|
|
569
|
+
within?: React.RefObject<Element | null> | Element | null;
|
|
570
|
+
/**
|
|
571
|
+
* `'ready'` (default) settles when each editor has finished booting.
|
|
572
|
+
* `'rendered'` also waits for its content to be in the DOM and re-arms on
|
|
573
|
+
* every post-boot re-render.
|
|
574
|
+
*/
|
|
575
|
+
settleOn?: 'ready' | 'rendered';
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Live readiness of the Blok editors in a DOM scope.
|
|
580
|
+
*
|
|
581
|
+
* Returns `false` until every editor inside `within` is settled, and keeps
|
|
582
|
+
* following the scope afterwards: editors mounted later re-close the gate, and
|
|
583
|
+
* with `settleOn: 'rendered'` so does each re-render. A scope holding no
|
|
584
|
+
* editors is ready, so callers need no "nothing to wait for" special case.
|
|
585
|
+
*
|
|
586
|
+
* The first render always reports `false` — the scope element is not attached
|
|
587
|
+
* yet, and over-waiting is safe while under-waiting is a bug.
|
|
588
|
+
*
|
|
589
|
+
* @param options - scope and readiness depth
|
|
590
|
+
*/
|
|
591
|
+
export declare function useBlokReady(options?: UseBlokReadyOptions): boolean;
|
|
592
|
+
|
|
544
593
|
// ---------------------------------------------------------------------------
|
|
545
594
|
// Block authoring (createReactBlock + portal host)
|
|
546
595
|
// ---------------------------------------------------------------------------
|
|
@@ -587,15 +636,46 @@ export interface ReactBlockRenderProps<Data, Config = Record<string, unknown>> {
|
|
|
587
636
|
BlockChildren: React.ComponentType;
|
|
588
637
|
}
|
|
589
638
|
|
|
639
|
+
/**
|
|
640
|
+
* Props handed to a React block's `viewComponent` (the read-only renderer):
|
|
641
|
+
* the entry props minus `commit` (no write path) and `readOnly` (always true).
|
|
642
|
+
*/
|
|
643
|
+
export type ReactBlockViewProps<Data, Config = Record<string, unknown>> = Omit<
|
|
644
|
+
ReactBlockRenderProps<Data, Config>,
|
|
645
|
+
'commit' | 'readOnly'
|
|
646
|
+
>;
|
|
647
|
+
|
|
648
|
+
/** A toolbox entry whose `icon` may be a React element instead of an SVG string. */
|
|
649
|
+
export type ReactToolboxConfigEntry = Omit<ToolboxConfigEntry, 'icon'> & {
|
|
650
|
+
/**
|
|
651
|
+
* Toolbox icon. Accepts the markup string core consumes directly, or the same
|
|
652
|
+
* React element rendered in the block body — the factory serializes elements
|
|
653
|
+
* to markup once (lazily, cached), so icons are never duplicated as parallel
|
|
654
|
+
* raw SVG strings. Without a DOM (SSR) the element is returned as-is and
|
|
655
|
+
* serialization retries on the next browser-side access.
|
|
656
|
+
*/
|
|
657
|
+
icon?: string | React.ReactElement;
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
/** Toolbox config for React blocks: single entry or several variants. */
|
|
661
|
+
export type ReactToolboxConfig = ReactToolboxConfigEntry | ReactToolboxConfigEntry[];
|
|
662
|
+
|
|
590
663
|
/** Spec for {@link createReactBlock}. */
|
|
591
664
|
export interface CreateReactBlockSpec<
|
|
592
665
|
Data extends BlockToolData = BlockToolData,
|
|
593
666
|
Config = Record<string, unknown>
|
|
594
667
|
> {
|
|
595
668
|
type: string;
|
|
596
|
-
toolbox?:
|
|
669
|
+
toolbox?: ReactToolboxConfig;
|
|
597
670
|
propSchema: PropSchema;
|
|
598
671
|
component: React.ComponentType<ReactBlockRenderProps<Data, Config>>;
|
|
672
|
+
/**
|
|
673
|
+
* Optional read-only renderer: rendered INSTEAD of `component` while the
|
|
674
|
+
* editor is read-only, so blocks need no display-vs-edit ternary. Toggling
|
|
675
|
+
* read-only swaps renderers (ephemeral state does not survive the swap);
|
|
676
|
+
* omit it to keep the single-component in-place toggle.
|
|
677
|
+
*/
|
|
678
|
+
viewComponent?: React.ComponentType<ReactBlockViewProps<Data, Config>>;
|
|
599
679
|
onRendered?: (block: BlockAPI) => void;
|
|
600
680
|
onMoved?: (block: BlockAPI) => void;
|
|
601
681
|
onRemoved?: (block: BlockAPI) => void;
|
|
@@ -644,21 +724,86 @@ export interface ReactInlineToolRenderProps<Config = Record<string, unknown>> {
|
|
|
644
724
|
config: Readonly<Partial<Config>>;
|
|
645
725
|
}
|
|
646
726
|
|
|
727
|
+
/** The spec's mark operations, bound to the live selection via `api.marks`. */
|
|
728
|
+
export interface InlineToolMarkOps<State = void> {
|
|
729
|
+
/** Whether the whole selection currently carries the mark. */
|
|
730
|
+
has(): boolean;
|
|
731
|
+
/** Toggle the mark on the selection; returns true when now applied. */
|
|
732
|
+
toggle(state?: State): boolean;
|
|
733
|
+
/** Apply (or update in place) the mark on the selection. */
|
|
734
|
+
apply(state?: State): HTMLElement[];
|
|
735
|
+
/** Remove the mark from the selection. */
|
|
736
|
+
remove(): HTMLElement[];
|
|
737
|
+
/** Read the mark's current declared values at the selection. */
|
|
738
|
+
read(): MarkSnapshot | null;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Everything a React inline tool's component (and any component nested in it —
|
|
743
|
+
* swatches, popovers) can reach via {@link useInlineTool} without prop-drilling.
|
|
744
|
+
*/
|
|
745
|
+
export interface InlineToolHandle<Config = Record<string, unknown>, State = void> {
|
|
746
|
+
/** Live active state, same value as the component's `active` prop. */
|
|
747
|
+
active: boolean;
|
|
748
|
+
/** The tool's config, same value as the component's `config` prop. */
|
|
749
|
+
config: Readonly<Partial<Config>>;
|
|
750
|
+
/** The editor API handed to the tool's constructor (undefined outside core). */
|
|
751
|
+
api: API | undefined;
|
|
752
|
+
/**
|
|
753
|
+
* Operations bound to the spec's `mark` — null when the spec declares no
|
|
754
|
+
* mark or the editor api provides no `marks` surface.
|
|
755
|
+
*/
|
|
756
|
+
mark: InlineToolMarkOps<State> | null;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Reach the enclosing React inline tool from any component inside its
|
|
761
|
+
* portaled UI: live `active` state, the tool `config`, the editor `api`, and
|
|
762
|
+
* the spec's mark operations bound to the live selection.
|
|
763
|
+
*/
|
|
764
|
+
export declare function useInlineTool<Config = Record<string, unknown>, State = void>(): InlineToolHandle<Config, State>;
|
|
765
|
+
|
|
647
766
|
/** Spec for {@link createReactInlineTool}. */
|
|
648
|
-
export interface CreateReactInlineToolSpec<Config = Record<string, unknown
|
|
767
|
+
export interface CreateReactInlineToolSpec<Config = Record<string, unknown>, State = void> {
|
|
649
768
|
/** Tool type name — the MenuConfig item name (fallback when the tools-map key is unavailable). */
|
|
650
769
|
type: string;
|
|
651
770
|
/** Optional toolbar item title (shown in overflow/search contexts). */
|
|
652
771
|
title?: string;
|
|
772
|
+
/**
|
|
773
|
+
* 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.
|
|
777
|
+
*/
|
|
778
|
+
titleKey?: string;
|
|
653
779
|
/** The component rendered as the tool's toolbar icon/UI. */
|
|
654
780
|
component: React.ComponentType<ReactInlineToolRenderProps<Config>>;
|
|
655
|
-
/**
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
781
|
+
/**
|
|
782
|
+
* Declarative description of the wrapper this tool produces. When present,
|
|
783
|
+
* `surround` (toggle), `checkState` (whole-range coverage) and `sanitize`
|
|
784
|
+
* are derived from it — range-aware splitting, adjacent-run semantics and
|
|
785
|
+
* the trailing-whitespace fix come from the editor's mark engine
|
|
786
|
+
* (`api.marks`) with no DOM plumbing in the tool.
|
|
787
|
+
*/
|
|
788
|
+
mark?: MarkSpec<State>;
|
|
789
|
+
/**
|
|
790
|
+
* Applies/removes the formatting on the LIVE selection's range (captured at
|
|
791
|
+
* activation time). Receives the editor API as its second argument.
|
|
792
|
+
* Takes precedence over the `mark` derivation.
|
|
793
|
+
*/
|
|
794
|
+
surround?: (range: Range, api: API | undefined) => void;
|
|
795
|
+
/**
|
|
796
|
+
* Reports whether the formatting is active for the given selection.
|
|
797
|
+
* Receives the editor API as its second argument.
|
|
798
|
+
* Takes precedence over the `mark` derivation.
|
|
799
|
+
*/
|
|
800
|
+
checkState?: (selection: Selection | null, api: API | undefined) => boolean;
|
|
659
801
|
/** Keyboard shortcut (e.g. `CMD+SHIFT+C`). */
|
|
660
802
|
shortcut?: string;
|
|
661
|
-
/**
|
|
803
|
+
/**
|
|
804
|
+
* Inline sanitizer config declaring the markup this tool produces.
|
|
805
|
+
* Defaults to the config derived from `mark` when one is declared.
|
|
806
|
+
*/
|
|
662
807
|
sanitize?: SanitizerConfig;
|
|
663
808
|
/** Whether the tool stays available in read-only mode. */
|
|
664
809
|
isReadOnlySupported?: boolean;
|
|
@@ -680,13 +825,12 @@ export interface CreateReactInlineToolSpec<Config = Record<string, unknown>> {
|
|
|
680
825
|
* type: 'descriptionColor',
|
|
681
826
|
* title: 'Text color',
|
|
682
827
|
* component: ({ active }) => <FontColorIcon active={active} />,
|
|
683
|
-
*
|
|
684
|
-
* checkState: (selection) => hasColor(selection),
|
|
828
|
+
* mark: { tag: 'span', className: 'hl-description' },
|
|
685
829
|
* });
|
|
686
830
|
* ```
|
|
687
831
|
*/
|
|
688
|
-
export declare function createReactInlineTool<Config = Record<string, unknown
|
|
689
|
-
spec: CreateReactInlineToolSpec<Config>
|
|
832
|
+
export declare function createReactInlineTool<Config = Record<string, unknown>, State = void>(
|
|
833
|
+
spec: CreateReactInlineToolSpec<Config, State>
|
|
690
834
|
): InlineToolConstructable;
|
|
691
835
|
|
|
692
836
|
/**
|
|
@@ -745,3 +889,49 @@ export interface BlockPortalHostProps {
|
|
|
745
889
|
* for advanced setups that manage the registry themselves.
|
|
746
890
|
*/
|
|
747
891
|
export declare function BlockPortalHost(props: BlockPortalHostProps): React.ReactElement;
|
|
892
|
+
|
|
893
|
+
/**
|
|
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.
|
|
899
|
+
*/
|
|
900
|
+
export interface BlokViewProps {
|
|
901
|
+
/** Saved document to display (strict or loose wire shape; nullish tolerated). */
|
|
902
|
+
data: OutputData | LooseOutputData | null | undefined;
|
|
903
|
+
/** View schema from `defineBlokSchema` — display under the composition that produced the document. */
|
|
904
|
+
schema?: BlocksToHtmlOptions['schema'];
|
|
905
|
+
/** Custom per-tool renderers; win over built-ins. */
|
|
906
|
+
renderers?: BlocksToHtmlOptions['renderers'];
|
|
907
|
+
/** Unknown-tool policy (default 'skip'). */
|
|
908
|
+
onUnknownBlock?: BlocksToHtmlOptions['onUnknownBlock'];
|
|
909
|
+
/** Class for the single wrapper div. */
|
|
910
|
+
className?: string;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* 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.
|
|
918
|
+
*
|
|
919
|
+
* @experimental Not frozen until a second framework adapter consumes the
|
|
920
|
+
* underlying view tree.
|
|
921
|
+
*/
|
|
922
|
+
export declare function BlokView(props: BlokViewProps): React.ReactNode;
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Render a saved Blok document to React elements with NO wrapper element —
|
|
926
|
+
* a Fragment of the block elements, for placement inside labels, table
|
|
927
|
+
* cells, and other slots where an extra `<div>` is invalid or unwanted.
|
|
928
|
+
* Synchronous, effect-free (SSR-safe), memoized on the data reference and
|
|
929
|
+
* the individual option values.
|
|
930
|
+
*
|
|
931
|
+
* @experimental Not frozen until a second framework adapter consumes the
|
|
932
|
+
* underlying view tree.
|
|
933
|
+
*/
|
|
934
|
+
export declare function useBlokView(
|
|
935
|
+
data: OutputData | LooseOutputData | null | undefined,
|
|
936
|
+
options?: BlocksToHtmlOptions
|
|
937
|
+
): React.ReactNode;
|