UNPKG

8.96 kBJavaScriptView Raw
1/**
2 * External dependencies
3 */
4import { filter, every, map, some } from 'lodash';
5
6/**
7 * WordPress dependencies
8 */
9import { __ } from '@wordpress/i18n';
10import { createBlock } from '@wordpress/blocks';
11import { RichText } from '@wordpress/block-editor';
12import { mediaUpload } from '@wordpress/editor';
13import { createBlobURL } from '@wordpress/blob';
14
15/**
16 * Internal dependencies
17 */
18import { default as edit, defaultColumnsNumber, pickRelevantMediaFiles } from './edit';
19import icon from './icon';
20
21const blockAttributes = {
22 images: {
23 type: 'array',
24 default: [],
25 source: 'query',
26 selector: 'ul.wp-block-gallery .blocks-gallery-item',
27 query: {
28 url: {
29 source: 'attribute',
30 selector: 'img',
31 attribute: 'src',
32 },
33 link: {
34 source: 'attribute',
35 selector: 'img',
36 attribute: 'data-link',
37 },
38 alt: {
39 source: 'attribute',
40 selector: 'img',
41 attribute: 'alt',
42 default: '',
43 },
44 id: {
45 source: 'attribute',
46 selector: 'img',
47 attribute: 'data-id',
48 },
49 caption: {
50 type: 'string',
51 source: 'html',
52 selector: 'figcaption',
53 },
54 },
55 },
56 ids: {
57 type: 'array',
58 default: [],
59 },
60 columns: {
61 type: 'number',
62 },
63 imageCrop: {
64 type: 'boolean',
65 default: true,
66 },
67 linkTo: {
68 type: 'string',
69 default: 'none',
70 },
71};
72
73export const name = 'core/gallery';
74
75const parseShortcodeIds = ( ids ) => {
76 if ( ! ids ) {
77 return [];
78 }
79
80 return ids.split( ',' ).map( ( id ) => (
81 parseInt( id, 10 )
82 ) );
83};
84
85export const settings = {
86 title: __( 'Gallery' ),
87 description: __( 'Display multiple images in a rich gallery.' ),
88 icon,
89 category: 'common',
90 keywords: [ __( 'images' ), __( 'photos' ) ],
91 attributes: blockAttributes,
92 supports: {
93 align: true,
94 },
95
96 transforms: {
97 from: [
98 {
99 type: 'block',
100 isMultiBlock: true,
101 blocks: [ 'core/image' ],
102 transform: ( attributes ) => {
103 // Init the align attribute from the first item which may be either the placeholder or an image.
104 let { align } = attributes[ 0 ];
105 // Loop through all the images and check if they have the same align.
106 align = every( attributes, [ 'align', align ] ) ? align : undefined;
107
108 const validImages = filter( attributes, ( { id, url } ) => id && url );
109
110 return createBlock( 'core/gallery', {
111 images: validImages.map( ( { id, url, alt, caption } ) => ( { id, url, alt, caption } ) ),
112 ids: validImages.map( ( { id } ) => id ),
113 align,
114 } );
115 },
116 },
117 {
118 type: 'shortcode',
119 tag: 'gallery',
120 attributes: {
121 images: {
122 type: 'array',
123 shortcode: ( { named: { ids } } ) => {
124 return parseShortcodeIds( ids ).map( ( id ) => ( {
125 id,
126 } ) );
127 },
128 },
129 ids: {
130 type: 'array',
131 shortcode: ( { named: { ids } } ) => {
132 return parseShortcodeIds( ids );
133 },
134 },
135 columns: {
136 type: 'number',
137 shortcode: ( { named: { columns = '3' } } ) => {
138 return parseInt( columns, 10 );
139 },
140 },
141 linkTo: {
142 type: 'string',
143 shortcode: ( { named: { link = 'attachment' } } ) => {
144 return link === 'file' ? 'media' : link;
145 },
146 },
147 },
148 },
149 {
150 // When created by drag and dropping multiple files on an insertion point
151 type: 'files',
152 isMatch( files ) {
153 return files.length !== 1 && every( files, ( file ) => file.type.indexOf( 'image/' ) === 0 );
154 },
155 transform( files, onChange ) {
156 const block = createBlock( 'core/gallery', {
157 images: files.map( ( file ) => pickRelevantMediaFiles( {
158 url: createBlobURL( file ),
159 } ) ),
160 } );
161 mediaUpload( {
162 filesList: files,
163 onFileChange: ( images ) => {
164 const imagesAttr = images.map(
165 pickRelevantMediaFiles
166 );
167 onChange( block.clientId, {
168 ids: map( imagesAttr, 'id' ),
169 images: imagesAttr,
170 } );
171 },
172 allowedTypes: [ 'image' ],
173 } );
174 return block;
175 },
176 },
177 ],
178 to: [
179 {
180 type: 'block',
181 blocks: [ 'core/image' ],
182 transform: ( { images, align } ) => {
183 if ( images.length > 0 ) {
184 return images.map( ( { id, url, alt, caption } ) => createBlock( 'core/image', { id, url, alt, caption, align } ) );
185 }
186 return createBlock( 'core/image', { align } );
187 },
188 },
189 ],
190 },
191
192 edit,
193
194 save( { attributes } ) {
195 const { images, columns = defaultColumnsNumber( attributes ), imageCrop, linkTo } = attributes;
196 return (
197 <ul className={ `columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }` } >
198 { images.map( ( image ) => {
199 let href;
200
201 switch ( linkTo ) {
202 case 'media':
203 href = image.url;
204 break;
205 case 'attachment':
206 href = image.link;
207 break;
208 }
209
210 const img = <img src={ image.url } alt={ image.alt } data-id={ image.id } data-link={ image.link } className={ image.id ? `wp-image-${ image.id }` : null } />;
211
212 return (
213 <li key={ image.id || image.url } className="blocks-gallery-item">
214 <figure>
215 { href ? <a href={ href }>{ img }</a> : img }
216 { image.caption && image.caption.length > 0 && (
217 <RichText.Content tagName="figcaption" value={ image.caption } />
218 ) }
219 </figure>
220 </li>
221 );
222 } ) }
223 </ul>
224 );
225 },
226
227 deprecated: [
228 {
229 attributes: blockAttributes,
230 isEligible( { images, ids } ) {
231 return images &&
232 images.length > 0 &&
233 (
234 ( ! ids && images ) ||
235 ( ids && images && ids.length !== images.length ) ||
236 some( images, ( id, index ) => {
237 if ( ! id && ids[ index ] !== null ) {
238 return true;
239 }
240 return parseInt( id, 10 ) !== ids[ index ];
241 } )
242 );
243 },
244 migrate( attributes ) {
245 return {
246 ...attributes,
247 ids: map( attributes.images, ( { id } ) => {
248 if ( ! id ) {
249 return null;
250 }
251 return parseInt( id, 10 );
252 } ),
253 };
254 },
255 save( { attributes } ) {
256 const { images, columns = defaultColumnsNumber( attributes ), imageCrop, linkTo } = attributes;
257 return (
258 <ul className={ `columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }` } >
259 { images.map( ( image ) => {
260 let href;
261
262 switch ( linkTo ) {
263 case 'media':
264 href = image.url;
265 break;
266 case 'attachment':
267 href = image.link;
268 break;
269 }
270
271 const img = <img src={ image.url } alt={ image.alt } data-id={ image.id } data-link={ image.link } className={ image.id ? `wp-image-${ image.id }` : null } />;
272
273 return (
274 <li key={ image.id || image.url } className="blocks-gallery-item">
275 <figure>
276 { href ? <a href={ href }>{ img }</a> : img }
277 { image.caption && image.caption.length > 0 && (
278 <RichText.Content tagName="figcaption" value={ image.caption } />
279 ) }
280 </figure>
281 </li>
282 );
283 } ) }
284 </ul>
285 );
286 },
287 },
288 {
289 attributes: blockAttributes,
290 save( { attributes } ) {
291 const { images, columns = defaultColumnsNumber( attributes ), imageCrop, linkTo } = attributes;
292 return (
293 <ul className={ `columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }` } >
294 { images.map( ( image ) => {
295 let href;
296
297 switch ( linkTo ) {
298 case 'media':
299 href = image.url;
300 break;
301 case 'attachment':
302 href = image.link;
303 break;
304 }
305
306 const img = <img src={ image.url } alt={ image.alt } data-id={ image.id } data-link={ image.link } />;
307
308 return (
309 <li key={ image.id || image.url } className="blocks-gallery-item">
310 <figure>
311 { href ? <a href={ href }>{ img }</a> : img }
312 { image.caption && image.caption.length > 0 && (
313 <RichText.Content tagName="figcaption" value={ image.caption } />
314 ) }
315 </figure>
316 </li>
317 );
318 } ) }
319 </ul>
320 );
321 },
322 },
323 {
324 attributes: {
325 ...blockAttributes,
326 images: {
327 ...blockAttributes.images,
328 selector: 'div.wp-block-gallery figure.blocks-gallery-image img',
329 },
330 align: {
331 type: 'string',
332 default: 'none',
333 },
334 },
335
336 save( { attributes } ) {
337 const { images, columns = defaultColumnsNumber( attributes ), align, imageCrop, linkTo } = attributes;
338 return (
339 <div className={ `align${ align } columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }` } >
340 { images.map( ( image ) => {
341 let href;
342
343 switch ( linkTo ) {
344 case 'media':
345 href = image.url;
346 break;
347 case 'attachment':
348 href = image.link;
349 break;
350 }
351
352 const img = <img src={ image.url } alt={ image.alt } data-id={ image.id } />;
353
354 return (
355 <figure key={ image.id || image.url } className="blocks-gallery-image">
356 { href ? <a href={ href }>{ img }</a> : img }
357 </figure>
358 );
359 } ) }
360 </div>
361 );
362 },
363 },
364 ],
365};