PluginProbe
Extendify / 3.2.2
Extendify v3.2.2
3.2.2 3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 All 128 releases
extendify / src / Agent / lib / insertable-blocks.js

insertable-blocks.js in Extendify 3.2.2, at src/Agent/lib/insertable-blocks.js

122 lines 3.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { applyBlockPatch } from '@agent/lib/block-patch';
2 import { createBlock, getBlockType, parse, serialize } from '@wordpress/blocks';
3
4 // The add catalog the backend prompt offers — the plugin constrains it, not
5 // the backend, so old fielded builds never see types they can't build.
6 export const INSERTABLE_BLOCK_TYPES = [
7 'core/button',
8 'core/heading',
9 'core/paragraph',
10 'core/list',
11 'core/quote',
12 'core/pullquote',
13 'core/code',
14 'core/preformatted',
15 'core/verse',
16 'core/group',
17 'core/column',
18 'core/spacer',
19 'core/separator',
20 'core/social-link',
21 'core/navigation-link',
22 // Says the save can nest a link under a menu item; never added on its own.
23 'core/navigation-submenu',
24 ];
25
26 // One level of explicit children, on containers only.
27 const CHILD_BEARING_TYPES = ['core/group', 'core/column'];
28
29 // A lone button or social icon is invalid markup.
30 const ROW_ONLY_TYPES = {
31 'core/button': 'core/buttons',
32 'core/social-link': 'core/social-links',
33 };
34
35 // The model authors `text`; each type stores it under its own attribute.
36 const RICH_TEXT_ATTR = {
37 'core/button': 'text',
38 'core/heading': 'content',
39 'core/paragraph': 'content',
40 'core/pullquote': 'value',
41 'core/code': 'content',
42 'core/preformatted': 'content',
43 'core/verse': 'content',
44 'core/navigation-link': 'label',
45 };
46
47 const paragraphChildren = (text) =>
48 text ? [createBlock('core/paragraph', { content: text })] : [];
49
50 // A bare column is only valid inside core/columns, so it can't be a child.
51 const childBlocks = (children) =>
52 (children ?? [])
53 .filter(
54 ({ blockType }) =>
55 // A composed child only carries text, and neither type is valid on its own.
56 blockType !== 'core/social-link' &&
57 blockType !== 'core/navigation-link' &&
58 blockType !== 'core/column' &&
59 INSERTABLE_BLOCK_TYPES.includes(blockType) &&
60 getBlockType(blockType),
61 )
62 .map(({ blockType, text }) =>
63 blockType === 'core/button'
64 ? createBlock('core/buttons', {}, [freshBlock('core/button', text)])
65 : freshBlock(blockType, text),
66 );
67
68 const freshBlock = (blockType, text, children = []) => {
69 if (blockType === 'core/list') {
70 // The model sometimes authors <li> markup despite the plain-text
71 // instruction; splitting on it beats an empty leading item.
72 const items = String(text ?? '')
73 .replaceAll(/<\/?li[^>]*>/gi, '\n')
74 .split('\n')
75 .map((line) => line.trim())
76 .filter(Boolean);
77 return createBlock(
78 'core/list',
79 {},
80 items.map((content) => createBlock('core/list-item', { content })),
81 );
82 }
83 if (CHILD_BEARING_TYPES.includes(blockType)) {
84 const composed = childBlocks(children);
85 return createBlock(
86 blockType,
87 {},
88 composed.length ? composed : paragraphChildren(text),
89 );
90 }
91 // Quote holds its text as a paragraph child, not an attribute.
92 if (blockType === 'core/quote')
93 return createBlock(blockType, {}, paragraphChildren(text));
94 const attribute = RICH_TEXT_ATTR[blockType];
95 return createBlock(
96 blockType,
97 attribute && text != null ? { [attribute]: text } : {},
98 );
99 };
100
101 export const buildNewBlock = (
102 blockType,
103 patch = {},
104 clear = [],
105 presetSlugs = {},
106 { joinsRow = false } = {},
107 ) => {
108 if (!INSERTABLE_BLOCK_TYPES.includes(blockType)) return null;
109 if (!getBlockType(blockType)) return null;
110 const { text, children, ...attributePatch } = patch ?? {};
111 const markup = applyBlockPatch(
112 serialize([freshBlock(blockType, text, children ?? [])]),
113 attributePatch,
114 clear ?? [],
115 presetSlugs,
116 );
117 // Landing beside a sibling puts it in that row already; wrapping would nest one.
118 const row = joinsRow ? null : ROW_ONLY_TYPES[blockType];
119 if (!row) return markup;
120 return serialize([createBlock(row, {}, parse(markup))]);
121 };
122