@qkix/better-blocks-react-renderer 0.18.0 → 0.18.2
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 +51 -14
- package/dist/index.d.mts +39 -5
- package/dist/index.d.ts +39 -5
- package/dist/index.js +58 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +58 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
<a href="https://www.npmjs.com/package/@qkix/better-blocks-react-renderer">
|
|
10
10
|
<img alt="npm downloads" src="https://img.shields.io/npm/dm/@qkix/better-blocks-react-renderer.svg" />
|
|
11
11
|
</a>
|
|
12
|
-
<a href="https://github.com/qkix/strapi-
|
|
12
|
+
<a href="https://github.com/qkix/strapi-plugins/blob/main/LICENSE">
|
|
13
13
|
<img alt="license" src="https://img.shields.io/npm/l/@qkix/better-blocks-react-renderer.svg" />
|
|
14
14
|
</a>
|
|
15
|
-
<a href="https://buymeacoffee.com/
|
|
15
|
+
<a href="https://buymeacoffee.com/qkix">
|
|
16
16
|
<img alt="Buy Me a Coffee" src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-support-FFDD00?logo=buymeacoffee&logoColor=black" />
|
|
17
17
|
</a>
|
|
18
18
|
</p>
|
|
@@ -33,16 +33,17 @@
|
|
|
33
33
|
6. [Supported Blocks](#supported-blocks)
|
|
34
34
|
7. [Supported Modifiers](#supported-modifiers)
|
|
35
35
|
8. [Custom Renderers](#custom-renderers)
|
|
36
|
-
9. [
|
|
37
|
-
10. [
|
|
38
|
-
11. [
|
|
39
|
-
12. [
|
|
36
|
+
9. [Registered Block Types](#registered-block-types)
|
|
37
|
+
10. [TypeScript](#typescript)
|
|
38
|
+
11. [Contributing](#contributing)
|
|
39
|
+
12. [Support this project](#support-this-project)
|
|
40
|
+
13. [License](#license)
|
|
40
41
|
|
|
41
42
|
---
|
|
42
43
|
|
|
43
44
|
## Why?
|
|
44
45
|
|
|
45
|
-
The official [`@strapi/blocks-react-renderer`](https://github.com/strapi/blocks-react-renderer) doesn't support the features that the [Better Blocks](https://github.com/qkix/strapi-
|
|
46
|
+
The official [`@strapi/blocks-react-renderer`](https://github.com/strapi/blocks-react-renderer) doesn't support the features that the [Better Blocks](https://github.com/qkix/strapi-plugins) plugin adds to the Strapi editor — color marks, text alignment, to-do lists, tables, media embeds, and more.
|
|
46
47
|
|
|
47
48
|
This package is a **drop-in replacement** that renders all Better Blocks features out of the box — no configuration needed.
|
|
48
49
|
|
|
@@ -333,7 +334,7 @@ The markup is wrapped in a `<figure className="bb-embed align-{alignment}">` (al
|
|
|
333
334
|
|
|
334
335
|
> **Trust boundary.** `embedHtml` is injected with `dangerouslySetInnerHTML`. The plugin sanitizes it at author time — the iframe is rebuilt from an attribute allowlist over an **https-only** `src`, with scripts, event handlers, inline styles and unknown attributes stripped, and `allow` filtered to a safe permission set — so treat your CMS content as trusted. If you accept `embed` blocks from untrusted authors, sanitize on the server before storing, or override the block (below) and render the parsed parts instead of the HTML.
|
|
335
336
|
|
|
336
|
-
Consumers need to allow the embed hosts in their `frame-src` (and `img-src` for thumbnails) CSP directives — see the [plugin README](https://github.com/qkix/strapi-
|
|
337
|
+
Consumers need to allow the embed hosts in their `frame-src` (and `img-src` for thumbnails) CSP directives — see the [plugin README](https://github.com/qkix/strapi-plugins#embed-json-shape-for-frontend-renderers) for the host list.
|
|
337
338
|
|
|
338
339
|
Baseline appearance ships as inline styles plus one small stylesheet (injected only when a default embed renders, since inline styles can't reach into injected markup). Every element carries a stable `bb-embed*` class:
|
|
339
340
|
|
|
@@ -540,7 +541,7 @@ Cell `children` go through the same inline renderer as paragraphs, so marks (`bo
|
|
|
540
541
|
|
|
541
542
|
These three blocks ship with GitHub-flavored defaults out of the box — no stylesheet to import. Each carries a stable `bb-*` class, and the styles are injected as a `<style>` tag only when the block actually appears in the content (and skipped entirely when you override the block). Everything is rethemable through CSS custom properties, so you can restyle without replacing any markup.
|
|
542
543
|
|
|
543
|
-
The same classes and custom properties are used by the [Astro renderer](https://github.com/qkix/strapi-
|
|
544
|
+
The same classes and custom properties are used by the [Astro renderer](https://github.com/qkix/strapi-plugins/tree/main/packages/better-blocks-astro-renderer), so one shared theme covers both.
|
|
544
545
|
|
|
545
546
|
| Block | Default element | Custom properties |
|
|
546
547
|
| ------- | ------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
@@ -849,6 +850,42 @@ Override any text modifier with your own component:
|
|
|
849
850
|
/>
|
|
850
851
|
```
|
|
851
852
|
|
|
853
|
+
## Registered Block Types
|
|
854
|
+
|
|
855
|
+
`blocks` overrides how a **known** block is drawn. `blockPlugins` adds a block
|
|
856
|
+
type this renderer has never heard of — one owned by another package, such as a
|
|
857
|
+
chart.
|
|
858
|
+
|
|
859
|
+
```tsx
|
|
860
|
+
import { BlocksRenderer } from '@qkix/better-blocks-react-renderer';
|
|
861
|
+
import type { BlockPlugin } from '@qkix/better-blocks-react-renderer';
|
|
862
|
+
|
|
863
|
+
const chart: BlockPlugin = {
|
|
864
|
+
type: 'chart',
|
|
865
|
+
// 'void' (attributes only, the default), 'inline' (text), or 'blocks' (nested blocks).
|
|
866
|
+
content: 'void',
|
|
867
|
+
component: ({ node }) => <MyChart spec={node.spec} />,
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
<BlocksRenderer content={content} blockPlugins={[chart]} />;
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
The component receives the whole `node` — this renderer does not know what
|
|
874
|
+
attributes the block has, which is the point — and, when the content model is
|
|
875
|
+
`inline` or `blocks`, its rendered `children`. A registered block works at any
|
|
876
|
+
depth, including inside a callout or a details.
|
|
877
|
+
|
|
878
|
+
A `BlockPlugin` is a core `BlockDefinition` plus `component`, so the same object
|
|
879
|
+
that teaches `validateDocument` and `migrateDocument` about the block also
|
|
880
|
+
teaches this renderer to draw it. See
|
|
881
|
+
[`@qkix/better-blocks-core`](../better-blocks-core#registering-a-block-type).
|
|
882
|
+
|
|
883
|
+
Passing plugins explicitly, rather than registering them into a global, is
|
|
884
|
+
deliberate: this renderer runs on servers handling concurrent requests, where
|
|
885
|
+
mutable module state leaks one page's registrations into another's.
|
|
886
|
+
|
|
887
|
+
A block type nobody registered renders nothing, exactly as before.
|
|
888
|
+
|
|
852
889
|
## TypeScript
|
|
853
890
|
|
|
854
891
|
All types are exported:
|
|
@@ -902,12 +939,12 @@ import type {
|
|
|
902
939
|
## Contributing
|
|
903
940
|
|
|
904
941
|
Contributions are welcome! This package lives in the
|
|
905
|
-
[strapi-plugin-better-blocks](https://github.com/qkix/strapi-
|
|
942
|
+
[strapi-plugin-better-blocks](https://github.com/qkix/strapi-plugins)
|
|
906
943
|
monorepo, next to the Strapi plugin and the Astro renderer. The easiest way to
|
|
907
944
|
get started is with Docker:
|
|
908
945
|
|
|
909
946
|
```bash
|
|
910
|
-
git clone https://github.com/qkix/strapi-
|
|
947
|
+
git clone https://github.com/qkix/strapi-plugins.git
|
|
911
948
|
cd strapi-plugin-better-blocks
|
|
912
949
|
|
|
913
950
|
docker compose up --build
|
|
@@ -948,13 +985,13 @@ pnpm --filter @qkix/better-blocks-react-renderer test # just this one
|
|
|
948
985
|
|
|
949
986
|
## Community & Support
|
|
950
987
|
|
|
951
|
-
- [GitHub Issues](https://github.com/qkix/strapi-
|
|
988
|
+
- [GitHub Issues](https://github.com/qkix/strapi-plugins/issues) — Bug reports and feature requests
|
|
952
989
|
|
|
953
990
|
## Support this project
|
|
954
991
|
|
|
955
992
|
This package is built and maintained in my free time, and it's free for everyone. If it has saved you time on a project, you can help keep it caffeinated and actively developed:
|
|
956
993
|
|
|
957
|
-
<a href="https://buymeacoffee.com/
|
|
994
|
+
<a href="https://buymeacoffee.com/qkix">
|
|
958
995
|
<img alt="Buy Me a Coffee" src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-support-FFDD00?logo=buymeacoffee&logoColor=black&style=for-the-badge" />
|
|
959
996
|
</a>
|
|
960
997
|
|
|
@@ -962,7 +999,7 @@ Every coffee goes toward fixing bugs, reviewing PRs, writing docs, and shipping
|
|
|
962
999
|
|
|
963
1000
|
## Related
|
|
964
1001
|
|
|
965
|
-
- [@qkix/strapi-plugin-better-blocks](https://github.com/qkix/strapi-
|
|
1002
|
+
- [@qkix/strapi-plugin-better-blocks](https://github.com/qkix/strapi-plugins) — Strapi plugin that extends the Blocks editor with colors, tables, to-do lists, media embeds, and more
|
|
966
1003
|
- [@strapi/blocks-react-renderer](https://github.com/strapi/blocks-react-renderer) — Official Strapi renderer (standard blocks only)
|
|
967
1004
|
|
|
968
1005
|
## License
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode, ComponentType, CSSProperties } from 'react';
|
|
2
|
-
import { BlocksContent, TableCellAlign, CalloutVariant, ButtonAlignment, ButtonLink, ButtonFile, ButtonStyle, SocialPlatform, SocialEmbedOembed, SocialEmbedAlignment, AudioFile, AudioPlayer, AudioAlignment, EmbedProvider, AspectRatio, MediaAlignment, VideoProvider, VideoFile, VideoPlayer } from '@qkix/better-blocks-core';
|
|
3
|
-
export { AspectRatio, AudioAlignment, AudioFile, AudioNode, AudioPlayer, AudioPreload, BlockNode, BlocksContent, ButtonAlignment, ButtonElement, ButtonFile, ButtonLink, ButtonStyle, CalloutNode, CalloutVariant, CodeNode, DetailsNode, DiagramNode, EmbedNode, EmbedProvider, HeadingNode, HorizontalLineNode, ImageNode, InlineNode, LinkNode, ListItemNode, ListNode, MathNode, MediaAlignment, MediaEmbedNode, ParagraphNode, QuoteNode, SocialEmbedAlignment, SocialEmbedNode, SocialEmbedOembed, SocialPlatform, TableCellAlign, TableCellNode, TableHeaderCellNode, TableNode, TableRowNode, TextAlign, TextNode, VideoFile, VideoNode, VideoPlayer, VideoProvider } from '@qkix/better-blocks-core';
|
|
2
|
+
import { BlockDefinition, CustomBlockNode, BlocksContent, ExtendedBlocksContent, TableCellAlign, CalloutVariant, ButtonAlignment, ButtonLink, ButtonFile, ButtonStyle, SocialPlatform, SocialEmbedOembed, SocialEmbedAlignment, AudioFile, AudioPlayer, AudioAlignment, EmbedProvider, AspectRatio, MediaAlignment, VideoProvider, VideoFile, VideoPlayer } from '@qkix/better-blocks-core';
|
|
3
|
+
export { AnyBlockNode, AspectRatio, AudioAlignment, AudioFile, AudioNode, AudioPlayer, AudioPreload, BlockDefinition, BlockNode, BlocksContent, ButtonAlignment, ButtonElement, ButtonFile, ButtonLink, ButtonStyle, CalloutNode, CalloutVariant, CodeNode, CustomBlockNode, DetailsNode, DiagramNode, EmbedNode, EmbedProvider, ExtendedBlocksContent, HeadingNode, HorizontalLineNode, ImageNode, InlineNode, LinkNode, ListItemNode, ListNode, MathNode, MediaAlignment, MediaEmbedNode, ParagraphNode, QuoteNode, SocialEmbedAlignment, SocialEmbedNode, SocialEmbedOembed, SocialPlatform, TableCellAlign, TableCellNode, TableHeaderCellNode, TableNode, TableRowNode, TextAlign, TextNode, VideoFile, VideoNode, VideoPlayer, VideoProvider } from '@qkix/better-blocks-core';
|
|
4
4
|
|
|
5
5
|
type ModifierProps = {
|
|
6
6
|
children: ReactNode;
|
|
@@ -166,10 +166,44 @@ type CustomModifiersConfig = Partial<{
|
|
|
166
166
|
fontFamily: ComponentType<FontFamilyModifierProps>;
|
|
167
167
|
fontSize: ComponentType<FontSizeModifierProps>;
|
|
168
168
|
}>;
|
|
169
|
+
/**
|
|
170
|
+
* Props handed to a registered block's component.
|
|
171
|
+
*
|
|
172
|
+
* The whole node is passed rather than spread attributes, because this renderer
|
|
173
|
+
* does not know what attributes the block has — that is the point of it being
|
|
174
|
+
* registered elsewhere.
|
|
175
|
+
*/
|
|
176
|
+
type CustomBlockProps = {
|
|
177
|
+
node: CustomBlockNode;
|
|
178
|
+
/**
|
|
179
|
+
* The rendered children, for a block whose content model is `inline` or
|
|
180
|
+
* `blocks`. Undefined for a `void` block, whose children are only the empty
|
|
181
|
+
* placeholder Slate requires.
|
|
182
|
+
*/
|
|
183
|
+
children?: ReactNode;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* A block type from another package, plus how React draws it.
|
|
187
|
+
*
|
|
188
|
+
* Extends the core {@link BlockDefinition} so one object serves validation,
|
|
189
|
+
* migration and rendering: a package publishes its definition once and adds
|
|
190
|
+
* `component` here.
|
|
191
|
+
*/
|
|
192
|
+
type BlockPlugin = BlockDefinition & {
|
|
193
|
+
component: ComponentType<CustomBlockProps>;
|
|
194
|
+
};
|
|
169
195
|
type BlocksRendererProps = {
|
|
170
|
-
content: BlocksContent;
|
|
196
|
+
content: BlocksContent | ExtendedBlocksContent;
|
|
171
197
|
blocks?: CustomBlocksConfig;
|
|
172
198
|
modifiers?: CustomModifiersConfig;
|
|
199
|
+
/**
|
|
200
|
+
* Block types this renderer does not ship, supplied by another package.
|
|
201
|
+
* Passed explicitly rather than read from a global, so a server rendering
|
|
202
|
+
* concurrent requests cannot leak one page's registrations into another's.
|
|
203
|
+
*
|
|
204
|
+
* An unregistered block type renders nothing, exactly as before.
|
|
205
|
+
*/
|
|
206
|
+
blockPlugins?: readonly BlockPlugin[];
|
|
173
207
|
/**
|
|
174
208
|
* Shiki theme for the default `code` block highlighting. Any bundled Shiki
|
|
175
209
|
* theme name (e.g. `github-dark`, `github-light`, `dracula`, `nord`).
|
|
@@ -184,6 +218,6 @@ type BlocksRendererProps = {
|
|
|
184
218
|
codeCopyButton?: boolean;
|
|
185
219
|
};
|
|
186
220
|
|
|
187
|
-
declare function BlocksRenderer({ content, blocks, modifiers, codeTheme, codeCopyButton, }: BlocksRendererProps): ReactNode;
|
|
221
|
+
declare function BlocksRenderer({ content, blocks, modifiers, blockPlugins, codeTheme, codeCopyButton, }: BlocksRendererProps): ReactNode;
|
|
188
222
|
|
|
189
|
-
export { type BackgroundColorModifierProps, type BlockComponentProps, BlocksRenderer, type BlocksRendererProps, type ColorModifierProps, type CustomBlocksConfig, type CustomModifiersConfig, type FontFamilyModifierProps, type FontSizeModifierProps, type ModifierProps, type TableCellRenderProps };
|
|
223
|
+
export { type BackgroundColorModifierProps, type BlockComponentProps, type BlockPlugin, BlocksRenderer, type BlocksRendererProps, type ColorModifierProps, type CustomBlockProps, type CustomBlocksConfig, type CustomModifiersConfig, type FontFamilyModifierProps, type FontSizeModifierProps, type ModifierProps, type TableCellRenderProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode, ComponentType, CSSProperties } from 'react';
|
|
2
|
-
import { BlocksContent, TableCellAlign, CalloutVariant, ButtonAlignment, ButtonLink, ButtonFile, ButtonStyle, SocialPlatform, SocialEmbedOembed, SocialEmbedAlignment, AudioFile, AudioPlayer, AudioAlignment, EmbedProvider, AspectRatio, MediaAlignment, VideoProvider, VideoFile, VideoPlayer } from '@qkix/better-blocks-core';
|
|
3
|
-
export { AspectRatio, AudioAlignment, AudioFile, AudioNode, AudioPlayer, AudioPreload, BlockNode, BlocksContent, ButtonAlignment, ButtonElement, ButtonFile, ButtonLink, ButtonStyle, CalloutNode, CalloutVariant, CodeNode, DetailsNode, DiagramNode, EmbedNode, EmbedProvider, HeadingNode, HorizontalLineNode, ImageNode, InlineNode, LinkNode, ListItemNode, ListNode, MathNode, MediaAlignment, MediaEmbedNode, ParagraphNode, QuoteNode, SocialEmbedAlignment, SocialEmbedNode, SocialEmbedOembed, SocialPlatform, TableCellAlign, TableCellNode, TableHeaderCellNode, TableNode, TableRowNode, TextAlign, TextNode, VideoFile, VideoNode, VideoPlayer, VideoProvider } from '@qkix/better-blocks-core';
|
|
2
|
+
import { BlockDefinition, CustomBlockNode, BlocksContent, ExtendedBlocksContent, TableCellAlign, CalloutVariant, ButtonAlignment, ButtonLink, ButtonFile, ButtonStyle, SocialPlatform, SocialEmbedOembed, SocialEmbedAlignment, AudioFile, AudioPlayer, AudioAlignment, EmbedProvider, AspectRatio, MediaAlignment, VideoProvider, VideoFile, VideoPlayer } from '@qkix/better-blocks-core';
|
|
3
|
+
export { AnyBlockNode, AspectRatio, AudioAlignment, AudioFile, AudioNode, AudioPlayer, AudioPreload, BlockDefinition, BlockNode, BlocksContent, ButtonAlignment, ButtonElement, ButtonFile, ButtonLink, ButtonStyle, CalloutNode, CalloutVariant, CodeNode, CustomBlockNode, DetailsNode, DiagramNode, EmbedNode, EmbedProvider, ExtendedBlocksContent, HeadingNode, HorizontalLineNode, ImageNode, InlineNode, LinkNode, ListItemNode, ListNode, MathNode, MediaAlignment, MediaEmbedNode, ParagraphNode, QuoteNode, SocialEmbedAlignment, SocialEmbedNode, SocialEmbedOembed, SocialPlatform, TableCellAlign, TableCellNode, TableHeaderCellNode, TableNode, TableRowNode, TextAlign, TextNode, VideoFile, VideoNode, VideoPlayer, VideoProvider } from '@qkix/better-blocks-core';
|
|
4
4
|
|
|
5
5
|
type ModifierProps = {
|
|
6
6
|
children: ReactNode;
|
|
@@ -166,10 +166,44 @@ type CustomModifiersConfig = Partial<{
|
|
|
166
166
|
fontFamily: ComponentType<FontFamilyModifierProps>;
|
|
167
167
|
fontSize: ComponentType<FontSizeModifierProps>;
|
|
168
168
|
}>;
|
|
169
|
+
/**
|
|
170
|
+
* Props handed to a registered block's component.
|
|
171
|
+
*
|
|
172
|
+
* The whole node is passed rather than spread attributes, because this renderer
|
|
173
|
+
* does not know what attributes the block has — that is the point of it being
|
|
174
|
+
* registered elsewhere.
|
|
175
|
+
*/
|
|
176
|
+
type CustomBlockProps = {
|
|
177
|
+
node: CustomBlockNode;
|
|
178
|
+
/**
|
|
179
|
+
* The rendered children, for a block whose content model is `inline` or
|
|
180
|
+
* `blocks`. Undefined for a `void` block, whose children are only the empty
|
|
181
|
+
* placeholder Slate requires.
|
|
182
|
+
*/
|
|
183
|
+
children?: ReactNode;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* A block type from another package, plus how React draws it.
|
|
187
|
+
*
|
|
188
|
+
* Extends the core {@link BlockDefinition} so one object serves validation,
|
|
189
|
+
* migration and rendering: a package publishes its definition once and adds
|
|
190
|
+
* `component` here.
|
|
191
|
+
*/
|
|
192
|
+
type BlockPlugin = BlockDefinition & {
|
|
193
|
+
component: ComponentType<CustomBlockProps>;
|
|
194
|
+
};
|
|
169
195
|
type BlocksRendererProps = {
|
|
170
|
-
content: BlocksContent;
|
|
196
|
+
content: BlocksContent | ExtendedBlocksContent;
|
|
171
197
|
blocks?: CustomBlocksConfig;
|
|
172
198
|
modifiers?: CustomModifiersConfig;
|
|
199
|
+
/**
|
|
200
|
+
* Block types this renderer does not ship, supplied by another package.
|
|
201
|
+
* Passed explicitly rather than read from a global, so a server rendering
|
|
202
|
+
* concurrent requests cannot leak one page's registrations into another's.
|
|
203
|
+
*
|
|
204
|
+
* An unregistered block type renders nothing, exactly as before.
|
|
205
|
+
*/
|
|
206
|
+
blockPlugins?: readonly BlockPlugin[];
|
|
173
207
|
/**
|
|
174
208
|
* Shiki theme for the default `code` block highlighting. Any bundled Shiki
|
|
175
209
|
* theme name (e.g. `github-dark`, `github-light`, `dracula`, `nord`).
|
|
@@ -184,6 +218,6 @@ type BlocksRendererProps = {
|
|
|
184
218
|
codeCopyButton?: boolean;
|
|
185
219
|
};
|
|
186
220
|
|
|
187
|
-
declare function BlocksRenderer({ content, blocks, modifiers, codeTheme, codeCopyButton, }: BlocksRendererProps): ReactNode;
|
|
221
|
+
declare function BlocksRenderer({ content, blocks, modifiers, blockPlugins, codeTheme, codeCopyButton, }: BlocksRendererProps): ReactNode;
|
|
188
222
|
|
|
189
|
-
export { type BackgroundColorModifierProps, type BlockComponentProps, BlocksRenderer, type BlocksRendererProps, type ColorModifierProps, type CustomBlocksConfig, type CustomModifiersConfig, type FontFamilyModifierProps, type FontSizeModifierProps, type ModifierProps, type TableCellRenderProps };
|
|
223
|
+
export { type BackgroundColorModifierProps, type BlockComponentProps, type BlockPlugin, BlocksRenderer, type BlocksRendererProps, type ColorModifierProps, type CustomBlockProps, type CustomBlocksConfig, type CustomModifiersConfig, type FontFamilyModifierProps, type FontSizeModifierProps, type ModifierProps, type TableCellRenderProps };
|
package/dist/index.js
CHANGED
|
@@ -120,17 +120,17 @@ function CodeBlock({
|
|
|
120
120
|
}) {
|
|
121
121
|
const [html, setHtml] = (0, import_react.useState)(null);
|
|
122
122
|
(0, import_react.useEffect)(() => {
|
|
123
|
-
let
|
|
123
|
+
let canceled = false;
|
|
124
124
|
setHtml(null);
|
|
125
125
|
if (!plainText) return;
|
|
126
126
|
const lang = (0, import_better_blocks_core2.normalizeCodeLang)(language);
|
|
127
127
|
loadHighlighter(theme, lang).then((highlighter) => highlighter.codeToHtml(plainText, { lang, theme })).then((highlighted) => {
|
|
128
|
-
if (!
|
|
128
|
+
if (!canceled) setHtml(highlighted);
|
|
129
129
|
}).catch(() => {
|
|
130
|
-
if (!
|
|
130
|
+
if (!canceled) setHtml(null);
|
|
131
131
|
});
|
|
132
132
|
return () => {
|
|
133
|
-
|
|
133
|
+
canceled = true;
|
|
134
134
|
};
|
|
135
135
|
}, [plainText, language, theme]);
|
|
136
136
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bb-code", children: [
|
|
@@ -157,16 +157,16 @@ var renderCount = 0;
|
|
|
157
157
|
function MermaidDiagram({ value }) {
|
|
158
158
|
const [svg, setSvg] = (0, import_react2.useState)(null);
|
|
159
159
|
(0, import_react2.useEffect)(() => {
|
|
160
|
-
let
|
|
160
|
+
let canceled = false;
|
|
161
161
|
setSvg(null);
|
|
162
162
|
if (!value) return;
|
|
163
163
|
loadMermaid().then((mermaid) => mermaid.render(`bb-mermaid-${renderCount++}`, value)).then(({ svg: svg2 }) => {
|
|
164
|
-
if (!
|
|
164
|
+
if (!canceled) setSvg(svg2);
|
|
165
165
|
}).catch(() => {
|
|
166
|
-
if (!
|
|
166
|
+
if (!canceled) setSvg(null);
|
|
167
167
|
});
|
|
168
168
|
return () => {
|
|
169
|
-
|
|
169
|
+
canceled = true;
|
|
170
170
|
};
|
|
171
171
|
}, [value]);
|
|
172
172
|
if (svg) {
|
|
@@ -348,14 +348,14 @@ function SocialEmbed({
|
|
|
348
348
|
});
|
|
349
349
|
const widget = WIDGET_SCRIPTS[platform];
|
|
350
350
|
if (!widget) return;
|
|
351
|
-
let
|
|
351
|
+
let canceled = false;
|
|
352
352
|
void loadWidgetScript(widget.src, platform).then(() => {
|
|
353
353
|
const container = containerRef.current;
|
|
354
|
-
if (
|
|
354
|
+
if (canceled || !container) return;
|
|
355
355
|
if (!widget.process(container)) reinjectWidgetScript(widget.src, platform);
|
|
356
356
|
});
|
|
357
357
|
return () => {
|
|
358
|
-
|
|
358
|
+
canceled = true;
|
|
359
359
|
};
|
|
360
360
|
}, [platform, html]);
|
|
361
361
|
const providerName = (_a = oembed == null ? void 0 : oembed.providerName) != null ? _a : PLATFORM_LABELS[platform];
|
|
@@ -415,12 +415,12 @@ function Video({
|
|
|
415
415
|
setMuxDefined(true);
|
|
416
416
|
return;
|
|
417
417
|
}
|
|
418
|
-
let
|
|
418
|
+
let canceled = false;
|
|
419
419
|
void window.customElements.whenDefined("mux-player").then(() => {
|
|
420
|
-
if (!
|
|
420
|
+
if (!canceled) setMuxDefined(true);
|
|
421
421
|
});
|
|
422
422
|
return () => {
|
|
423
|
-
|
|
423
|
+
canceled = true;
|
|
424
424
|
};
|
|
425
425
|
}, [wantsMux]);
|
|
426
426
|
const isHls = HLS_MANIFEST.test(url);
|
|
@@ -642,44 +642,63 @@ function renderTable(block, key, blocks, modifiers) {
|
|
|
642
642
|
function isHeaderRow(row) {
|
|
643
643
|
return !!row && row.children.length > 0 && row.children.every((c) => c.type === "table-header-cell");
|
|
644
644
|
}
|
|
645
|
+
function renderCustomBlock(plugin, block, key, blocks, modifiers, options) {
|
|
646
|
+
const { component: Component, content = "void" } = plugin;
|
|
647
|
+
const children = Array.isArray(block.children) ? block.children : [];
|
|
648
|
+
if (content === "blocks") {
|
|
649
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Component, { node: block, children: children.map(
|
|
650
|
+
(child, index) => renderBlock(child, index, blocks, modifiers, options)
|
|
651
|
+
) }, key);
|
|
652
|
+
}
|
|
653
|
+
if (content === "inline") {
|
|
654
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Component, { node: block, children: renderInlineContent(children, blocks, modifiers) }, key);
|
|
655
|
+
}
|
|
656
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Component, { node: block }, key);
|
|
657
|
+
}
|
|
645
658
|
function renderBlock(block, key, blocks, modifiers, options) {
|
|
646
|
-
|
|
659
|
+
var _a;
|
|
660
|
+
const plugin = (_a = options == null ? void 0 : options.plugins) == null ? void 0 : _a.get(block.type);
|
|
661
|
+
if (plugin) {
|
|
662
|
+
return renderCustomBlock(plugin, block, key, blocks, modifiers, options);
|
|
663
|
+
}
|
|
664
|
+
const builtIn = block;
|
|
665
|
+
switch (builtIn.type) {
|
|
647
666
|
case "paragraph":
|
|
648
|
-
return renderParagraph(
|
|
667
|
+
return renderParagraph(builtIn, key, blocks, modifiers);
|
|
649
668
|
case "heading":
|
|
650
|
-
return renderHeading(
|
|
669
|
+
return renderHeading(builtIn, key, blocks, modifiers);
|
|
651
670
|
case "list":
|
|
652
|
-
return renderList(
|
|
671
|
+
return renderList(builtIn, key, blocks, modifiers);
|
|
653
672
|
case "quote":
|
|
654
|
-
return renderQuote(
|
|
673
|
+
return renderQuote(builtIn, key, blocks, modifiers);
|
|
655
674
|
case "code":
|
|
656
|
-
return renderCode(
|
|
675
|
+
return renderCode(builtIn, key, blocks, options);
|
|
657
676
|
case "image":
|
|
658
|
-
return renderImage(
|
|
677
|
+
return renderImage(builtIn, key, blocks);
|
|
659
678
|
case "horizontal-line":
|
|
660
|
-
return renderHorizontalLine(
|
|
679
|
+
return renderHorizontalLine(builtIn, key, blocks);
|
|
661
680
|
case "table":
|
|
662
|
-
return renderTable(
|
|
681
|
+
return renderTable(builtIn, key, blocks, modifiers);
|
|
663
682
|
case "media-embed":
|
|
664
|
-
return renderMediaEmbed(
|
|
683
|
+
return renderMediaEmbed(builtIn, key, blocks);
|
|
665
684
|
case "embed":
|
|
666
|
-
return renderEmbed(
|
|
685
|
+
return renderEmbed(builtIn, key, blocks);
|
|
667
686
|
case "video":
|
|
668
|
-
return renderVideo(
|
|
687
|
+
return renderVideo(builtIn, key, blocks);
|
|
669
688
|
case "math":
|
|
670
|
-
return renderMath(
|
|
689
|
+
return renderMath(builtIn, key, blocks);
|
|
671
690
|
case "diagram":
|
|
672
|
-
return renderDiagram(
|
|
691
|
+
return renderDiagram(builtIn, key, blocks);
|
|
673
692
|
case "callout":
|
|
674
|
-
return renderCallout(
|
|
693
|
+
return renderCallout(builtIn, key, blocks, modifiers, options);
|
|
675
694
|
case "details":
|
|
676
|
-
return renderDetails(
|
|
695
|
+
return renderDetails(builtIn, key, blocks, modifiers, options);
|
|
677
696
|
case "button":
|
|
678
|
-
return renderButton(
|
|
697
|
+
return renderButton(builtIn, key, blocks);
|
|
679
698
|
case "social-embed":
|
|
680
|
-
return renderSocialEmbed(
|
|
699
|
+
return renderSocialEmbed(builtIn, key, blocks);
|
|
681
700
|
case "audio":
|
|
682
|
-
return renderAudio(
|
|
701
|
+
return renderAudio(builtIn, key, blocks);
|
|
683
702
|
default:
|
|
684
703
|
return null;
|
|
685
704
|
}
|
|
@@ -1260,6 +1279,7 @@ function BlocksRenderer({
|
|
|
1260
1279
|
content,
|
|
1261
1280
|
blocks,
|
|
1262
1281
|
modifiers,
|
|
1282
|
+
blockPlugins,
|
|
1263
1283
|
codeTheme = DEFAULT_CODE_THEME,
|
|
1264
1284
|
codeCopyButton = false
|
|
1265
1285
|
}) {
|
|
@@ -1271,7 +1291,11 @@ function BlocksRenderer({
|
|
|
1271
1291
|
const needsTableCss = !(blocks == null ? void 0 : blocks.table) && contentHasBlock(content, "table");
|
|
1272
1292
|
const needsQuoteCss = !(blocks == null ? void 0 : blocks.quote) && contentHasBlock(content, "quote");
|
|
1273
1293
|
const needsCodeCss = !(blocks == null ? void 0 : blocks.code) && contentHasBlock(content, "code");
|
|
1274
|
-
const options = {
|
|
1294
|
+
const options = {
|
|
1295
|
+
codeTheme,
|
|
1296
|
+
codeCopyButton,
|
|
1297
|
+
plugins: (blockPlugins == null ? void 0 : blockPlugins.length) ? new Map(blockPlugins.map((plugin) => [plugin.type, plugin])) : void 0
|
|
1298
|
+
};
|
|
1275
1299
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
1276
1300
|
needsButtonCss && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: BUTTON_HOVER_CSS }),
|
|
1277
1301
|
needsEmbedCss && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: EMBED_FRAME_CSS }),
|