@portabletext/block-tools 2.0.8 → 3.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/README.md +1 -31
- package/lib/index.cjs +78 -162
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +27 -41
- package/lib/index.d.ts +27 -41
- package/lib/index.js +78 -162
- package/lib/index.js.map +1 -1
- package/package.json +9 -6
- package/src/HtmlDeserializer/helpers.ts +23 -46
- package/src/HtmlDeserializer/index.ts +15 -30
- package/src/HtmlDeserializer/rules/gdocs.ts +6 -9
- package/src/HtmlDeserializer/rules/html.ts +35 -24
- package/src/HtmlDeserializer/rules/index.ts +7 -7
- package/src/HtmlDeserializer/rules/notion.ts +1 -4
- package/src/index.ts +25 -30
- package/src/types.portable-text.ts +79 -0
- package/src/types.ts +11 -57
- package/src/util/normalizeBlock.ts +23 -7
- package/src/util/blockContentTypeFeatures.ts +0 -141
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ArraySchemaType,
|
|
3
|
-
PortableTextBlock,
|
|
4
|
-
PortableTextObject,
|
|
5
|
-
PortableTextTextBlock,
|
|
6
|
-
} from '@sanity/types'
|
|
1
|
+
import type {Schema} from '@portabletext/schema'
|
|
7
2
|
import {flatten} from 'lodash'
|
|
8
3
|
import type {
|
|
9
4
|
ArbitraryTypedObject,
|
|
@@ -13,10 +8,13 @@ import type {
|
|
|
13
8
|
PlaceholderDecorator,
|
|
14
9
|
TypedObject,
|
|
15
10
|
} from '../types'
|
|
16
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
isTextBlock,
|
|
13
|
+
type PortableTextBlock,
|
|
14
|
+
type PortableTextObject,
|
|
15
|
+
} from '../types.portable-text'
|
|
17
16
|
import {resolveJsType} from '../util/resolveJsType'
|
|
18
17
|
import {
|
|
19
|
-
createRuleOptions,
|
|
20
18
|
defaultParseHtml,
|
|
21
19
|
ensureRootIsBlocks,
|
|
22
20
|
flattenNestedBlocks,
|
|
@@ -36,7 +34,7 @@ import {createRules} from './rules'
|
|
|
36
34
|
*
|
|
37
35
|
*/
|
|
38
36
|
export default class HtmlDeserializer {
|
|
39
|
-
|
|
37
|
+
schema: Schema
|
|
40
38
|
rules: DeserializerRule[]
|
|
41
39
|
parseHtml: (html: string) => HTMLElement
|
|
42
40
|
_markDefs: PortableTextObject[] = []
|
|
@@ -47,21 +45,14 @@ export default class HtmlDeserializer {
|
|
|
47
45
|
* @param blockContentType - Schema type for array containing _at least_ a block child type
|
|
48
46
|
* @param options - Options for the deserialization process
|
|
49
47
|
*/
|
|
50
|
-
constructor(
|
|
51
|
-
blockContentType: ArraySchemaType,
|
|
52
|
-
options: HtmlDeserializerOptions = {},
|
|
53
|
-
) {
|
|
48
|
+
constructor(schema: Schema, options: HtmlDeserializerOptions = {}) {
|
|
54
49
|
const {rules = [], unstable_whitespaceOnPasteMode = 'preserve'} = options
|
|
55
|
-
|
|
56
|
-
throw new Error("Parameter 'blockContentType' is required")
|
|
57
|
-
}
|
|
58
|
-
const standardRules = createRules(blockContentType, {
|
|
59
|
-
...createRuleOptions(blockContentType),
|
|
50
|
+
const standardRules = createRules(schema, {
|
|
60
51
|
keyGenerator: options.keyGenerator,
|
|
61
52
|
})
|
|
53
|
+
this.schema = schema
|
|
62
54
|
this.rules = [...rules, ...standardRules]
|
|
63
55
|
const parseHtml = options.parseHtml || defaultParseHtml()
|
|
64
|
-
this.blockContentType = blockContentType
|
|
65
56
|
this.parseHtml = (html) => {
|
|
66
57
|
const doc = preprocess(html, parseHtml, {unstable_whitespaceOnPasteMode})
|
|
67
58
|
return doc.body
|
|
@@ -81,16 +72,16 @@ export default class HtmlDeserializer {
|
|
|
81
72
|
const children = Array.from(fragment.childNodes) as HTMLElement[]
|
|
82
73
|
// Ensure that there are no blocks within blocks, and trim whitespace
|
|
83
74
|
const blocks = trimWhitespace(
|
|
75
|
+
this.schema,
|
|
84
76
|
flattenNestedBlocks(
|
|
85
|
-
|
|
77
|
+
this.schema,
|
|
78
|
+
ensureRootIsBlocks(this.schema, this.deserializeElements(children)),
|
|
86
79
|
),
|
|
87
80
|
)
|
|
88
81
|
|
|
89
82
|
if (this._markDefs.length > 0) {
|
|
90
83
|
blocks
|
|
91
|
-
.filter(
|
|
92
|
-
(block): block is PortableTextTextBlock => block._type === 'block',
|
|
93
|
-
)
|
|
84
|
+
.filter((block) => isTextBlock(this.schema, block))
|
|
94
85
|
.forEach((block) => {
|
|
95
86
|
block.markDefs = block.markDefs || []
|
|
96
87
|
block.markDefs = block.markDefs.concat(
|
|
@@ -103,15 +94,9 @@ export default class HtmlDeserializer {
|
|
|
103
94
|
})
|
|
104
95
|
}
|
|
105
96
|
|
|
106
|
-
// Set back the potentially hoisted block type
|
|
107
|
-
const type = this.blockContentType.of.find(findBlockType)
|
|
108
|
-
if (!type) {
|
|
109
|
-
return blocks
|
|
110
|
-
}
|
|
111
|
-
|
|
112
97
|
return blocks.map((block) => {
|
|
113
98
|
if (block._type === 'block') {
|
|
114
|
-
block._type =
|
|
99
|
+
block._type = this.schema.block.name
|
|
115
100
|
}
|
|
116
101
|
return block
|
|
117
102
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {Schema} from '@portabletext/schema'
|
|
2
2
|
import {
|
|
3
3
|
BLOCK_DEFAULT_STYLE,
|
|
4
4
|
DEFAULT_BLOCK,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
HTML_HEADER_TAGS,
|
|
8
8
|
HTML_LIST_CONTAINER_TAGS,
|
|
9
9
|
} from '../../constants'
|
|
10
|
-
import type {
|
|
10
|
+
import type {DeserializerRule} from '../../types'
|
|
11
11
|
import {isElement, tagName} from '../helpers'
|
|
12
12
|
|
|
13
13
|
const LIST_CONTAINER_TAGS = Object.keys(HTML_LIST_CONTAINER_TAGS)
|
|
@@ -81,22 +81,19 @@ const blocks: Record<string, {style: string} | undefined> = {
|
|
|
81
81
|
...HTML_HEADER_TAGS,
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function getBlockStyle(
|
|
84
|
+
function getBlockStyle(schema: Schema, el: Node): string {
|
|
85
85
|
const childTag = tagName(el.firstChild)
|
|
86
86
|
const block = childTag && blocks[childTag]
|
|
87
87
|
if (!block) {
|
|
88
88
|
return BLOCK_DEFAULT_STYLE
|
|
89
89
|
}
|
|
90
|
-
if (!
|
|
90
|
+
if (!schema.styles.some((style) => style.name === block.style)) {
|
|
91
91
|
return BLOCK_DEFAULT_STYLE
|
|
92
92
|
}
|
|
93
93
|
return block.style
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
export default function createGDocsRules(
|
|
97
|
-
_blockContentType: ArraySchemaType,
|
|
98
|
-
options: BlockEnabledFeatures,
|
|
99
|
-
): DeserializerRule[] {
|
|
96
|
+
export default function createGDocsRules(schema: Schema): DeserializerRule[] {
|
|
100
97
|
return [
|
|
101
98
|
{
|
|
102
99
|
deserialize(el) {
|
|
@@ -130,7 +127,7 @@ export default function createGDocsRules(
|
|
|
130
127
|
...DEFAULT_BLOCK,
|
|
131
128
|
listItem: getListItemStyle(el),
|
|
132
129
|
level: getListItemLevel(el),
|
|
133
|
-
style: getBlockStyle(
|
|
130
|
+
style: getBlockStyle(schema, el),
|
|
134
131
|
children: next(el.firstChild?.childNodes || []),
|
|
135
132
|
}
|
|
136
133
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {Schema} from '@portabletext/schema'
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_BLOCK,
|
|
4
4
|
DEFAULT_SPAN,
|
|
@@ -10,27 +10,33 @@ import {
|
|
|
10
10
|
HTML_SPAN_TAGS,
|
|
11
11
|
type PartialBlock,
|
|
12
12
|
} from '../../constants'
|
|
13
|
-
import type {
|
|
13
|
+
import type {DeserializerRule} from '../../types'
|
|
14
14
|
import {keyGenerator} from '../../util/randomKey'
|
|
15
15
|
import {isElement, tagName} from '../helpers'
|
|
16
16
|
import {whitespaceTextNodeRule} from './whitespace-text-node'
|
|
17
17
|
|
|
18
18
|
export function resolveListItem(
|
|
19
|
+
schema: Schema,
|
|
19
20
|
listNodeTagName: string,
|
|
20
|
-
enabledListTypes: string[],
|
|
21
21
|
): string | undefined {
|
|
22
|
-
if (
|
|
22
|
+
if (
|
|
23
|
+
listNodeTagName === 'ul' &&
|
|
24
|
+
schema.lists.some((list) => list.name === 'bullet')
|
|
25
|
+
) {
|
|
23
26
|
return 'bullet'
|
|
24
27
|
}
|
|
25
|
-
if (
|
|
28
|
+
if (
|
|
29
|
+
listNodeTagName === 'ol' &&
|
|
30
|
+
schema.lists.some((list) => list.name === 'number')
|
|
31
|
+
) {
|
|
26
32
|
return 'number'
|
|
27
33
|
}
|
|
28
34
|
return undefined
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
export default function createHTMLRules(
|
|
32
|
-
|
|
33
|
-
options:
|
|
38
|
+
schema: Schema,
|
|
39
|
+
options: {keyGenerator?: () => string},
|
|
34
40
|
): DeserializerRule[] {
|
|
35
41
|
return [
|
|
36
42
|
whitespaceTextNodeRule,
|
|
@@ -41,7 +47,9 @@ export default function createHTMLRules(
|
|
|
41
47
|
return undefined
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
const isCodeEnabled =
|
|
50
|
+
const isCodeEnabled = schema.styles.some(
|
|
51
|
+
(style) => style.name === 'code',
|
|
52
|
+
)
|
|
45
53
|
|
|
46
54
|
return {
|
|
47
55
|
_type: 'block',
|
|
@@ -134,8 +142,9 @@ export default function createHTMLRules(
|
|
|
134
142
|
if (el.parentNode && tagName(el.parentNode) === 'li') {
|
|
135
143
|
return next(el.childNodes)
|
|
136
144
|
}
|
|
145
|
+
const blockStyle = block.style
|
|
137
146
|
// If style is not supported, return a defaultBlockType
|
|
138
|
-
if (!
|
|
147
|
+
if (!schema.styles.some((style) => style.name === blockStyle)) {
|
|
139
148
|
block = DEFAULT_BLOCK
|
|
140
149
|
}
|
|
141
150
|
return {
|
|
@@ -194,10 +203,7 @@ export default function createHTMLRules(
|
|
|
194
203
|
) {
|
|
195
204
|
return undefined
|
|
196
205
|
}
|
|
197
|
-
const enabledListItem = resolveListItem(
|
|
198
|
-
parentTag,
|
|
199
|
-
options.enabledListTypes,
|
|
200
|
-
)
|
|
206
|
+
const enabledListItem = resolveListItem(schema, parentTag)
|
|
201
207
|
// If the list item style is not supported, return a new default block
|
|
202
208
|
if (!enabledListItem) {
|
|
203
209
|
return block({_type: 'block', children: next(el.childNodes)})
|
|
@@ -212,7 +218,12 @@ export default function createHTMLRules(
|
|
|
212
218
|
{
|
|
213
219
|
deserialize(el, next) {
|
|
214
220
|
const decorator = HTML_DECORATOR_TAGS[tagName(el) || '']
|
|
215
|
-
if (
|
|
221
|
+
if (
|
|
222
|
+
!decorator ||
|
|
223
|
+
!schema.decorators.some(
|
|
224
|
+
(decoratorType) => decoratorType.name === decorator,
|
|
225
|
+
)
|
|
226
|
+
) {
|
|
216
227
|
return undefined
|
|
217
228
|
}
|
|
218
229
|
return {
|
|
@@ -228,23 +239,23 @@ export default function createHTMLRules(
|
|
|
228
239
|
if (tagName(el) !== 'a') {
|
|
229
240
|
return undefined
|
|
230
241
|
}
|
|
231
|
-
const linkEnabled =
|
|
242
|
+
const linkEnabled = schema.annotations.some(
|
|
243
|
+
(annotation) => annotation.name === 'link',
|
|
244
|
+
)
|
|
232
245
|
const href = isElement(el) && el.getAttribute('href')
|
|
233
246
|
if (!href) {
|
|
234
247
|
return next(el.childNodes)
|
|
235
248
|
}
|
|
236
|
-
let markDef: TypedObject | undefined
|
|
237
249
|
if (linkEnabled) {
|
|
238
|
-
markDef = {
|
|
239
|
-
_key: options.keyGenerator
|
|
240
|
-
? options.keyGenerator()
|
|
241
|
-
: keyGenerator(),
|
|
242
|
-
_type: 'link',
|
|
243
|
-
href: href,
|
|
244
|
-
}
|
|
245
250
|
return {
|
|
246
251
|
_type: '__annotation',
|
|
247
|
-
markDef:
|
|
252
|
+
markDef: {
|
|
253
|
+
_key: options.keyGenerator
|
|
254
|
+
? options.keyGenerator()
|
|
255
|
+
: keyGenerator(),
|
|
256
|
+
_type: 'link',
|
|
257
|
+
href: href,
|
|
258
|
+
},
|
|
248
259
|
children: next(el.childNodes),
|
|
249
260
|
}
|
|
250
261
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type {Schema} from '@portabletext/schema'
|
|
2
|
+
import type {DeserializerRule} from '../../types'
|
|
3
3
|
import createGDocsRules from './gdocs'
|
|
4
4
|
import createHTMLRules from './html'
|
|
5
5
|
import createNotionRules from './notion'
|
|
6
6
|
import createWordRules from './word'
|
|
7
7
|
|
|
8
8
|
export function createRules(
|
|
9
|
-
|
|
10
|
-
options:
|
|
9
|
+
schema: Schema,
|
|
10
|
+
options: {keyGenerator?: () => string},
|
|
11
11
|
): DeserializerRule[] {
|
|
12
12
|
return [
|
|
13
13
|
...createWordRules(),
|
|
14
|
-
...createNotionRules(
|
|
15
|
-
...createGDocsRules(
|
|
16
|
-
...createHTMLRules(
|
|
14
|
+
...createNotionRules(),
|
|
15
|
+
...createGDocsRules(schema),
|
|
16
|
+
...createHTMLRules(schema, options),
|
|
17
17
|
]
|
|
18
18
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {ArraySchemaType} from '@sanity/types'
|
|
2
1
|
import {DEFAULT_SPAN} from '../../constants'
|
|
3
2
|
import type {DeserializerRule} from '../../types'
|
|
4
3
|
import {isElement, tagName} from '../helpers'
|
|
@@ -28,9 +27,7 @@ function isNotion(el: Node): boolean {
|
|
|
28
27
|
return isElement(el) && Boolean(el.getAttribute('data-is-notion'))
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
export default function createNotionRules(
|
|
32
|
-
_blockContentType: ArraySchemaType,
|
|
33
|
-
): DeserializerRule[] {
|
|
30
|
+
export default function createNotionRules(): DeserializerRule[] {
|
|
34
31
|
return [
|
|
35
32
|
{
|
|
36
33
|
deserialize(el) {
|
package/src/index.ts
CHANGED
|
@@ -1,54 +1,49 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {sanitySchemaToPortableTextSchema} from '@portabletext/sanity-bridge'
|
|
2
|
+
import type {Schema} from '@portabletext/schema'
|
|
3
|
+
import type {ArraySchemaType} from '@sanity/types'
|
|
2
4
|
import HtmlDeserializer from './HtmlDeserializer'
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
HtmlDeserializerOptions,
|
|
6
|
-
TypedObject,
|
|
7
|
-
} from './types'
|
|
8
|
-
import blockContentTypeFeatures from './util/blockContentTypeFeatures'
|
|
5
|
+
import type {HtmlDeserializerOptions, TypedObject} from './types'
|
|
6
|
+
import type {PortableTextTextBlock} from './types.portable-text'
|
|
9
7
|
import {normalizeBlock} from './util/normalizeBlock'
|
|
10
8
|
|
|
11
9
|
/**
|
|
12
10
|
* Convert HTML to blocks respecting the block content type's schema
|
|
13
11
|
*
|
|
14
12
|
* @param html - The HTML to convert to blocks
|
|
15
|
-
* @param
|
|
13
|
+
* @param schemaType - A compiled version of the schema type for the block content
|
|
16
14
|
* @param options - Options for deserializing HTML to blocks
|
|
17
15
|
* @returns Array of blocks
|
|
18
16
|
* @public
|
|
19
17
|
*/
|
|
20
18
|
export function htmlToBlocks(
|
|
21
19
|
html: string,
|
|
22
|
-
|
|
20
|
+
schemaType: ArraySchemaType | Schema,
|
|
23
21
|
options: HtmlDeserializerOptions = {},
|
|
24
22
|
): (TypedObject | PortableTextTextBlock)[] {
|
|
25
|
-
const
|
|
23
|
+
const schema = isSanitySchema(schemaType)
|
|
24
|
+
? sanitySchemaToPortableTextSchema(schemaType)
|
|
25
|
+
: schemaType
|
|
26
|
+
|
|
27
|
+
const deserializer = new HtmlDeserializer(schema, options)
|
|
26
28
|
return deserializer
|
|
27
29
|
.deserialize(html)
|
|
28
30
|
.map((block) => normalizeBlock(block, {keyGenerator: options.keyGenerator}))
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
* Normalize and extract features of an schema type containing a block type
|
|
33
|
-
*
|
|
34
|
-
* @param blockContentType - Schema type for the block type
|
|
35
|
-
* @returns Returns the featureset of a compiled block content type.
|
|
36
|
-
* @public
|
|
37
|
-
*/
|
|
38
|
-
export function getBlockContentFeatures(
|
|
39
|
-
blockContentType: ArraySchemaType,
|
|
40
|
-
): BlockContentFeatures {
|
|
41
|
-
return blockContentTypeFeatures(blockContentType)
|
|
42
|
-
}
|
|
43
|
-
|
|
33
|
+
export type {ArbitraryTypedObject, DeserializerRule, HtmlParser} from './types'
|
|
44
34
|
export type {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
} from './types'
|
|
35
|
+
PortableTextBlock,
|
|
36
|
+
PortableTextObject,
|
|
37
|
+
PortableTextSpan,
|
|
38
|
+
PortableTextTextBlock,
|
|
39
|
+
} from './types.portable-text'
|
|
51
40
|
export type {BlockNormalizationOptions} from './util/normalizeBlock'
|
|
52
41
|
export {randomKey} from './util/randomKey'
|
|
53
42
|
export {normalizeBlock}
|
|
54
|
-
export type {
|
|
43
|
+
export type {HtmlDeserializerOptions, TypedObject}
|
|
44
|
+
|
|
45
|
+
function isSanitySchema(
|
|
46
|
+
schema: ArraySchemaType | Schema,
|
|
47
|
+
): schema is ArraySchemaType {
|
|
48
|
+
return schema.hasOwnProperty('jsonType')
|
|
49
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type {Schema} from '@portabletext/schema'
|
|
2
|
+
import {isArbitraryTypedObject} from './types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export type PortableTextBlock = PortableTextTextBlock | PortableTextObject
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export interface PortableTextTextBlock<
|
|
13
|
+
TChild = PortableTextSpan | PortableTextObject,
|
|
14
|
+
> {
|
|
15
|
+
_type: string
|
|
16
|
+
_key: string
|
|
17
|
+
children: TChild[]
|
|
18
|
+
markDefs?: PortableTextObject[]
|
|
19
|
+
listItem?: string
|
|
20
|
+
style?: string
|
|
21
|
+
level?: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isTextBlock(
|
|
25
|
+
schema: Schema,
|
|
26
|
+
block: unknown,
|
|
27
|
+
): block is PortableTextTextBlock {
|
|
28
|
+
if (!isArbitraryTypedObject(block)) {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (block._type !== schema.block.name) {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!Array.isArray(block.children)) {
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export interface PortableTextSpan {
|
|
47
|
+
_key: string
|
|
48
|
+
_type: 'span'
|
|
49
|
+
text: string
|
|
50
|
+
marks?: string[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function isSpan(
|
|
54
|
+
schema: Schema,
|
|
55
|
+
child: unknown,
|
|
56
|
+
): child is PortableTextSpan {
|
|
57
|
+
if (!isArbitraryTypedObject(child)) {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (child._type !== schema.span.name) {
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (typeof child.text !== 'string') {
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
export interface PortableTextObject {
|
|
76
|
+
_type: string
|
|
77
|
+
_key: string
|
|
78
|
+
[other: string]: unknown
|
|
79
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,47 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ArraySchemaType,
|
|
3
|
-
I18nTitledListValue,
|
|
4
|
-
ObjectSchemaType,
|
|
5
|
-
PortableTextObject,
|
|
6
|
-
SpanSchemaType,
|
|
7
|
-
TitledListValue,
|
|
8
|
-
} from '@sanity/types'
|
|
9
|
-
import type {ComponentType} from 'react'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @public
|
|
13
|
-
*/
|
|
14
|
-
export interface BlockContentFeatures {
|
|
15
|
-
styles: TitledListValue<string>[]
|
|
16
|
-
decorators: TitledListValue<string>[]
|
|
17
|
-
annotations: ResolvedAnnotationType[]
|
|
18
|
-
lists: I18nTitledListValue<string>[]
|
|
19
|
-
types: {
|
|
20
|
-
block: ArraySchemaType
|
|
21
|
-
span: SpanSchemaType
|
|
22
|
-
inlineObjects: ObjectSchemaType[]
|
|
23
|
-
blockObjects: ObjectSchemaType[]
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @beta
|
|
29
|
-
*/
|
|
30
|
-
export interface BlockEditorSchemaProps {
|
|
31
|
-
icon?: string | ComponentType
|
|
32
|
-
render?: ComponentType
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @public
|
|
37
|
-
*/
|
|
38
|
-
export interface ResolvedAnnotationType {
|
|
39
|
-
blockEditor?: BlockEditorSchemaProps
|
|
40
|
-
title: string | undefined
|
|
41
|
-
value: string
|
|
42
|
-
type: ObjectSchemaType
|
|
43
|
-
icon: ComponentType | undefined
|
|
44
|
-
}
|
|
1
|
+
import type {PortableTextObject} from './types.portable-text'
|
|
45
2
|
|
|
46
3
|
/**
|
|
47
4
|
* @public
|
|
@@ -58,6 +15,16 @@ export interface ArbitraryTypedObject extends TypedObject {
|
|
|
58
15
|
[key: string]: unknown
|
|
59
16
|
}
|
|
60
17
|
|
|
18
|
+
export function isArbitraryTypedObject(
|
|
19
|
+
object: unknown,
|
|
20
|
+
): object is ArbitraryTypedObject {
|
|
21
|
+
return isRecord(object) && typeof object._type === 'string'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
25
|
+
return !!value && (typeof value === 'object' || typeof value === 'function')
|
|
26
|
+
}
|
|
27
|
+
|
|
61
28
|
export interface MinimalSpan {
|
|
62
29
|
_type: 'span'
|
|
63
30
|
_key?: string
|
|
@@ -106,9 +73,6 @@ export interface HtmlDeserializerOptions {
|
|
|
106
73
|
unstable_whitespaceOnPasteMode?: WhiteSpacePasteMode
|
|
107
74
|
}
|
|
108
75
|
|
|
109
|
-
/**
|
|
110
|
-
* @public
|
|
111
|
-
*/
|
|
112
76
|
export interface HtmlPreprocessorOptions {
|
|
113
77
|
unstable_whitespaceOnPasteMode?: WhiteSpacePasteMode
|
|
114
78
|
}
|
|
@@ -128,13 +92,3 @@ export interface DeserializerRule {
|
|
|
128
92
|
},
|
|
129
93
|
) => TypedObject | TypedObject[] | undefined
|
|
130
94
|
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* @public
|
|
134
|
-
*/
|
|
135
|
-
export interface BlockEnabledFeatures {
|
|
136
|
-
enabledBlockStyles: string[]
|
|
137
|
-
enabledSpanDecorators: string[]
|
|
138
|
-
enabledListTypes: string[]
|
|
139
|
-
enabledBlockAnnotations: string[]
|
|
140
|
-
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import type {Schema} from '@portabletext/schema'
|
|
2
|
+
import {isEqual} from 'lodash'
|
|
3
|
+
import type {TypedObject} from '../types'
|
|
1
4
|
import {
|
|
2
|
-
|
|
5
|
+
isSpan,
|
|
3
6
|
type PortableTextSpan,
|
|
4
7
|
type PortableTextTextBlock,
|
|
5
|
-
} from '
|
|
6
|
-
import {isEqual} from 'lodash'
|
|
7
|
-
import type {TypedObject} from '../types'
|
|
8
|
+
} from '../types.portable-text'
|
|
8
9
|
import {keyGenerator} from './randomKey'
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -53,6 +54,21 @@ export function normalizeBlock(
|
|
|
53
54
|
> & {
|
|
54
55
|
_key: string
|
|
55
56
|
} {
|
|
57
|
+
const schema: Schema = {
|
|
58
|
+
block: {
|
|
59
|
+
name: options.blockTypeName || 'block',
|
|
60
|
+
},
|
|
61
|
+
span: {
|
|
62
|
+
name: 'span',
|
|
63
|
+
},
|
|
64
|
+
styles: [],
|
|
65
|
+
lists: [],
|
|
66
|
+
decorators: [],
|
|
67
|
+
annotations: [],
|
|
68
|
+
blockObjects: [],
|
|
69
|
+
inlineObjects: [],
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
if (node._type !== (options.blockTypeName || 'block')) {
|
|
57
73
|
return '_key' in node
|
|
58
74
|
? (node as TypedObject & {_key: string})
|
|
@@ -99,8 +115,8 @@ export function normalizeBlock(
|
|
|
99
115
|
const previousChild = acc[acc.length - 1]
|
|
100
116
|
if (
|
|
101
117
|
previousChild &&
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
isSpan(schema, child) &&
|
|
119
|
+
isSpan(schema, previousChild) &&
|
|
104
120
|
isEqual(previousChild.marks, child.marks)
|
|
105
121
|
) {
|
|
106
122
|
if (
|
|
@@ -129,7 +145,7 @@ export function normalizeBlock(
|
|
|
129
145
|
? options.keyGenerator()
|
|
130
146
|
: keyGenerator()
|
|
131
147
|
|
|
132
|
-
if (
|
|
148
|
+
if (isSpan(schema, child)) {
|
|
133
149
|
if (!child.marks) {
|
|
134
150
|
child.marks = []
|
|
135
151
|
} else if (allowedDecorators) {
|