UNPKG

3.79 kBJavaScriptView Raw
1/**
2 * External dependencies
3 */
4import { omit } from 'lodash';
5
6/**
7 * WordPress dependencies
8 */
9import { __ } from '@wordpress/i18n';
10import {
11 createBlock,
12 getPhrasingContentSchema,
13 getBlockAttributes,
14} from '@wordpress/blocks';
15import { RichText } from '@wordpress/block-editor';
16import {
17 Path,
18 SVG,
19} from '@wordpress/components';
20
21/**
22 * Internal dependencies
23 */
24import edit from './edit';
25
26/**
27 * Given a node name string for a heading node, returns its numeric level.
28 *
29 * @param {string} nodeName Heading node name.
30 *
31 * @return {number} Heading level.
32 */
33export function getLevelFromHeadingNodeName( nodeName ) {
34 return Number( nodeName.substr( 1 ) );
35}
36
37const supports = {
38 className: false,
39 anchor: true,
40};
41
42const schema = {
43 content: {
44 type: 'string',
45 source: 'html',
46 selector: 'h1,h2,h3,h4,h5,h6',
47 default: '',
48 },
49 level: {
50 type: 'number',
51 default: 2,
52 },
53 align: {
54 type: 'string',
55 },
56 placeholder: {
57 type: 'string',
58 },
59};
60
61export const name = 'core/heading';
62
63export const settings = {
64 title: __( 'Heading' ),
65
66 description: __( 'Introduce new sections and organize content to help visitors (and search engines) understand the structure of your content.' ),
67
68 icon: <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><Path d="M5 4v3h5.5v12h3V7H19V4z" /><Path fill="none" d="M0 0h24v24H0V0z" /></SVG>,
69
70 category: 'common',
71
72 keywords: [ __( 'title' ), __( 'subtitle' ) ],
73
74 supports,
75
76 attributes: schema,
77
78 transforms: {
79 from: [
80 {
81 type: 'block',
82 blocks: [ 'core/paragraph' ],
83 transform: ( { content } ) => {
84 return createBlock( 'core/heading', {
85 content,
86 } );
87 },
88 },
89 {
90 type: 'raw',
91 selector: 'h1,h2,h3,h4,h5,h6',
92 schema: {
93 h1: { children: getPhrasingContentSchema() },
94 h2: { children: getPhrasingContentSchema() },
95 h3: { children: getPhrasingContentSchema() },
96 h4: { children: getPhrasingContentSchema() },
97 h5: { children: getPhrasingContentSchema() },
98 h6: { children: getPhrasingContentSchema() },
99 },
100 transform( node ) {
101 return createBlock( 'core/heading', {
102 ...getBlockAttributes(
103 'core/heading',
104 node.outerHTML
105 ),
106 level: getLevelFromHeadingNodeName( node.nodeName ),
107 } );
108 },
109 },
110 ...[ 2, 3, 4, 5, 6 ].map( ( level ) => ( {
111 type: 'prefix',
112 prefix: Array( level + 1 ).join( '#' ),
113 transform( content ) {
114 return createBlock( 'core/heading', {
115 level,
116 content,
117 } );
118 },
119 } ) ),
120 ],
121 to: [
122 {
123 type: 'block',
124 blocks: [ 'core/paragraph' ],
125 transform: ( { content } ) => {
126 return createBlock( 'core/paragraph', {
127 content,
128 } );
129 },
130 },
131 ],
132 },
133
134 deprecated: [
135 {
136 supports,
137 attributes: {
138 ...omit( schema, [ 'level' ] ),
139 nodeName: {
140 type: 'string',
141 source: 'property',
142 selector: 'h1,h2,h3,h4,h5,h6',
143 property: 'nodeName',
144 default: 'H2',
145 },
146 },
147 migrate( attributes ) {
148 const { nodeName, ...migratedAttributes } = attributes;
149
150 return {
151 ...migratedAttributes,
152 level: getLevelFromHeadingNodeName( nodeName ),
153 };
154 },
155 save( { attributes } ) {
156 const { align, nodeName, content } = attributes;
157
158 return (
159 <RichText.Content
160 tagName={ nodeName.toLowerCase() }
161 style={ { textAlign: align } }
162 value={ content }
163 />
164 );
165 },
166 },
167 ],
168
169 merge( attributes, attributesToMerge ) {
170 return {
171 content: ( attributes.content || '' ) + ( attributesToMerge.content || '' ),
172 };
173 },
174
175 edit,
176
177 save( { attributes } ) {
178 const { align, level, content } = attributes;
179 const tagName = 'h' + level;
180
181 return (
182 <RichText.Content
183 tagName={ tagName }
184 style={ { textAlign: align } }
185 value={ content }
186 />
187 );
188 },
189};