@portabletext/block-tools 3.3.3 → 3.4.1
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/lib/index.cjs +142 -124
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +4 -36
- package/lib/index.d.ts +4 -36
- package/lib/index.js +114 -95
- package/lib/index.js.map +1 -1
- package/package.json +10 -9
- package/src/HtmlDeserializer/flatten-nested-blocks.test.ts +112 -1
- package/src/HtmlDeserializer/flatten-nested-blocks.ts +174 -0
- package/src/HtmlDeserializer/helpers.ts +24 -159
- package/src/HtmlDeserializer/index.ts +7 -7
- package/src/HtmlDeserializer/rules/html.ts +12 -0
- package/src/index.ts +1 -1
- package/src/types.ts +1 -1
- package/src/util/normalizeBlock.ts +6 -6
- package/src/types.portable-text.ts +0 -79
|
@@ -1,9 +1,120 @@
|
|
|
1
1
|
import {compileSchema, defineSchema} from '@portabletext/schema'
|
|
2
2
|
import {describe, expect, test} from 'vitest'
|
|
3
3
|
import {createTestKeyGenerator} from '../../test/test-key-generator'
|
|
4
|
-
import {flattenNestedBlocks} from './
|
|
4
|
+
import {flattenNestedBlocks} from './flatten-nested-blocks'
|
|
5
5
|
|
|
6
6
|
describe(flattenNestedBlocks.name, () => {
|
|
7
|
+
test('flattening text blocks', () => {
|
|
8
|
+
const keyGenerator = createTestKeyGenerator('k')
|
|
9
|
+
const schema = compileSchema(defineSchema({}))
|
|
10
|
+
expect(
|
|
11
|
+
flattenNestedBlocks({schema, keyGenerator}, [
|
|
12
|
+
{
|
|
13
|
+
_type: 'block',
|
|
14
|
+
children: [
|
|
15
|
+
{
|
|
16
|
+
_type: 'block',
|
|
17
|
+
children: [
|
|
18
|
+
{
|
|
19
|
+
_type: 'span',
|
|
20
|
+
marks: [],
|
|
21
|
+
text: 'foo',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
markDefs: [],
|
|
25
|
+
style: 'normal',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
markDefs: [],
|
|
29
|
+
style: 'normal',
|
|
30
|
+
},
|
|
31
|
+
]),
|
|
32
|
+
).toEqual([
|
|
33
|
+
{
|
|
34
|
+
_type: 'block',
|
|
35
|
+
children: [{_type: 'span', text: 'foo', marks: []}],
|
|
36
|
+
markDefs: [],
|
|
37
|
+
style: 'normal',
|
|
38
|
+
},
|
|
39
|
+
])
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('flattening text blocks with block objects in schema', () => {
|
|
43
|
+
const keyGenerator = createTestKeyGenerator('k')
|
|
44
|
+
const schema = compileSchema(
|
|
45
|
+
defineSchema({blockObjects: [{name: 'image'}]}),
|
|
46
|
+
)
|
|
47
|
+
expect(
|
|
48
|
+
flattenNestedBlocks({schema, keyGenerator}, [
|
|
49
|
+
{
|
|
50
|
+
_type: 'block',
|
|
51
|
+
children: [
|
|
52
|
+
{
|
|
53
|
+
_type: 'block',
|
|
54
|
+
children: [
|
|
55
|
+
{
|
|
56
|
+
_type: 'span',
|
|
57
|
+
text: 'foo',
|
|
58
|
+
marks: [],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
markDefs: [],
|
|
62
|
+
style: 'normal',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
markDefs: [],
|
|
66
|
+
style: 'normal',
|
|
67
|
+
},
|
|
68
|
+
]),
|
|
69
|
+
).toEqual([
|
|
70
|
+
{
|
|
71
|
+
_type: 'block',
|
|
72
|
+
children: [{_type: 'span', text: 'foo', marks: []}],
|
|
73
|
+
markDefs: [],
|
|
74
|
+
style: 'normal',
|
|
75
|
+
},
|
|
76
|
+
])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('flattening text blocks with styles in schema', () => {
|
|
80
|
+
const keyGenerator = createTestKeyGenerator('k')
|
|
81
|
+
const schema = compileSchema(
|
|
82
|
+
defineSchema({
|
|
83
|
+
styles: [{name: 'h1'}],
|
|
84
|
+
}),
|
|
85
|
+
)
|
|
86
|
+
expect(
|
|
87
|
+
flattenNestedBlocks({schema, keyGenerator}, [
|
|
88
|
+
{
|
|
89
|
+
_type: 'block',
|
|
90
|
+
children: [
|
|
91
|
+
{
|
|
92
|
+
_type: 'block',
|
|
93
|
+
children: [
|
|
94
|
+
{
|
|
95
|
+
_type: 'span',
|
|
96
|
+
marks: [],
|
|
97
|
+
text: 'foo',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
markDefs: [],
|
|
101
|
+
style: 'normal',
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
markDefs: [],
|
|
105
|
+
style: 'h1',
|
|
106
|
+
},
|
|
107
|
+
]),
|
|
108
|
+
).toEqual([
|
|
109
|
+
{
|
|
110
|
+
_type: 'block',
|
|
111
|
+
children: [{_type: 'span', text: 'foo', marks: []}],
|
|
112
|
+
markDefs: [],
|
|
113
|
+
style: 'normal',
|
|
114
|
+
},
|
|
115
|
+
])
|
|
116
|
+
})
|
|
117
|
+
|
|
7
118
|
test('splitting text block', () => {
|
|
8
119
|
const keyGenerator = createTestKeyGenerator('k')
|
|
9
120
|
const schema = compileSchema(
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type {Schema} from '@portabletext/schema'
|
|
2
|
+
import {
|
|
3
|
+
isSpan,
|
|
4
|
+
isTextBlock,
|
|
5
|
+
type PortableTextBlock,
|
|
6
|
+
type PortableTextObject,
|
|
7
|
+
type PortableTextSpan,
|
|
8
|
+
type PortableTextTextBlock,
|
|
9
|
+
} from '@portabletext/schema'
|
|
10
|
+
import {isEqual} from 'lodash'
|
|
11
|
+
import {
|
|
12
|
+
isArbitraryTypedObject,
|
|
13
|
+
type ArbitraryTypedObject,
|
|
14
|
+
type TypedObject,
|
|
15
|
+
} from '../types'
|
|
16
|
+
|
|
17
|
+
export function flattenNestedBlocks(
|
|
18
|
+
context: {
|
|
19
|
+
schema: Schema
|
|
20
|
+
keyGenerator: () => string
|
|
21
|
+
},
|
|
22
|
+
blocks: Array<ArbitraryTypedObject>,
|
|
23
|
+
): TypedObject[] {
|
|
24
|
+
const flattened = blocks.flatMap((block) => {
|
|
25
|
+
if (isBlockContainer(block)) {
|
|
26
|
+
return flattenNestedBlocks(context, [block.block])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (isTextBlock(context, block)) {
|
|
30
|
+
const hasBlockObjects = block.children.some((child) => {
|
|
31
|
+
const knownBlockObject = context.schema.blockObjects.some(
|
|
32
|
+
(blockObject) => blockObject.name === child._type,
|
|
33
|
+
)
|
|
34
|
+
return knownBlockObject
|
|
35
|
+
})
|
|
36
|
+
const hasBlocks = block.children.some(
|
|
37
|
+
(child) => child._type === '__block' || child._type === 'block',
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if (hasBlockObjects || hasBlocks) {
|
|
41
|
+
const splitChildren = getSplitChildren(context, block)
|
|
42
|
+
|
|
43
|
+
if (
|
|
44
|
+
splitChildren.length === 1 &&
|
|
45
|
+
splitChildren[0].type === 'children' &&
|
|
46
|
+
isEqual(splitChildren[0].children, block.children)
|
|
47
|
+
) {
|
|
48
|
+
return [block]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return splitChildren.flatMap((slice) => {
|
|
52
|
+
if (slice.type === 'block object') {
|
|
53
|
+
return [slice.block]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (slice.type === 'block') {
|
|
57
|
+
return flattenNestedBlocks(context, [
|
|
58
|
+
slice.block as ArbitraryTypedObject,
|
|
59
|
+
])
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (slice.children.length > 0) {
|
|
63
|
+
if (
|
|
64
|
+
slice.children.every(
|
|
65
|
+
(child) => isSpan(context, child) && child.text.trim() === '',
|
|
66
|
+
)
|
|
67
|
+
) {
|
|
68
|
+
return []
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return flattenNestedBlocks(context, [
|
|
72
|
+
{
|
|
73
|
+
...block,
|
|
74
|
+
children: slice.children,
|
|
75
|
+
},
|
|
76
|
+
])
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return []
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return [block]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return [block]
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
return flattened
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isBlockContainer(
|
|
93
|
+
block: ArbitraryTypedObject,
|
|
94
|
+
): block is BlockContainer {
|
|
95
|
+
return block._type === '__block' && isArbitraryTypedObject(block.block)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
type BlockContainer = {
|
|
99
|
+
_type: '__block'
|
|
100
|
+
block: ArbitraryTypedObject
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getSplitChildren(
|
|
104
|
+
context: {schema: Schema},
|
|
105
|
+
block: PortableTextTextBlock,
|
|
106
|
+
) {
|
|
107
|
+
return block.children.reduce(
|
|
108
|
+
(slices, child) => {
|
|
109
|
+
const knownInlineObject = context.schema.inlineObjects.some(
|
|
110
|
+
(inlineObject) => inlineObject.name === child._type,
|
|
111
|
+
)
|
|
112
|
+
const knownBlockObject = context.schema.blockObjects.some(
|
|
113
|
+
(blockObject) => blockObject.name === child._type,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const lastSlice = slices.pop()
|
|
117
|
+
|
|
118
|
+
if (!isSpan(context, child) && !knownInlineObject) {
|
|
119
|
+
if (knownBlockObject) {
|
|
120
|
+
return [
|
|
121
|
+
...slices,
|
|
122
|
+
...(lastSlice ? [lastSlice] : []),
|
|
123
|
+
{type: 'block object' as const, block: child},
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (child._type === '__block') {
|
|
129
|
+
return [
|
|
130
|
+
...slices,
|
|
131
|
+
...(lastSlice ? [lastSlice] : []),
|
|
132
|
+
{
|
|
133
|
+
type: 'block object' as const,
|
|
134
|
+
block: (child as any).block,
|
|
135
|
+
},
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (child._type === 'block') {
|
|
140
|
+
return [
|
|
141
|
+
...slices,
|
|
142
|
+
...(lastSlice ? [lastSlice] : []),
|
|
143
|
+
{type: 'block' as const, block: child},
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (lastSlice) {
|
|
148
|
+
if (lastSlice.type === 'children') {
|
|
149
|
+
return [
|
|
150
|
+
...slices,
|
|
151
|
+
{
|
|
152
|
+
type: 'children' as const,
|
|
153
|
+
children: [...lastSlice.children, child],
|
|
154
|
+
},
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return [
|
|
160
|
+
...slices,
|
|
161
|
+
...(lastSlice ? [lastSlice] : []),
|
|
162
|
+
{type: 'children' as const, children: [child]},
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
[] as Array<
|
|
166
|
+
| {
|
|
167
|
+
type: 'children'
|
|
168
|
+
children: Array<PortableTextSpan | PortableTextObject>
|
|
169
|
+
}
|
|
170
|
+
| {type: 'block object'; block: PortableTextObject}
|
|
171
|
+
| {type: 'block'; block: PortableTextBlock}
|
|
172
|
+
>,
|
|
173
|
+
)
|
|
174
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type {Schema} from '@portabletext/schema'
|
|
2
|
+
import {
|
|
3
|
+
isTextBlock,
|
|
4
|
+
type PortableTextObject,
|
|
5
|
+
type PortableTextTextBlock,
|
|
6
|
+
} from '@portabletext/schema'
|
|
2
7
|
import {vercelStegaClean} from '@vercel/stega'
|
|
3
8
|
import {isEqual} from 'lodash'
|
|
4
9
|
import {DEFAULT_BLOCK} from '../constants'
|
|
@@ -12,13 +17,6 @@ import type {
|
|
|
12
17
|
PlaceholderDecorator,
|
|
13
18
|
TypedObject,
|
|
14
19
|
} from '../types'
|
|
15
|
-
import {
|
|
16
|
-
isSpan,
|
|
17
|
-
isTextBlock,
|
|
18
|
-
type PortableTextObject,
|
|
19
|
-
type PortableTextSpan,
|
|
20
|
-
type PortableTextTextBlock,
|
|
21
|
-
} from '../types.portable-text'
|
|
22
20
|
import {resolveJsType} from '../util/resolveJsType'
|
|
23
21
|
import preprocessors from './preprocessors'
|
|
24
22
|
|
|
@@ -72,143 +70,6 @@ export function defaultParseHtml(): HtmlParser {
|
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
export function flattenNestedBlocks(
|
|
76
|
-
context: {
|
|
77
|
-
schema: Schema
|
|
78
|
-
keyGenerator: () => string
|
|
79
|
-
},
|
|
80
|
-
blocks: Array<ArbitraryTypedObject>,
|
|
81
|
-
): TypedObject[] {
|
|
82
|
-
let depth = 0
|
|
83
|
-
const flattened: TypedObject[] = []
|
|
84
|
-
|
|
85
|
-
const traverse = (nodes: TypedObject[]) => {
|
|
86
|
-
const toRemove: TypedObject[] = []
|
|
87
|
-
nodes.forEach((node) => {
|
|
88
|
-
if (depth === 0) {
|
|
89
|
-
//Only apply splitting logic if we have block objects defined in the schema
|
|
90
|
-
if (
|
|
91
|
-
context.schema.blockObjects.length > 0 &&
|
|
92
|
-
isTextBlock(context.schema, node)
|
|
93
|
-
) {
|
|
94
|
-
const hasBlockObjects = node.children.some((child) => {
|
|
95
|
-
const knownBlockObject = context.schema.blockObjects.some(
|
|
96
|
-
(blockObject) => blockObject.name === child._type,
|
|
97
|
-
)
|
|
98
|
-
return knownBlockObject
|
|
99
|
-
})
|
|
100
|
-
const hasBlocks = node.children.some(
|
|
101
|
-
(child) => child._type === '__block',
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
if (hasBlockObjects || hasBlocks) {
|
|
105
|
-
// Split the block when it contains block objects
|
|
106
|
-
const splitChildren = node.children.reduce(
|
|
107
|
-
(slices, child) => {
|
|
108
|
-
const knownInlineObject = context.schema.inlineObjects.some(
|
|
109
|
-
(inlineObject) => inlineObject.name === child._type,
|
|
110
|
-
)
|
|
111
|
-
const knownBlockObject = context.schema.blockObjects.some(
|
|
112
|
-
(blockObject) => blockObject.name === child._type,
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
const lastSlice = slices.pop()
|
|
116
|
-
|
|
117
|
-
if (!isSpan(context.schema, child) && !knownInlineObject) {
|
|
118
|
-
if (knownBlockObject) {
|
|
119
|
-
return [
|
|
120
|
-
...slices,
|
|
121
|
-
...(lastSlice ? [lastSlice] : []),
|
|
122
|
-
{type: 'block object' as const, block: child},
|
|
123
|
-
]
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (child._type === '__block') {
|
|
128
|
-
return [
|
|
129
|
-
...slices,
|
|
130
|
-
...(lastSlice ? [lastSlice] : []),
|
|
131
|
-
{
|
|
132
|
-
type: 'block object' as const,
|
|
133
|
-
block: (child as any).block,
|
|
134
|
-
},
|
|
135
|
-
]
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (lastSlice) {
|
|
139
|
-
if (lastSlice.type === 'children') {
|
|
140
|
-
return [
|
|
141
|
-
...slices,
|
|
142
|
-
{
|
|
143
|
-
type: 'children' as const,
|
|
144
|
-
children: [...lastSlice.children, child],
|
|
145
|
-
},
|
|
146
|
-
]
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return [
|
|
151
|
-
...slices,
|
|
152
|
-
...(lastSlice ? [lastSlice] : []),
|
|
153
|
-
{type: 'children' as const, children: [child]},
|
|
154
|
-
]
|
|
155
|
-
},
|
|
156
|
-
[] as Array<
|
|
157
|
-
| {
|
|
158
|
-
type: 'children'
|
|
159
|
-
children: Array<PortableTextSpan | PortableTextObject>
|
|
160
|
-
}
|
|
161
|
-
| {type: 'block object'; block: PortableTextObject}
|
|
162
|
-
>,
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
// Process each slice
|
|
166
|
-
splitChildren.forEach((slice) => {
|
|
167
|
-
if (slice.type === 'block object') {
|
|
168
|
-
// Add the block object directly
|
|
169
|
-
flattened.push(slice.block)
|
|
170
|
-
} else if (slice.children.length > 0) {
|
|
171
|
-
// Create a new text block with the remaining children
|
|
172
|
-
const newBlock = {
|
|
173
|
-
...node,
|
|
174
|
-
children: slice.children,
|
|
175
|
-
}
|
|
176
|
-
flattened.push(newBlock)
|
|
177
|
-
}
|
|
178
|
-
})
|
|
179
|
-
return
|
|
180
|
-
} else {
|
|
181
|
-
// No block objects, add the block as is
|
|
182
|
-
flattened.push(node)
|
|
183
|
-
}
|
|
184
|
-
} else {
|
|
185
|
-
//Not a text block or no block objects in schema, add directly
|
|
186
|
-
flattened.push(node)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (isTextBlock(context.schema, node)) {
|
|
191
|
-
if (depth > 0) {
|
|
192
|
-
toRemove.push(node)
|
|
193
|
-
flattened.push(node)
|
|
194
|
-
}
|
|
195
|
-
depth++
|
|
196
|
-
traverse(node.children)
|
|
197
|
-
}
|
|
198
|
-
if (node._type === '__block') {
|
|
199
|
-
toRemove.push(node)
|
|
200
|
-
flattened.push((node as any).block)
|
|
201
|
-
}
|
|
202
|
-
})
|
|
203
|
-
toRemove.forEach((node) => {
|
|
204
|
-
nodes.splice(nodes.indexOf(node), 1)
|
|
205
|
-
})
|
|
206
|
-
depth--
|
|
207
|
-
}
|
|
208
|
-
traverse(blocks)
|
|
209
|
-
return flattened
|
|
210
|
-
}
|
|
211
|
-
|
|
212
73
|
function nextSpan(block: PortableTextTextBlock, index: number) {
|
|
213
74
|
const next = block.children[index + 1]
|
|
214
75
|
return next && next._type === 'span' ? next : null
|
|
@@ -234,7 +95,7 @@ export function trimWhitespace(
|
|
|
234
95
|
blocks: TypedObject[],
|
|
235
96
|
): TypedObject[] {
|
|
236
97
|
blocks.forEach((block) => {
|
|
237
|
-
if (!isTextBlock(schema, block)) {
|
|
98
|
+
if (!isTextBlock({schema}, block)) {
|
|
238
99
|
return
|
|
239
100
|
}
|
|
240
101
|
|
|
@@ -293,27 +154,27 @@ export function trimWhitespace(
|
|
|
293
154
|
|
|
294
155
|
export function ensureRootIsBlocks(
|
|
295
156
|
schema: Schema,
|
|
296
|
-
|
|
157
|
+
objects: Array<ArbitraryTypedObject>,
|
|
297
158
|
): ArbitraryTypedObject[] {
|
|
298
|
-
return
|
|
159
|
+
return objects.reduce((blocks, node, i, original) => {
|
|
299
160
|
if (node._type === 'block') {
|
|
300
|
-
|
|
301
|
-
return
|
|
161
|
+
blocks.push(node)
|
|
162
|
+
return blocks
|
|
302
163
|
}
|
|
303
164
|
|
|
304
165
|
if (node._type === '__block') {
|
|
305
|
-
|
|
306
|
-
return
|
|
166
|
+
blocks.push((node as any).block)
|
|
167
|
+
return blocks
|
|
307
168
|
}
|
|
308
169
|
|
|
309
|
-
const lastBlock =
|
|
170
|
+
const lastBlock = blocks[blocks.length - 1]
|
|
310
171
|
if (
|
|
311
172
|
i > 0 &&
|
|
312
|
-
!isTextBlock(schema, original[i - 1]) &&
|
|
313
|
-
isTextBlock(schema, lastBlock)
|
|
173
|
+
!isTextBlock({schema}, original[i - 1]) &&
|
|
174
|
+
isTextBlock({schema}, lastBlock)
|
|
314
175
|
) {
|
|
315
176
|
lastBlock.children.push(node as PortableTextObject)
|
|
316
|
-
return
|
|
177
|
+
return blocks
|
|
317
178
|
}
|
|
318
179
|
|
|
319
180
|
const block = {
|
|
@@ -321,8 +182,8 @@ export function ensureRootIsBlocks(
|
|
|
321
182
|
children: [node],
|
|
322
183
|
}
|
|
323
184
|
|
|
324
|
-
|
|
325
|
-
return
|
|
185
|
+
blocks.push(block)
|
|
186
|
+
return blocks
|
|
326
187
|
}, [] as ArbitraryTypedObject[])
|
|
327
188
|
}
|
|
328
189
|
|
|
@@ -393,7 +254,9 @@ export function normalizeWhitespace(rootNode: Node) {
|
|
|
393
254
|
}
|
|
394
255
|
|
|
395
256
|
// Remove marked nodes
|
|
396
|
-
nodesToRemove.forEach((node) =>
|
|
257
|
+
nodesToRemove.forEach((node) => {
|
|
258
|
+
node.parentElement?.removeChild(node)
|
|
259
|
+
})
|
|
397
260
|
}
|
|
398
261
|
|
|
399
262
|
/**
|
|
@@ -438,7 +301,9 @@ export function removeAllWhitespace(rootNode: Node) {
|
|
|
438
301
|
collectNodesToRemove(rootNode)
|
|
439
302
|
|
|
440
303
|
// Remove the collected nodes
|
|
441
|
-
nodesToRemove.forEach((node) =>
|
|
304
|
+
nodesToRemove.forEach((node) => {
|
|
305
|
+
node.parentElement?.removeChild(node)
|
|
306
|
+
})
|
|
442
307
|
}
|
|
443
308
|
|
|
444
309
|
function isWhitespaceBlock(elm: HTMLElement): boolean {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type {Schema} from '@portabletext/schema'
|
|
2
|
+
import {
|
|
3
|
+
isTextBlock,
|
|
4
|
+
type PortableTextBlock,
|
|
5
|
+
type PortableTextObject,
|
|
6
|
+
} from '@portabletext/schema'
|
|
2
7
|
import {flatten} from 'lodash'
|
|
3
8
|
import type {
|
|
4
9
|
ArbitraryTypedObject,
|
|
@@ -8,17 +13,12 @@ import type {
|
|
|
8
13
|
PlaceholderDecorator,
|
|
9
14
|
TypedObject,
|
|
10
15
|
} from '../types'
|
|
11
|
-
import {
|
|
12
|
-
isTextBlock,
|
|
13
|
-
type PortableTextBlock,
|
|
14
|
-
type PortableTextObject,
|
|
15
|
-
} from '../types.portable-text'
|
|
16
16
|
import {keyGenerator} from '../util/randomKey'
|
|
17
17
|
import {resolveJsType} from '../util/resolveJsType'
|
|
18
|
+
import {flattenNestedBlocks} from './flatten-nested-blocks'
|
|
18
19
|
import {
|
|
19
20
|
defaultParseHtml,
|
|
20
21
|
ensureRootIsBlocks,
|
|
21
|
-
flattenNestedBlocks,
|
|
22
22
|
isMinimalBlock,
|
|
23
23
|
isMinimalSpan,
|
|
24
24
|
isNodeList,
|
|
@@ -88,7 +88,7 @@ export default class HtmlDeserializer {
|
|
|
88
88
|
|
|
89
89
|
if (this._markDefs.length > 0) {
|
|
90
90
|
blocks
|
|
91
|
-
.filter((block) => isTextBlock(this.schema, block))
|
|
91
|
+
.filter((block) => isTextBlock({schema: this.schema}, block))
|
|
92
92
|
.forEach((block) => {
|
|
93
93
|
block.markDefs = block.markDefs || []
|
|
94
94
|
block.markDefs = block.markDefs.concat(
|
|
@@ -266,6 +266,18 @@ export default function createHTMLRules(
|
|
|
266
266
|
)
|
|
267
267
|
},
|
|
268
268
|
},
|
|
269
|
+
{
|
|
270
|
+
deserialize(el, next) {
|
|
271
|
+
if (isElement(el) && (tagName(el) === 'td' || tagName(el) === 'th')) {
|
|
272
|
+
return {
|
|
273
|
+
...DEFAULT_BLOCK,
|
|
274
|
+
children: next(el.childNodes),
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return undefined
|
|
279
|
+
},
|
|
280
|
+
},
|
|
269
281
|
{
|
|
270
282
|
deserialize(el) {
|
|
271
283
|
if (isElement(el) && tagName(el) === 'img') {
|
package/src/index.ts
CHANGED
|
@@ -36,7 +36,7 @@ export type {
|
|
|
36
36
|
PortableTextObject,
|
|
37
37
|
PortableTextSpan,
|
|
38
38
|
PortableTextTextBlock,
|
|
39
|
-
} from '
|
|
39
|
+
} from '@portabletext/schema'
|
|
40
40
|
export type {BlockNormalizationOptions} from './util/normalizeBlock'
|
|
41
41
|
export {randomKey} from './util/randomKey'
|
|
42
42
|
export {normalizeBlock}
|
package/src/types.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type {Schema} from '@portabletext/schema'
|
|
2
|
-
import {isEqual} from 'lodash'
|
|
3
|
-
import type {TypedObject} from '../types'
|
|
4
2
|
import {
|
|
5
3
|
isSpan,
|
|
6
4
|
type PortableTextSpan,
|
|
7
5
|
type PortableTextTextBlock,
|
|
8
|
-
} from '
|
|
6
|
+
} from '@portabletext/schema'
|
|
7
|
+
import {isEqual} from 'lodash'
|
|
8
|
+
import type {TypedObject} from '../types'
|
|
9
9
|
import {keyGenerator} from './randomKey'
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -115,8 +115,8 @@ export function normalizeBlock(
|
|
|
115
115
|
const previousChild = acc[acc.length - 1]
|
|
116
116
|
if (
|
|
117
117
|
previousChild &&
|
|
118
|
-
isSpan(schema, child) &&
|
|
119
|
-
isSpan(schema, previousChild) &&
|
|
118
|
+
isSpan({schema}, child) &&
|
|
119
|
+
isSpan({schema}, previousChild) &&
|
|
120
120
|
isEqual(previousChild.marks, child.marks)
|
|
121
121
|
) {
|
|
122
122
|
if (
|
|
@@ -145,7 +145,7 @@ export function normalizeBlock(
|
|
|
145
145
|
? options.keyGenerator()
|
|
146
146
|
: keyGenerator()
|
|
147
147
|
|
|
148
|
-
if (isSpan(schema, child)) {
|
|
148
|
+
if (isSpan({schema}, child)) {
|
|
149
149
|
if (!child.marks) {
|
|
150
150
|
child.marks = []
|
|
151
151
|
} else if (allowedDecorators) {
|