@octanejs/portabletext 0.1.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/LICENSE +21 -0
- package/README.md +26 -0
- package/package.json +55 -0
- package/src/components/defaults.ts +71 -0
- package/src/components/list.ts +27 -0
- package/src/components/marks.ts +42 -0
- package/src/components/merge.ts +43 -0
- package/src/components/unknown.ts +43 -0
- package/src/index.ts +7 -0
- package/src/portable-text.ts +200 -0
- package/src/types.ts +329 -0
- package/src/warnings.ts +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sanity.io <hello@sanity.io>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @octanejs/portabletext
|
|
2
|
+
|
|
3
|
+
Octane renderer for [Portable Text](https://www.portabletext.org/), compatible with the
|
|
4
|
+
runtime API of `@portabletext/react@8.0.1`.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npm install @octanejs/portabletext
|
|
8
|
+
pnpm add @octanejs/portabletext
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```tsrx
|
|
12
|
+
import {PortableText} from '@octanejs/portabletext'
|
|
13
|
+
|
|
14
|
+
export function Article(props: {body: PortableTextBlock[]}) @{
|
|
15
|
+
<article><PortableText value={props.body} /></article>
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The package reuses `@portabletext/toolkit` and `@portabletext/types` unchanged. It
|
|
20
|
+
supports default block, list, mark and hard-break rendering; custom component maps;
|
|
21
|
+
missing-component handlers; list nesting modes; `toPlainText`; client rendering; and SSR.
|
|
22
|
+
|
|
23
|
+
Component callbacks return `OctaneNode` and refs/events follow Octane semantics. The
|
|
24
|
+
upstream `PortableTextReactComponents` and `ReactPortableTextList` type names remain as
|
|
25
|
+
migration aliases; prefer `PortableTextOctaneComponents` and `OctanePortableTextList` in
|
|
26
|
+
new code.
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octanejs/portabletext",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22.22.2"
|
|
8
|
+
},
|
|
9
|
+
"description": "Render Portable Text with Octane using the @portabletext/react API.",
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Dominic Gannaway",
|
|
12
|
+
"email": "dg@domgan.com"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/octanejs/octane.git",
|
|
20
|
+
"directory": "packages/portabletext"
|
|
21
|
+
},
|
|
22
|
+
"main": "src/index.ts",
|
|
23
|
+
"module": "src/index.ts",
|
|
24
|
+
"types": "src/index.ts",
|
|
25
|
+
"files": [
|
|
26
|
+
"src",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"exports": {
|
|
32
|
+
".": "./src/index.ts",
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@portabletext/toolkit": "6.0.0",
|
|
37
|
+
"@portabletext/types": "4.0.2"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"octane": "0.1.50"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@portabletext/react": "8.0.1",
|
|
44
|
+
"@tsrx/react": "^0.2.61",
|
|
45
|
+
"esbuild": "^0.28.1",
|
|
46
|
+
"react": "^19.2.7",
|
|
47
|
+
"react-dom": "^19.2.7",
|
|
48
|
+
"vitest": "^4.1.10",
|
|
49
|
+
"octane": "0.1.50"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"test": "vitest run --root ../.. --project portabletext --project portabletext-ssr",
|
|
53
|
+
"typecheck": "tsgo --noEmit -p tsconfig.json"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { PortableTextBlock } from '@portabletext/types';
|
|
2
|
+
import { createElement } from 'octane';
|
|
3
|
+
import type {
|
|
4
|
+
DefaultPortableTextBlockStyle,
|
|
5
|
+
PortableTextBlockComponent,
|
|
6
|
+
PortableTextComponentProps,
|
|
7
|
+
PortableTextOctaneComponents,
|
|
8
|
+
} from '../types';
|
|
9
|
+
import { DefaultListItem, defaultLists } from './list';
|
|
10
|
+
import { defaultMarks } from './marks';
|
|
11
|
+
import {
|
|
12
|
+
DefaultUnknownBlockStyle,
|
|
13
|
+
DefaultUnknownList,
|
|
14
|
+
DefaultUnknownListItem,
|
|
15
|
+
DefaultUnknownMark,
|
|
16
|
+
DefaultUnknownType,
|
|
17
|
+
} from './unknown';
|
|
18
|
+
|
|
19
|
+
export function DefaultHardBreak() {
|
|
20
|
+
return createElement('br', {});
|
|
21
|
+
}
|
|
22
|
+
function DefaultNormal({ children }: PortableTextComponentProps<PortableTextBlock>) {
|
|
23
|
+
return createElement('p', { children });
|
|
24
|
+
}
|
|
25
|
+
function DefaultBlockquote({ children }: PortableTextComponentProps<PortableTextBlock>) {
|
|
26
|
+
return createElement('blockquote', { children });
|
|
27
|
+
}
|
|
28
|
+
function DefaultH1({ children }: PortableTextComponentProps<PortableTextBlock>) {
|
|
29
|
+
return createElement('h1', { children });
|
|
30
|
+
}
|
|
31
|
+
function DefaultH2({ children }: PortableTextComponentProps<PortableTextBlock>) {
|
|
32
|
+
return createElement('h2', { children });
|
|
33
|
+
}
|
|
34
|
+
function DefaultH3({ children }: PortableTextComponentProps<PortableTextBlock>) {
|
|
35
|
+
return createElement('h3', { children });
|
|
36
|
+
}
|
|
37
|
+
function DefaultH4({ children }: PortableTextComponentProps<PortableTextBlock>) {
|
|
38
|
+
return createElement('h4', { children });
|
|
39
|
+
}
|
|
40
|
+
function DefaultH5({ children }: PortableTextComponentProps<PortableTextBlock>) {
|
|
41
|
+
return createElement('h5', { children });
|
|
42
|
+
}
|
|
43
|
+
function DefaultH6({ children }: PortableTextComponentProps<PortableTextBlock>) {
|
|
44
|
+
return createElement('h6', { children });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const defaultBlockStyles: Record<DefaultPortableTextBlockStyle, PortableTextBlockComponent> =
|
|
48
|
+
{
|
|
49
|
+
normal: DefaultNormal,
|
|
50
|
+
blockquote: DefaultBlockquote,
|
|
51
|
+
h1: DefaultH1,
|
|
52
|
+
h2: DefaultH2,
|
|
53
|
+
h3: DefaultH3,
|
|
54
|
+
h4: DefaultH4,
|
|
55
|
+
h5: DefaultH5,
|
|
56
|
+
h6: DefaultH6,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const defaultComponents: PortableTextOctaneComponents = {
|
|
60
|
+
types: {},
|
|
61
|
+
block: defaultBlockStyles,
|
|
62
|
+
marks: defaultMarks,
|
|
63
|
+
list: defaultLists,
|
|
64
|
+
listItem: DefaultListItem,
|
|
65
|
+
hardBreak: DefaultHardBreak,
|
|
66
|
+
unknownType: DefaultUnknownType,
|
|
67
|
+
unknownMark: DefaultUnknownMark,
|
|
68
|
+
unknownList: DefaultUnknownList,
|
|
69
|
+
unknownListItem: DefaultUnknownListItem,
|
|
70
|
+
unknownBlockStyle: DefaultUnknownBlockStyle,
|
|
71
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PortableTextListItemBlock } from '@portabletext/types';
|
|
2
|
+
import { createElement } from 'octane';
|
|
3
|
+
import type {
|
|
4
|
+
DefaultPortableTextListItem,
|
|
5
|
+
OctanePortableTextList,
|
|
6
|
+
PortableTextComponentProps,
|
|
7
|
+
PortableTextListComponent,
|
|
8
|
+
} from '../types';
|
|
9
|
+
|
|
10
|
+
function DefaultNumberList({ children }: PortableTextComponentProps<OctanePortableTextList>) {
|
|
11
|
+
return createElement('ol', { children });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function DefaultBulletList({ children }: PortableTextComponentProps<OctanePortableTextList>) {
|
|
15
|
+
return createElement('ul', { children });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function DefaultListItem({
|
|
19
|
+
children,
|
|
20
|
+
}: PortableTextComponentProps<PortableTextListItemBlock>) {
|
|
21
|
+
return createElement('li', { children });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const defaultLists: Record<DefaultPortableTextListItem, PortableTextListComponent> = {
|
|
25
|
+
number: DefaultNumberList,
|
|
26
|
+
bullet: DefaultBulletList,
|
|
27
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { TypedObject } from '@portabletext/types';
|
|
2
|
+
import { createElement } from 'octane';
|
|
3
|
+
import type {
|
|
4
|
+
DefaultPortableTextMark,
|
|
5
|
+
PortableTextMarkComponent,
|
|
6
|
+
PortableTextMarkComponentProps,
|
|
7
|
+
} from '../types';
|
|
8
|
+
|
|
9
|
+
interface DefaultLinkMark extends TypedObject {
|
|
10
|
+
_type: 'link';
|
|
11
|
+
href: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const underlineStyle = { textDecoration: 'underline' };
|
|
15
|
+
|
|
16
|
+
function DefaultEm({ children }: PortableTextMarkComponentProps) {
|
|
17
|
+
return createElement('em', { children });
|
|
18
|
+
}
|
|
19
|
+
function DefaultStrong({ children }: PortableTextMarkComponentProps) {
|
|
20
|
+
return createElement('strong', { children });
|
|
21
|
+
}
|
|
22
|
+
function DefaultCode({ children }: PortableTextMarkComponentProps) {
|
|
23
|
+
return createElement('code', { children });
|
|
24
|
+
}
|
|
25
|
+
function DefaultUnderline({ children }: PortableTextMarkComponentProps) {
|
|
26
|
+
return createElement('span', { style: underlineStyle, children });
|
|
27
|
+
}
|
|
28
|
+
function DefaultStrikeThrough({ children }: PortableTextMarkComponentProps) {
|
|
29
|
+
return createElement('del', { children });
|
|
30
|
+
}
|
|
31
|
+
function DefaultLink({ children, value }: PortableTextMarkComponentProps<DefaultLinkMark>) {
|
|
32
|
+
return createElement('a', { href: value?.href, children });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const defaultMarks: Record<DefaultPortableTextMark, PortableTextMarkComponent> = {
|
|
36
|
+
em: DefaultEm,
|
|
37
|
+
strong: DefaultStrong,
|
|
38
|
+
code: DefaultCode,
|
|
39
|
+
underline: DefaultUnderline,
|
|
40
|
+
'strike-through': DefaultStrikeThrough,
|
|
41
|
+
link: DefaultLink,
|
|
42
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { PortableTextComponents, PortableTextOctaneComponents } from '../types';
|
|
2
|
+
|
|
3
|
+
export function mergeComponents(
|
|
4
|
+
parent: PortableTextOctaneComponents,
|
|
5
|
+
overrides: PortableTextComponents,
|
|
6
|
+
): PortableTextOctaneComponents {
|
|
7
|
+
const {
|
|
8
|
+
block: _block,
|
|
9
|
+
list: _list,
|
|
10
|
+
listItem: _listItem,
|
|
11
|
+
marks: _marks,
|
|
12
|
+
types: _types,
|
|
13
|
+
...rest
|
|
14
|
+
} = overrides;
|
|
15
|
+
return {
|
|
16
|
+
...parent,
|
|
17
|
+
block: mergeDeeply(parent, overrides, 'block') as PortableTextOctaneComponents['block'],
|
|
18
|
+
list: mergeDeeply(parent, overrides, 'list') as PortableTextOctaneComponents['list'],
|
|
19
|
+
listItem: mergeDeeply(
|
|
20
|
+
parent,
|
|
21
|
+
overrides,
|
|
22
|
+
'listItem',
|
|
23
|
+
) as PortableTextOctaneComponents['listItem'],
|
|
24
|
+
marks: mergeDeeply(parent, overrides, 'marks') as PortableTextOctaneComponents['marks'],
|
|
25
|
+
types: mergeDeeply(parent, overrides, 'types') as PortableTextOctaneComponents['types'],
|
|
26
|
+
...rest,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function mergeDeeply(
|
|
31
|
+
parent: PortableTextOctaneComponents,
|
|
32
|
+
overrides: PortableTextComponents,
|
|
33
|
+
key: 'block' | 'list' | 'listItem' | 'marks' | 'types',
|
|
34
|
+
): PortableTextOctaneComponents[typeof key] {
|
|
35
|
+
const override = overrides[key];
|
|
36
|
+
const parentValue = parent[key];
|
|
37
|
+
if (typeof override === 'function') return override;
|
|
38
|
+
if (override && typeof parentValue === 'function') return override;
|
|
39
|
+
if (override) {
|
|
40
|
+
return { ...parentValue, ...override } as PortableTextOctaneComponents[typeof key];
|
|
41
|
+
}
|
|
42
|
+
return parentValue;
|
|
43
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { PortableTextBlock, PortableTextListItemBlock } from '@portabletext/types';
|
|
2
|
+
import { createElement } from 'octane';
|
|
3
|
+
import type {
|
|
4
|
+
OctanePortableTextList,
|
|
5
|
+
PortableTextComponentProps,
|
|
6
|
+
PortableTextMarkComponentProps,
|
|
7
|
+
UnknownNodeType,
|
|
8
|
+
} from '../types';
|
|
9
|
+
import { unknownTypeWarning } from '../warnings';
|
|
10
|
+
|
|
11
|
+
const hidden = { display: 'none' };
|
|
12
|
+
|
|
13
|
+
export function DefaultUnknownType({
|
|
14
|
+
value,
|
|
15
|
+
isInline,
|
|
16
|
+
}: PortableTextComponentProps<UnknownNodeType>) {
|
|
17
|
+
const warning = unknownTypeWarning(value._type);
|
|
18
|
+
return isInline
|
|
19
|
+
? createElement('span', { style: hidden, children: warning })
|
|
20
|
+
: createElement('div', { style: hidden, children: warning });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function DefaultUnknownMark({ markType, children }: PortableTextMarkComponentProps) {
|
|
24
|
+
return createElement('span', { className: `unknown__pt__mark__${markType}`, children });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function DefaultUnknownBlockStyle({
|
|
28
|
+
children,
|
|
29
|
+
}: PortableTextComponentProps<PortableTextBlock>) {
|
|
30
|
+
return createElement('p', { children });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function DefaultUnknownList({
|
|
34
|
+
children,
|
|
35
|
+
}: PortableTextComponentProps<OctanePortableTextList>) {
|
|
36
|
+
return createElement('ul', { children });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function DefaultUnknownListItem({
|
|
40
|
+
children,
|
|
41
|
+
}: PortableTextComponentProps<PortableTextListItemBlock>) {
|
|
42
|
+
return createElement('li', { children });
|
|
43
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { defaultComponents } from './components/defaults';
|
|
2
|
+
export { mergeComponents } from './components/merge';
|
|
3
|
+
export { PortableText } from './portable-text';
|
|
4
|
+
export * from './types';
|
|
5
|
+
export type { ToolkitListNestMode as ListNestMode } from '@portabletext/toolkit';
|
|
6
|
+
export { toPlainText } from '@portabletext/toolkit';
|
|
7
|
+
export type { PortableTextBlock } from '@portabletext/types';
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type { ToolkitNestedPortableTextSpan, ToolkitTextNode } from '@portabletext/toolkit';
|
|
2
|
+
import {
|
|
3
|
+
LIST_NEST_MODE_HTML,
|
|
4
|
+
buildMarksTree,
|
|
5
|
+
isPortableTextBlock,
|
|
6
|
+
isPortableTextListItemBlock,
|
|
7
|
+
isPortableTextToolkitList,
|
|
8
|
+
isPortableTextToolkitSpan,
|
|
9
|
+
isPortableTextToolkitTextNode,
|
|
10
|
+
nestLists,
|
|
11
|
+
spanToPlainText,
|
|
12
|
+
} from '@portabletext/toolkit';
|
|
13
|
+
import type {
|
|
14
|
+
PortableTextBlock,
|
|
15
|
+
PortableTextListItemBlock,
|
|
16
|
+
TypedObject,
|
|
17
|
+
} from '@portabletext/types';
|
|
18
|
+
import { createElement } from 'octane';
|
|
19
|
+
import type { OctaneNode } from 'octane';
|
|
20
|
+
import { defaultComponents } from './components/defaults';
|
|
21
|
+
import { mergeComponents } from './components/merge';
|
|
22
|
+
import type {
|
|
23
|
+
MissingComponentHandler,
|
|
24
|
+
NodeRenderer,
|
|
25
|
+
OctanePortableTextList,
|
|
26
|
+
PortableTextOctaneComponents,
|
|
27
|
+
PortableTextProps,
|
|
28
|
+
Serializable,
|
|
29
|
+
SerializedBlock,
|
|
30
|
+
} from './types';
|
|
31
|
+
import {
|
|
32
|
+
printWarning,
|
|
33
|
+
unknownBlockStyleWarning,
|
|
34
|
+
unknownListItemStyleWarning,
|
|
35
|
+
unknownListStyleWarning,
|
|
36
|
+
unknownMarkWarning,
|
|
37
|
+
unknownTypeWarning,
|
|
38
|
+
} from './warnings';
|
|
39
|
+
|
|
40
|
+
export function PortableText<B extends TypedObject = PortableTextBlock>({
|
|
41
|
+
value: input,
|
|
42
|
+
components: componentOverrides,
|
|
43
|
+
listNestingMode,
|
|
44
|
+
onMissingComponent: missingComponentHandler = printWarning,
|
|
45
|
+
}: PortableTextProps<B>): OctaneNode {
|
|
46
|
+
const handleMissingComponent = missingComponentHandler || noop;
|
|
47
|
+
const blocks = Array.isArray(input) ? input : input == null ? [] : [input];
|
|
48
|
+
const nested = nestLists(blocks, listNestingMode || LIST_NEST_MODE_HTML);
|
|
49
|
+
const components = componentOverrides
|
|
50
|
+
? mergeComponents(defaultComponents, componentOverrides)
|
|
51
|
+
: defaultComponents;
|
|
52
|
+
const renderNode = getNodeRenderer(components, handleMissingComponent);
|
|
53
|
+
return nested.map((node, index) => renderNode({ node, index, isInline: false, renderNode }));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getNodeRenderer(
|
|
57
|
+
components: PortableTextOctaneComponents,
|
|
58
|
+
handleMissingComponent: MissingComponentHandler,
|
|
59
|
+
): NodeRenderer {
|
|
60
|
+
function renderNode<N extends TypedObject>(options: Serializable<N>): OctaneNode {
|
|
61
|
+
const { node, index, isInline } = options;
|
|
62
|
+
const key = node._key || `node-${index}`;
|
|
63
|
+
if (isPortableTextToolkitList(node)) return renderList(node, index, key);
|
|
64
|
+
if (isPortableTextListItemBlock(node)) return renderListItem(node, index, key);
|
|
65
|
+
if (isPortableTextToolkitSpan(node)) return renderSpan(node, index, key);
|
|
66
|
+
if (node._type in components.types) return renderCustomBlock(node, index, key, isInline);
|
|
67
|
+
if (isPortableTextBlock(node)) return renderBlock(node, index, key, isInline);
|
|
68
|
+
if (isPortableTextToolkitTextNode(node)) return renderText(node, key);
|
|
69
|
+
return renderUnknownType(node, index, key, isInline);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function renderListItem(node: PortableTextListItemBlock, index: number, key: string) {
|
|
73
|
+
const tree = serializeBlock({ node, index, isInline: false, renderNode });
|
|
74
|
+
const configured = components.listItem;
|
|
75
|
+
const handler = typeof configured === 'function' ? configured : configured[node.listItem];
|
|
76
|
+
const ListItem = handler || components.unknownListItem;
|
|
77
|
+
if (ListItem === components.unknownListItem) {
|
|
78
|
+
const style = node.listItem || 'bullet';
|
|
79
|
+
handleMissingComponent(unknownListItemStyleWarning(style), {
|
|
80
|
+
type: style,
|
|
81
|
+
nodeType: 'listItemStyle',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
let children = tree.children;
|
|
85
|
+
if (node.style && node.style !== 'normal') {
|
|
86
|
+
const { listItem: _listItem, ...blockNode } = node;
|
|
87
|
+
children = renderNode({ node: blockNode, index, isInline: false, renderNode });
|
|
88
|
+
}
|
|
89
|
+
return createElement(ListItem, {
|
|
90
|
+
key,
|
|
91
|
+
value: node,
|
|
92
|
+
index,
|
|
93
|
+
isInline: false,
|
|
94
|
+
renderNode,
|
|
95
|
+
children,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function renderList(node: OctanePortableTextList, index: number, key: string) {
|
|
100
|
+
const children = node.children.map((child, childIndex) =>
|
|
101
|
+
renderNode({
|
|
102
|
+
node: child._key ? child : { ...child, _key: `li-${index}-${childIndex}` },
|
|
103
|
+
index: childIndex,
|
|
104
|
+
isInline: false,
|
|
105
|
+
renderNode,
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
108
|
+
const configured = components.list;
|
|
109
|
+
const handler = typeof configured === 'function' ? configured : configured[node.listItem];
|
|
110
|
+
const List = handler || components.unknownList;
|
|
111
|
+
if (List === components.unknownList) {
|
|
112
|
+
const style = node.listItem || 'bullet';
|
|
113
|
+
handleMissingComponent(unknownListStyleWarning(style), {
|
|
114
|
+
nodeType: 'listStyle',
|
|
115
|
+
type: style,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return createElement(List, {
|
|
119
|
+
key,
|
|
120
|
+
value: node,
|
|
121
|
+
index,
|
|
122
|
+
isInline: false,
|
|
123
|
+
renderNode,
|
|
124
|
+
children,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function renderSpan(node: ToolkitNestedPortableTextSpan, _index: number, key: string) {
|
|
129
|
+
const { markDef, markType, markKey } = node;
|
|
130
|
+
const Span = components.marks[markType] || components.unknownMark;
|
|
131
|
+
const children = node.children.map((child, childIndex) =>
|
|
132
|
+
renderNode({ node: child, index: childIndex, isInline: true, renderNode }),
|
|
133
|
+
);
|
|
134
|
+
if (Span === components.unknownMark) {
|
|
135
|
+
handleMissingComponent(unknownMarkWarning(markType), { nodeType: 'mark', type: markType });
|
|
136
|
+
}
|
|
137
|
+
return createElement(Span, {
|
|
138
|
+
key,
|
|
139
|
+
text: spanToPlainText(node),
|
|
140
|
+
value: markDef,
|
|
141
|
+
markType,
|
|
142
|
+
markKey,
|
|
143
|
+
renderNode,
|
|
144
|
+
children,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function renderBlock(node: PortableTextBlock, index: number, key: string, isInline: boolean) {
|
|
149
|
+
const { _key, ...serialized } = serializeBlock({ node, index, isInline, renderNode });
|
|
150
|
+
const style = serialized.node.style || 'normal';
|
|
151
|
+
const configured = components.block;
|
|
152
|
+
const handler = typeof configured === 'function' ? configured : configured[style];
|
|
153
|
+
const Block = handler || components.unknownBlockStyle;
|
|
154
|
+
if (Block === components.unknownBlockStyle) {
|
|
155
|
+
handleMissingComponent(unknownBlockStyleWarning(style), {
|
|
156
|
+
nodeType: 'blockStyle',
|
|
157
|
+
type: style,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return createElement(Block, {
|
|
161
|
+
key,
|
|
162
|
+
...serialized,
|
|
163
|
+
value: serialized.node,
|
|
164
|
+
renderNode,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function renderText(node: ToolkitTextNode, key: string) {
|
|
169
|
+
if (node.text === '\n') {
|
|
170
|
+
const HardBreak = components.hardBreak;
|
|
171
|
+
return HardBreak ? createElement(HardBreak, { key }) : '\n';
|
|
172
|
+
}
|
|
173
|
+
return node.text;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function renderUnknownType(node: TypedObject, index: number, key: string, isInline: boolean) {
|
|
177
|
+
const nodeOptions = { value: node, isInline, index, renderNode };
|
|
178
|
+
handleMissingComponent(unknownTypeWarning(node._type), { nodeType: 'block', type: node._type });
|
|
179
|
+
return createElement(components.unknownType, { key, ...nodeOptions });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function renderCustomBlock(node: TypedObject, index: number, key: string, isInline: boolean) {
|
|
183
|
+
const Component = components.types[node._type];
|
|
184
|
+
return Component
|
|
185
|
+
? createElement(Component, { key, value: node, isInline, index, renderNode })
|
|
186
|
+
: null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return renderNode;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function serializeBlock(options: Serializable<PortableTextBlock>): SerializedBlock {
|
|
193
|
+
const { node, index, isInline, renderNode } = options;
|
|
194
|
+
const children = buildMarksTree(node).map((child, childIndex) =>
|
|
195
|
+
renderNode({ node: child, isInline: true, index: childIndex, renderNode }),
|
|
196
|
+
);
|
|
197
|
+
return { _key: node._key || `block-${index}`, children, index, isInline, node };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function noop() {}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ToolkitListNestMode,
|
|
3
|
+
ToolkitPortableTextList,
|
|
4
|
+
ToolkitPortableTextListItem,
|
|
5
|
+
} from '@portabletext/toolkit';
|
|
6
|
+
import type {
|
|
7
|
+
ArbitraryTypedObject,
|
|
8
|
+
PortableTextBlock,
|
|
9
|
+
PortableTextBlockStyle,
|
|
10
|
+
PortableTextListItemBlock,
|
|
11
|
+
PortableTextListItemType,
|
|
12
|
+
TypedObject,
|
|
13
|
+
} from '@portabletext/types';
|
|
14
|
+
import type { OctaneNode } from 'octane';
|
|
15
|
+
|
|
16
|
+
export type PortableTextComponentType<Props = any> = (props: Props) => OctaneNode;
|
|
17
|
+
|
|
18
|
+
export interface PortableTextProps<
|
|
19
|
+
B extends TypedObject = PortableTextBlock | ArbitraryTypedObject,
|
|
20
|
+
> {
|
|
21
|
+
value: B | B[] | null | undefined;
|
|
22
|
+
components?: PortableTextComponents<B>;
|
|
23
|
+
onMissingComponent?: MissingComponentHandler | false;
|
|
24
|
+
listNestingMode?: ToolkitListNestMode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type PortableTextComponent<N> = PortableTextComponentType<PortableTextComponentProps<N>>;
|
|
28
|
+
export type PortableTextBlockComponent = PortableTextComponent<PortableTextBlock>;
|
|
29
|
+
export type PortableTextListComponent = PortableTextComponent<OctanePortableTextList>;
|
|
30
|
+
export type PortableTextListItemComponent = PortableTextComponent<PortableTextListItemBlock>;
|
|
31
|
+
export type PortableTextMarkComponent<M extends TypedObject = any> = PortableTextComponentType<
|
|
32
|
+
PortableTextMarkComponentProps<M>
|
|
33
|
+
>;
|
|
34
|
+
export type PortableTextTypeComponent<V extends TypedObject = any> = PortableTextComponentType<
|
|
35
|
+
PortableTextTypeComponentProps<V>
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
type LooseRecord<K extends string, V> = Record<string, V> & { [P in K]?: V };
|
|
39
|
+
type TypeName<T> = T extends { _type: infer Name } ? (Name extends string ? Name : never) : never;
|
|
40
|
+
type BuiltInPortableTextString<T> = T extends string ? (string extends T ? never : T) : never;
|
|
41
|
+
type BlockFrom<B extends TypedObject> = Extract<B, { _type: 'block' }>;
|
|
42
|
+
type CustomTypeFrom<B extends TypedObject> = Exclude<B, { _type: 'block' }>;
|
|
43
|
+
type CustomTypeName<B extends TypedObject> = TypeName<CustomTypeFrom<B>>;
|
|
44
|
+
|
|
45
|
+
export type DefaultPortableTextBlockStyle = BuiltInPortableTextString<PortableTextBlockStyle>;
|
|
46
|
+
export type DefaultPortableTextListItem = BuiltInPortableTextString<PortableTextListItemType>;
|
|
47
|
+
export type DefaultPortableTextMark =
|
|
48
|
+
'em' | 'strong' | 'code' | 'underline' | 'strike-through' | 'link';
|
|
49
|
+
|
|
50
|
+
type BlockStyleName<B extends TypedObject> =
|
|
51
|
+
BlockFrom<B> extends { style?: infer Style }
|
|
52
|
+
? NonNullable<Style> extends string
|
|
53
|
+
? NonNullable<Style>
|
|
54
|
+
: never
|
|
55
|
+
: never;
|
|
56
|
+
type CustomBlockStyleName<B extends TypedObject> = Exclude<
|
|
57
|
+
BlockStyleName<B>,
|
|
58
|
+
DefaultPortableTextBlockStyle
|
|
59
|
+
>;
|
|
60
|
+
type ListItemName<B extends TypedObject> =
|
|
61
|
+
BlockFrom<B> extends { listItem?: infer Item }
|
|
62
|
+
? NonNullable<Item> extends string
|
|
63
|
+
? NonNullable<Item>
|
|
64
|
+
: never
|
|
65
|
+
: never;
|
|
66
|
+
type CustomListItemName<B extends TypedObject> = Exclude<
|
|
67
|
+
ListItemName<B>,
|
|
68
|
+
DefaultPortableTextListItem
|
|
69
|
+
>;
|
|
70
|
+
type MarkFrom<B extends TypedObject> =
|
|
71
|
+
BlockFrom<B> extends { markDefs?: infer MarkDefs }
|
|
72
|
+
? NonNullable<MarkDefs> extends readonly (infer MarkDef)[]
|
|
73
|
+
? Extract<MarkDef, TypedObject>
|
|
74
|
+
: never
|
|
75
|
+
: never;
|
|
76
|
+
type MarkName<B extends TypedObject> = TypeName<MarkFrom<B>>;
|
|
77
|
+
type CustomMarkName<B extends TypedObject> = Exclude<MarkName<B>, DefaultPortableTextMark>;
|
|
78
|
+
|
|
79
|
+
type BlockComponentFor<B extends TypedObject> =
|
|
80
|
+
BlockFrom<B> extends never ? PortableTextBlockComponent : PortableTextComponent<BlockFrom<B>>;
|
|
81
|
+
type ListComponentFor<B extends TypedObject> =
|
|
82
|
+
ListItemName<B> extends never
|
|
83
|
+
? PortableTextListComponent
|
|
84
|
+
: PortableTextComponent<
|
|
85
|
+
Omit<OctanePortableTextList, 'listItem'> & { listItem: ListItemName<B> }
|
|
86
|
+
>;
|
|
87
|
+
type ListItemComponentFor<B extends TypedObject> =
|
|
88
|
+
ListItemName<B> extends never
|
|
89
|
+
? PortableTextListItemComponent
|
|
90
|
+
: PortableTextComponent<Omit<BlockFrom<B>, 'listItem'> & { listItem: ListItemName<B> }>;
|
|
91
|
+
|
|
92
|
+
type BlockComponents<B extends TypedObject> =
|
|
93
|
+
string extends BlockStyleName<B>
|
|
94
|
+
? LooseRecord<PortableTextBlockStyle, PortableTextBlockComponent | undefined>
|
|
95
|
+
: BlockStyleName<B> extends never
|
|
96
|
+
? LooseRecord<PortableTextBlockStyle, PortableTextBlockComponent | undefined>
|
|
97
|
+
: Record<string, PortableTextComponent<any> | undefined> & {
|
|
98
|
+
[Style in BlockStyleName<B>]?: PortableTextComponent<
|
|
99
|
+
Omit<BlockFrom<B>, 'style'> & { style?: Style }
|
|
100
|
+
>;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type ListComponents<B extends TypedObject> =
|
|
104
|
+
string extends ListItemName<B>
|
|
105
|
+
? LooseRecord<PortableTextListItemType, PortableTextListComponent | undefined>
|
|
106
|
+
: ListItemName<B> extends never
|
|
107
|
+
? Record<string, PortableTextListComponent | undefined>
|
|
108
|
+
: Record<string, PortableTextComponent<any> | undefined> & {
|
|
109
|
+
[Item in ListItemName<B>]?: PortableTextComponent<
|
|
110
|
+
Omit<OctanePortableTextList, 'listItem'> & { listItem: Item }
|
|
111
|
+
>;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
type ListItemComponents<B extends TypedObject> =
|
|
115
|
+
string extends ListItemName<B>
|
|
116
|
+
? LooseRecord<PortableTextListItemType, PortableTextListItemComponent | undefined>
|
|
117
|
+
: ListItemName<B> extends never
|
|
118
|
+
? Record<string, PortableTextListItemComponent | undefined>
|
|
119
|
+
: Record<string, PortableTextComponent<any> | undefined> & {
|
|
120
|
+
[Item in ListItemName<B>]?: PortableTextComponent<
|
|
121
|
+
Omit<BlockFrom<B>, 'listItem'> & { listItem: Item }
|
|
122
|
+
>;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
type MarkComponents<B extends TypedObject> =
|
|
126
|
+
string extends MarkName<B>
|
|
127
|
+
? Record<string, PortableTextMarkComponent | undefined>
|
|
128
|
+
: MarkName<B> extends never
|
|
129
|
+
? Record<string, PortableTextMarkComponent | undefined>
|
|
130
|
+
: Record<string, PortableTextMarkComponent | undefined> & {
|
|
131
|
+
[Name in MarkName<B>]?: PortableTextMarkComponent<Extract<MarkFrom<B>, { _type: Name }>>;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
type TypeComponents<B extends TypedObject> =
|
|
135
|
+
string extends CustomTypeName<B>
|
|
136
|
+
? Record<string, PortableTextTypeComponent | undefined>
|
|
137
|
+
: CustomTypeName<B> extends never
|
|
138
|
+
? Record<string, PortableTextTypeComponent | undefined>
|
|
139
|
+
: Record<string, PortableTextTypeComponent | undefined> & {
|
|
140
|
+
[Name in CustomTypeName<B>]?: PortableTextTypeComponent<
|
|
141
|
+
Extract<CustomTypeFrom<B>, { _type: Name }>
|
|
142
|
+
>;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export interface PortableTextOctaneComponents<B extends TypedObject = any> {
|
|
146
|
+
types: TypeComponents<B>;
|
|
147
|
+
marks: MarkComponents<B>;
|
|
148
|
+
block: BlockComponents<B> | PortableTextComponent<BlockFrom<B>>;
|
|
149
|
+
list: ListComponents<B> | PortableTextListComponent;
|
|
150
|
+
listItem: ListItemComponents<B> | PortableTextListItemComponent;
|
|
151
|
+
hardBreak: PortableTextComponentType | false;
|
|
152
|
+
unknownMark: PortableTextMarkComponent;
|
|
153
|
+
unknownType: PortableTextComponent<UnknownNodeType>;
|
|
154
|
+
unknownBlockStyle: PortableTextComponent<PortableTextBlock>;
|
|
155
|
+
unknownList: PortableTextComponent<OctanePortableTextList>;
|
|
156
|
+
unknownListItem: PortableTextComponent<PortableTextListItemBlock>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** @deprecated Prefer PortableTextOctaneComponents in new Octane code. */
|
|
160
|
+
export type PortableTextReactComponents<B extends TypedObject = any> =
|
|
161
|
+
PortableTextOctaneComponents<B>;
|
|
162
|
+
export type PortableTextComponents<B extends TypedObject = any> = Partial<
|
|
163
|
+
PortableTextOctaneComponents<B>
|
|
164
|
+
>;
|
|
165
|
+
|
|
166
|
+
type PortableTextValueItem<T> = Extract<
|
|
167
|
+
NonNullable<T> extends readonly (infer B)[] ? B : NonNullable<T>,
|
|
168
|
+
TypedObject
|
|
169
|
+
>;
|
|
170
|
+
type PortableTextArrayItem<T> =
|
|
171
|
+
NonNullable<T> extends readonly (infer Item)[]
|
|
172
|
+
? Extract<NonNullable<Item>, { _type: 'block' }> extends never
|
|
173
|
+
? never
|
|
174
|
+
: Extract<NonNullable<Item>, TypedObject>
|
|
175
|
+
: never;
|
|
176
|
+
type InferPortableTextTypedObject<T> = T extends unknown
|
|
177
|
+
? PortableTextArrayItem<T> extends never
|
|
178
|
+
? NonNullable<T> extends readonly (infer Item)[]
|
|
179
|
+
? InferPortableTextTypedObject<Item>
|
|
180
|
+
: NonNullable<T> extends object
|
|
181
|
+
? {
|
|
182
|
+
[Key in keyof NonNullable<T>]: InferPortableTextTypedObject<NonNullable<T>[Key]>;
|
|
183
|
+
}[keyof NonNullable<T>]
|
|
184
|
+
: never
|
|
185
|
+
: PortableTextArrayItem<T>
|
|
186
|
+
: never;
|
|
187
|
+
|
|
188
|
+
type StrictTypeComponents<B extends TypedObject> =
|
|
189
|
+
string extends CustomTypeName<B>
|
|
190
|
+
? Record<string, PortableTextTypeComponent | undefined>
|
|
191
|
+
: CustomTypeName<B> extends never
|
|
192
|
+
? Record<string, never>
|
|
193
|
+
: {
|
|
194
|
+
[Name in CustomTypeName<B>]-?: PortableTextTypeComponent<
|
|
195
|
+
Extract<CustomTypeFrom<B>, { _type: Name }>
|
|
196
|
+
>;
|
|
197
|
+
};
|
|
198
|
+
type StrictTypeOverrides<B extends TypedObject> =
|
|
199
|
+
CustomTypeName<B> extends never
|
|
200
|
+
? { types?: StrictTypeComponents<B> }
|
|
201
|
+
: { types: StrictTypeComponents<B> };
|
|
202
|
+
|
|
203
|
+
type StrictMarkComponents<B extends TypedObject> =
|
|
204
|
+
string extends MarkName<B>
|
|
205
|
+
? Record<string, PortableTextMarkComponent | undefined>
|
|
206
|
+
: MarkName<B> extends never
|
|
207
|
+
? Record<string, never>
|
|
208
|
+
: {
|
|
209
|
+
[Name in CustomMarkName<B>]-?: PortableTextMarkComponent<
|
|
210
|
+
Extract<MarkFrom<B>, { _type: Name }>
|
|
211
|
+
>;
|
|
212
|
+
} & {
|
|
213
|
+
[Name in Extract<DefaultPortableTextMark, MarkName<B>>]?: PortableTextMarkComponent<
|
|
214
|
+
Extract<MarkFrom<B>, { _type: Name }>
|
|
215
|
+
>;
|
|
216
|
+
};
|
|
217
|
+
type StrictMarkOverrides<B extends TypedObject> =
|
|
218
|
+
CustomMarkName<B> extends never
|
|
219
|
+
? { marks?: StrictMarkComponents<B> }
|
|
220
|
+
: { marks: StrictMarkComponents<B> };
|
|
221
|
+
|
|
222
|
+
type StrictBlockComponents<B extends TypedObject> =
|
|
223
|
+
string extends BlockStyleName<B>
|
|
224
|
+
? LooseRecord<PortableTextBlockStyle, PortableTextBlockComponent | undefined>
|
|
225
|
+
: BlockStyleName<B> extends never
|
|
226
|
+
? Record<string, never>
|
|
227
|
+
: {
|
|
228
|
+
[Style in CustomBlockStyleName<B>]-?: PortableTextComponent<
|
|
229
|
+
Omit<BlockFrom<B>, 'style'> & { style?: Style }
|
|
230
|
+
>;
|
|
231
|
+
} & {
|
|
232
|
+
[
|
|
233
|
+
Style in Extract<DefaultPortableTextBlockStyle, BlockStyleName<B>>
|
|
234
|
+
]?: PortableTextComponent<Omit<BlockFrom<B>, 'style'> & { style?: Style }>;
|
|
235
|
+
};
|
|
236
|
+
type StrictBlockOverrides<B extends TypedObject> =
|
|
237
|
+
CustomBlockStyleName<B> extends never
|
|
238
|
+
? { block?: StrictBlockComponents<B> | BlockComponentFor<B> }
|
|
239
|
+
: { block: StrictBlockComponents<B> | BlockComponentFor<B> };
|
|
240
|
+
|
|
241
|
+
type StrictListComponents<B extends TypedObject> =
|
|
242
|
+
string extends ListItemName<B>
|
|
243
|
+
? LooseRecord<PortableTextListItemType, PortableTextListComponent | undefined>
|
|
244
|
+
: ListItemName<B> extends never
|
|
245
|
+
? Record<string, never>
|
|
246
|
+
: {
|
|
247
|
+
[Item in CustomListItemName<B>]-?: PortableTextComponent<
|
|
248
|
+
Omit<OctanePortableTextList, 'listItem'> & { listItem: Item }
|
|
249
|
+
>;
|
|
250
|
+
} & {
|
|
251
|
+
[Item in Extract<DefaultPortableTextListItem, ListItemName<B>>]?: PortableTextComponent<
|
|
252
|
+
Omit<OctanePortableTextList, 'listItem'> & { listItem: Item }
|
|
253
|
+
>;
|
|
254
|
+
};
|
|
255
|
+
type StrictListOverrides<B extends TypedObject> =
|
|
256
|
+
CustomListItemName<B> extends never
|
|
257
|
+
? { list?: StrictListComponents<B> | ListComponentFor<B> }
|
|
258
|
+
: { list: StrictListComponents<B> | ListComponentFor<B> };
|
|
259
|
+
|
|
260
|
+
type StrictListItemComponents<B extends TypedObject> =
|
|
261
|
+
string extends ListItemName<B>
|
|
262
|
+
? LooseRecord<PortableTextListItemType, PortableTextListItemComponent | undefined>
|
|
263
|
+
: ListItemName<B> extends never
|
|
264
|
+
? Record<string, never>
|
|
265
|
+
: {
|
|
266
|
+
[Item in ListItemName<B>]-?: PortableTextComponent<
|
|
267
|
+
Omit<BlockFrom<B>, 'listItem'> & { listItem: Item }
|
|
268
|
+
>;
|
|
269
|
+
};
|
|
270
|
+
type StrictListItemOverrides<B extends TypedObject> = {
|
|
271
|
+
listItem?: StrictListItemComponents<B> | ListItemComponentFor<B>;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export type InferComponents<T> = PortableTextComponents<PortableTextValueItem<T>>;
|
|
275
|
+
export type InferValue<T> = Exclude<InferPortableTextTypedObject<T>, undefined>[];
|
|
276
|
+
export type InferStrictComponents<T> = Omit<
|
|
277
|
+
PortableTextComponents<PortableTextValueItem<T>>,
|
|
278
|
+
'types' | 'marks' | 'block' | 'list' | 'listItem'
|
|
279
|
+
> &
|
|
280
|
+
StrictTypeOverrides<PortableTextValueItem<T>> &
|
|
281
|
+
StrictMarkOverrides<PortableTextValueItem<T>> &
|
|
282
|
+
StrictBlockOverrides<PortableTextValueItem<T>> &
|
|
283
|
+
StrictListOverrides<PortableTextValueItem<T>> &
|
|
284
|
+
StrictListItemOverrides<PortableTextValueItem<T>>;
|
|
285
|
+
|
|
286
|
+
export interface PortableTextComponentProps<T> {
|
|
287
|
+
value: T;
|
|
288
|
+
index: number;
|
|
289
|
+
isInline: boolean;
|
|
290
|
+
children?: OctaneNode;
|
|
291
|
+
renderNode: NodeRenderer;
|
|
292
|
+
}
|
|
293
|
+
export type PortableTextTypeComponentProps<T> = Omit<PortableTextComponentProps<T>, 'children'>;
|
|
294
|
+
export interface PortableTextMarkComponentProps<M extends TypedObject = ArbitraryTypedObject> {
|
|
295
|
+
value?: M;
|
|
296
|
+
text: string;
|
|
297
|
+
markKey?: string;
|
|
298
|
+
markType: string;
|
|
299
|
+
children: OctaneNode;
|
|
300
|
+
renderNode: NodeRenderer;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export type UnknownNodeType = { _type: string; [key: string]: unknown } | TypedObject;
|
|
304
|
+
export type NodeRenderer = <T extends TypedObject>(options: Serializable<T>) => OctaneNode;
|
|
305
|
+
export type NodeType = 'block' | 'mark' | 'blockStyle' | 'listStyle' | 'listItemStyle';
|
|
306
|
+
export type MissingComponentHandler = (
|
|
307
|
+
message: string,
|
|
308
|
+
options: { type: string; nodeType: NodeType },
|
|
309
|
+
) => void;
|
|
310
|
+
export interface Serializable<T> {
|
|
311
|
+
node: T;
|
|
312
|
+
index: number;
|
|
313
|
+
isInline: boolean;
|
|
314
|
+
renderNode: NodeRenderer;
|
|
315
|
+
}
|
|
316
|
+
export interface SerializedBlock {
|
|
317
|
+
_key: string;
|
|
318
|
+
children: OctaneNode;
|
|
319
|
+
index: number;
|
|
320
|
+
isInline: boolean;
|
|
321
|
+
node: PortableTextBlock | PortableTextListItemBlock;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export type OctanePortableTextList = ToolkitPortableTextList;
|
|
325
|
+
export type OctanePortableTextListItem = ToolkitPortableTextListItem;
|
|
326
|
+
/** @deprecated Prefer OctanePortableTextList in new Octane code. */
|
|
327
|
+
export type ReactPortableTextList = OctanePortableTextList;
|
|
328
|
+
/** @deprecated Prefer OctanePortableTextListItem in new Octane code. */
|
|
329
|
+
export type ReactPortableTextListItem = OctanePortableTextListItem;
|
package/src/warnings.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const getTemplate = (type: string, prop: string): string =>
|
|
2
|
+
`[@portabletext/react] Unknown ${type}, specify a component for it in the \`components.${prop}\` prop`;
|
|
3
|
+
|
|
4
|
+
export const unknownTypeWarning = (typeName: string): string =>
|
|
5
|
+
getTemplate(`block type "${typeName}"`, 'types');
|
|
6
|
+
export const unknownMarkWarning = (markType: string): string =>
|
|
7
|
+
getTemplate(`mark type "${markType}"`, 'marks');
|
|
8
|
+
export const unknownBlockStyleWarning = (blockStyle: string): string =>
|
|
9
|
+
getTemplate(`block style "${blockStyle}"`, 'block');
|
|
10
|
+
export const unknownListStyleWarning = (listStyle: string): string =>
|
|
11
|
+
getTemplate(`list style "${listStyle}"`, 'list');
|
|
12
|
+
export const unknownListItemStyleWarning = (listStyle: string): string =>
|
|
13
|
+
getTemplate(`list item style "${listStyle}"`, 'listItem');
|
|
14
|
+
|
|
15
|
+
export function printWarning(message: string): void {
|
|
16
|
+
console.warn(message);
|
|
17
|
+
}
|