@wordpress/block-library 9.19.1 → 9.20.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.
Files changed (67) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/archives/edit.js +2 -2
  3. package/build/archives/edit.js.map +1 -1
  4. package/build/audio/edit.js +66 -33
  5. package/build/audio/edit.js.map +1 -1
  6. package/build/avatar/index.js +8 -3
  7. package/build/avatar/index.js.map +1 -1
  8. package/build/button/edit.js +43 -16
  9. package/build/button/edit.js.map +1 -1
  10. package/build/comment-template/hooks.js +6 -0
  11. package/build/comment-template/hooks.js.map +1 -1
  12. package/build/cover/index.js +8 -1
  13. package/build/cover/index.js.map +1 -1
  14. package/build/image/constants.js +2 -1
  15. package/build/image/constants.js.map +1 -1
  16. package/build/image/image.js +85 -72
  17. package/build/image/image.js.map +1 -1
  18. package/build/post-author/index.js +8 -1
  19. package/build/post-author/index.js.map +1 -1
  20. package/build/site-logo/index.js +8 -1
  21. package/build/site-logo/index.js.map +1 -1
  22. package/build/social-links/index.js +1 -0
  23. package/build/social-links/index.js.map +1 -1
  24. package/build/table-of-contents/hooks.js +6 -3
  25. package/build/table-of-contents/hooks.js.map +1 -1
  26. package/build-module/archives/edit.js +2 -2
  27. package/build-module/archives/edit.js.map +1 -1
  28. package/build-module/audio/edit.js +68 -35
  29. package/build-module/audio/edit.js.map +1 -1
  30. package/build-module/avatar/index.js +8 -3
  31. package/build-module/avatar/index.js.map +1 -1
  32. package/build-module/button/edit.js +44 -17
  33. package/build-module/button/edit.js.map +1 -1
  34. package/build-module/comment-template/hooks.js +6 -0
  35. package/build-module/comment-template/hooks.js.map +1 -1
  36. package/build-module/cover/index.js +8 -1
  37. package/build-module/cover/index.js.map +1 -1
  38. package/build-module/image/constants.js +1 -0
  39. package/build-module/image/constants.js.map +1 -1
  40. package/build-module/image/image.js +88 -75
  41. package/build-module/image/image.js.map +1 -1
  42. package/build-module/post-author/index.js +8 -1
  43. package/build-module/post-author/index.js.map +1 -1
  44. package/build-module/site-logo/index.js +8 -1
  45. package/build-module/site-logo/index.js.map +1 -1
  46. package/build-module/social-links/index.js +1 -0
  47. package/build-module/social-links/index.js.map +1 -1
  48. package/build-module/table-of-contents/hooks.js +6 -3
  49. package/build-module/table-of-contents/hooks.js.map +1 -1
  50. package/build-style/editor-rtl.css +0 -9
  51. package/build-style/editor.css +0 -9
  52. package/build-style/image/editor-rtl.css +0 -9
  53. package/build-style/image/editor.css +0 -9
  54. package/package.json +35 -35
  55. package/src/archives/edit.js +2 -2
  56. package/src/audio/edit.js +84 -33
  57. package/src/avatar/block.json +8 -3
  58. package/src/button/edit.js +69 -24
  59. package/src/comment-template/hooks.js +14 -6
  60. package/src/cover/block.json +8 -1
  61. package/src/image/constants.js +1 -0
  62. package/src/image/editor.scss +0 -13
  63. package/src/image/image.js +115 -122
  64. package/src/post-author/block.json +8 -1
  65. package/src/site-logo/block.json +8 -1
  66. package/src/social-links/block.json +1 -0
  67. package/src/table-of-contents/hooks.js +5 -2
@@ -19,7 +19,11 @@ import {
19
19
  DropdownMenu,
20
20
  Popover,
21
21
  } from '@wordpress/components';
22
- import { useViewportMatch } from '@wordpress/compose';
22
+ import {
23
+ useMergeRefs,
24
+ useResizeObserver,
25
+ useViewportMatch,
26
+ } from '@wordpress/compose';
23
27
  import { useSelect, useDispatch } from '@wordpress/data';
24
28
  import {
25
29
  BlockControls,
@@ -34,7 +38,7 @@ import {
34
38
  privateApis as blockEditorPrivateApis,
35
39
  BlockSettingsMenuControls,
36
40
  } from '@wordpress/block-editor';
37
- import { useEffect, useMemo, useState, useRef } from '@wordpress/element';
41
+ import { useCallback, useEffect, useMemo, useState } from '@wordpress/element';
38
42
  import { __, _x, sprintf, isRTL } from '@wordpress/i18n';
39
43
  import { getFilename } from '@wordpress/url';
40
44
  import { getBlockBindingsSource, switchToBlockType } from '@wordpress/blocks';
@@ -54,7 +58,7 @@ import { Caption } from '../utils/caption';
54
58
  * Module constants
55
59
  */
56
60
  import { useToolsPanelDropdownMenuProps } from '../utils/hooks';
57
- import { MIN_SIZE, ALLOWED_MEDIA_TYPES } from './constants';
61
+ import { MIN_SIZE, ALLOWED_MEDIA_TYPES, SIZED_LAYOUTS } from './constants';
58
62
  import { evalAspectRatio } from './utils';
59
63
 
60
64
  const { DimensionsTool, ResolutionTool } = unlock( blockEditorPrivateApis );
@@ -280,12 +284,22 @@ export default function Image( {
280
284
  lightbox,
281
285
  metadata,
282
286
  } = attributes;
283
-
284
- // The only supported unit is px, so we can parseInt to strip the px here.
285
- const numericWidth = width ? parseInt( width, 10 ) : undefined;
286
- const numericHeight = height ? parseInt( height, 10 ) : undefined;
287
-
288
- const imageRef = useRef();
287
+ const [ imageElement, setImageElement ] = useState();
288
+ const [ resizeDelta, setResizeDelta ] = useState( null );
289
+ const [ pixelSize, setPixelSize ] = useState( {} );
290
+ const [ offsetTop, setOffsetTop ] = useState( 0 );
291
+ const setResizeObserved = useResizeObserver( ( [ entry ] ) => {
292
+ if ( ! resizeDelta ) {
293
+ const [ box ] = entry.borderBoxSize;
294
+ setPixelSize( { width: box.inlineSize, height: box.blockSize } );
295
+ }
296
+ // This is usually 0 unless the image height is less than the line-height.
297
+ setOffsetTop( entry.target.offsetTop );
298
+ } );
299
+ const effectResizeableBoxPlacement = useCallback( () => {
300
+ setOffsetTop( imageElement?.offsetTop ?? 0 );
301
+ }, [ imageElement ] );
302
+ const setRefs = useMergeRefs( [ setImageElement, setResizeObserved ] );
289
303
  const { allowResize = true } = context;
290
304
  const { getBlock, getSettings } = useSelect( blockEditorStore );
291
305
 
@@ -369,34 +383,18 @@ export default function Image( {
369
383
  .catch( () => {} );
370
384
  }, [ id, url, isSingleSelected, externalBlob ] );
371
385
 
372
- // Get naturalWidth and naturalHeight from image ref, and fall back to loaded natural
386
+ // Get naturalWidth and naturalHeight from image, and fall back to loaded natural
373
387
  // width and height. This resolves an issue in Safari where the loaded natural
374
388
  // width and height is otherwise lost when switching between alignments.
375
389
  // See: https://github.com/WordPress/gutenberg/pull/37210.
376
390
  const { naturalWidth, naturalHeight } = useMemo( () => {
377
391
  return {
378
392
  naturalWidth:
379
- imageRef.current?.naturalWidth ||
380
- loadedNaturalWidth ||
381
- undefined,
393
+ imageElement?.naturalWidth || loadedNaturalWidth || undefined,
382
394
  naturalHeight:
383
- imageRef.current?.naturalHeight ||
384
- loadedNaturalHeight ||
385
- undefined,
395
+ imageElement?.naturalHeight || loadedNaturalHeight || undefined,
386
396
  };
387
- }, [
388
- loadedNaturalWidth,
389
- loadedNaturalHeight,
390
- imageRef.current?.complete,
391
- ] );
392
-
393
- function onResizeStart() {
394
- toggleSelection( false );
395
- }
396
-
397
- function onResizeStop() {
398
- toggleSelection( true );
399
- }
397
+ }, [ loadedNaturalWidth, loadedNaturalHeight, imageElement?.complete ] );
400
398
 
401
399
  function onImageError() {
402
400
  setHasImageErrored( true );
@@ -541,49 +539,49 @@ export default function Image( {
541
539
 
542
540
  const dropdownMenuProps = useToolsPanelDropdownMenuProps();
543
541
 
544
- const dimensionsControl = (
545
- <DimensionsTool
546
- value={ { width, height, scale, aspectRatio } }
547
- onChange={ ( {
548
- width: newWidth,
549
- height: newHeight,
550
- scale: newScale,
551
- aspectRatio: newAspectRatio,
552
- } ) => {
553
- // Rebuilding the object forces setting `undefined`
554
- // for values that are removed since setAttributes
555
- // doesn't do anything with keys that aren't set.
556
- setAttributes( {
557
- // CSS includes `height: auto`, but we need
558
- // `width: auto` to fix the aspect ratio when
559
- // only height is set due to the width and
560
- // height attributes set via the server.
561
- width: ! newWidth && newHeight ? 'auto' : newWidth,
542
+ const dimensionsControl =
543
+ isResizable &&
544
+ ( SIZED_LAYOUTS.includes( parentLayoutType ) ? (
545
+ <DimensionsTool
546
+ value={ { aspectRatio } }
547
+ onChange={ ( { aspectRatio: newAspectRatio } ) => {
548
+ setAttributes( {
549
+ aspectRatio: newAspectRatio,
550
+ scale: 'cover',
551
+ } );
552
+ } }
553
+ defaultAspectRatio="auto"
554
+ tools={ [ 'aspectRatio' ] }
555
+ />
556
+ ) : (
557
+ <DimensionsTool
558
+ value={ { width, height, scale, aspectRatio } }
559
+ onChange={ ( {
560
+ width: newWidth,
562
561
  height: newHeight,
563
562
  scale: newScale,
564
563
  aspectRatio: newAspectRatio,
565
- } );
566
- } }
567
- defaultScale="cover"
568
- defaultAspectRatio="auto"
569
- scaleOptions={ scaleOptions }
570
- unitsOptions={ dimensionsUnitsOptions }
571
- />
572
- );
573
-
574
- const aspectRatioControl = (
575
- <DimensionsTool
576
- value={ { aspectRatio } }
577
- onChange={ ( { aspectRatio: newAspectRatio } ) => {
578
- setAttributes( {
579
- aspectRatio: newAspectRatio,
580
- scale: 'cover',
581
- } );
582
- } }
583
- defaultAspectRatio="auto"
584
- tools={ [ 'aspectRatio' ] }
585
- />
586
- );
564
+ } ) => {
565
+ // Rebuilding the object forces setting `undefined`
566
+ // for values that are removed since setAttributes
567
+ // doesn't do anything with keys that aren't set.
568
+ setAttributes( {
569
+ // CSS includes `height: auto`, but we need
570
+ // `width: auto` to fix the aspect ratio when
571
+ // only height is set due to the width and
572
+ // height attributes set via the server.
573
+ width: ! newWidth && newHeight ? 'auto' : newWidth,
574
+ height: newHeight,
575
+ scale: newScale,
576
+ aspectRatio: newAspectRatio,
577
+ } );
578
+ } }
579
+ defaultScale="cover"
580
+ defaultAspectRatio="auto"
581
+ scaleOptions={ scaleOptions }
582
+ unitsOptions={ dimensionsUnitsOptions }
583
+ />
584
+ ) );
587
585
 
588
586
  const resetAll = () => {
589
587
  setAttributes( {
@@ -603,10 +601,7 @@ export default function Image( {
603
601
  resetAll={ resetAll }
604
602
  dropdownMenuProps={ dropdownMenuProps }
605
603
  >
606
- { isResizable &&
607
- ( parentLayoutType === 'grid'
608
- ? aspectRatioControl
609
- : dimensionsControl ) }
604
+ { dimensionsControl }
610
605
  </ToolsPanel>
611
606
  </InspectorControls>
612
607
  );
@@ -835,10 +830,7 @@ export default function Image( {
835
830
  />
836
831
  </ToolsPanelItem>
837
832
  ) }
838
- { isResizable &&
839
- ( parentLayoutType === 'grid'
840
- ? aspectRatioControl
841
- : dimensionsControl ) }
833
+ { dimensionsControl }
842
834
  { !! imageSizeOptions.length && (
843
835
  <ResolutionTool
844
836
  value={ sizeSlug }
@@ -926,17 +918,19 @@ export default function Image( {
926
918
  alt={ defaultedAlt }
927
919
  onError={ onImageError }
928
920
  onLoad={ onImageLoad }
929
- ref={ imageRef }
921
+ ref={ setRefs }
930
922
  className={ borderProps.className }
923
+ width={ naturalWidth }
924
+ height={ naturalHeight }
931
925
  style={ {
932
- width:
933
- ( width && height ) || aspectRatio
934
- ? '100%'
935
- : undefined,
936
- height:
937
- ( width && height ) || aspectRatio
938
- ? '100%'
939
- : undefined,
926
+ aspectRatio,
927
+ ...( resizeDelta
928
+ ? {
929
+ width: pixelSize.width + resizeDelta.width,
930
+ height:
931
+ pixelSize.height + resizeDelta.height,
932
+ }
933
+ : { width, height } ),
940
934
  objectFit: scale,
941
935
  ...borderProps.style,
942
936
  ...shadowProps.style,
@@ -953,8 +947,7 @@ export default function Image( {
953
947
  <ImageEditor
954
948
  id={ id }
955
949
  url={ url }
956
- width={ numericWidth }
957
- height={ numericHeight }
950
+ { ...pixelSize }
958
951
  naturalHeight={ naturalHeight }
959
952
  naturalWidth={ naturalWidth }
960
953
  onSaveImage={ ( imageAttributes ) =>
@@ -967,26 +960,21 @@ export default function Image( {
967
960
  />
968
961
  </ImageWrapper>
969
962
  );
970
- } else if ( ! isResizable || parentLayoutType === 'grid' ) {
971
- img = (
972
- <div style={ { width, height, aspectRatio } }>
973
- <ImageWrapper href={ href }>{ img }</ImageWrapper>
974
- </div>
975
- );
976
963
  } else {
964
+ img = <ImageWrapper href={ href }>{ img }</ImageWrapper>;
965
+ }
966
+
967
+ let resizableBox;
968
+ if (
969
+ isResizable &&
970
+ isSingleSelected &&
971
+ ! isEditingImage &&
972
+ ! SIZED_LAYOUTS.includes( parentLayoutType )
973
+ ) {
977
974
  const numericRatio = aspectRatio && evalAspectRatio( aspectRatio );
978
- const customRatio = numericWidth / numericHeight;
975
+ const customRatio = pixelSize.width / pixelSize.height;
979
976
  const naturalRatio = naturalWidth / naturalHeight;
980
977
  const ratio = numericRatio || customRatio || naturalRatio || 1;
981
- const currentWidth =
982
- ! numericWidth && numericHeight
983
- ? numericHeight * ratio
984
- : numericWidth;
985
- const currentHeight =
986
- ! numericHeight && numericWidth
987
- ? numericWidth / ratio
988
- : numericHeight;
989
-
990
978
  const minWidth =
991
979
  naturalWidth < naturalHeight ? MIN_SIZE : MIN_SIZE * ratio;
992
980
  const minHeight =
@@ -1032,21 +1020,17 @@ export default function Image( {
1032
1020
  }
1033
1021
  }
1034
1022
  /* eslint-enable no-lonely-if */
1035
- img = (
1023
+ resizableBox = (
1036
1024
  <ResizableBox
1025
+ ref={ effectResizeableBoxPlacement }
1037
1026
  style={ {
1038
- display: 'block',
1039
- objectFit: scale,
1040
- aspectRatio:
1041
- ! width && ! height && aspectRatio
1042
- ? aspectRatio
1043
- : undefined,
1044
- } }
1045
- size={ {
1046
- width: currentWidth ?? 'auto',
1047
- height: currentHeight ?? 'auto',
1027
+ position: 'absolute',
1028
+ // To match the vertical-align: bottom of the img (from style.scss)
1029
+ // syncs the top with the img. This matters when the img height is
1030
+ // less than the line-height.
1031
+ inset: `${ offsetTop }px 0 0 0`,
1048
1032
  } }
1049
- showHandle={ isSingleSelected }
1033
+ size={ pixelSize }
1050
1034
  minWidth={ minWidth }
1051
1035
  maxWidth={ maxResizeWidth }
1052
1036
  minHeight={ minHeight }
@@ -1058,9 +1042,19 @@ export default function Image( {
1058
1042
  bottom: true,
1059
1043
  left: showLeftHandle,
1060
1044
  } }
1061
- onResizeStart={ onResizeStart }
1062
- onResizeStop={ ( event, direction, elt ) => {
1063
- onResizeStop();
1045
+ onResizeStart={ () => {
1046
+ toggleSelection( false );
1047
+ } }
1048
+ onResize={ ( event, direction, elt, delta ) => {
1049
+ setResizeDelta( delta );
1050
+ } }
1051
+ onResizeStop={ ( event, direction, elt, delta ) => {
1052
+ toggleSelection( true );
1053
+ setResizeDelta( null );
1054
+ setPixelSize( ( current ) => ( {
1055
+ width: current.width + delta.width,
1056
+ height: current.height + delta.height,
1057
+ } ) );
1064
1058
 
1065
1059
  // Clear hardcoded width if the resized width is close to the max-content width.
1066
1060
  if (
@@ -1091,9 +1085,7 @@ export default function Image( {
1091
1085
  } );
1092
1086
  } }
1093
1087
  resizeRatio={ align === 'center' ? 2 : 1 }
1094
- >
1095
- <ImageWrapper href={ href }>{ img }</ImageWrapper>
1096
- </ResizableBox>
1088
+ />
1097
1089
  );
1098
1090
  }
1099
1091
 
@@ -1139,6 +1131,7 @@ export default function Image( {
1139
1131
  { controls }
1140
1132
  { featuredImageControl }
1141
1133
  { img }
1134
+ { resizableBox }
1142
1135
 
1143
1136
  <Caption
1144
1137
  attributes={ attributes }
@@ -58,7 +58,6 @@
58
58
  "color": {
59
59
  "gradients": true,
60
60
  "link": true,
61
- "__experimentalDuotone": ".wp-block-post-author__avatar img",
62
61
  "__experimentalDefaultControls": {
63
62
  "background": true,
64
63
  "text": true
@@ -78,6 +77,14 @@
78
77
  "width": true,
79
78
  "style": true
80
79
  }
80
+ },
81
+ "filter": {
82
+ "duotone": true
83
+ }
84
+ },
85
+ "selectors": {
86
+ "filter": {
87
+ "duotone": ".wp-block-post-author .wp-block-post-author__avatar img"
81
88
  }
82
89
  },
83
90
  "editorStyle": "wp-block-post-author-editor",
@@ -36,7 +36,6 @@
36
36
  "align": true,
37
37
  "alignWide": false,
38
38
  "color": {
39
- "__experimentalDuotone": "img, .components-placeholder__illustration, .components-placeholder::before",
40
39
  "text": false,
41
40
  "background": false
42
41
  },
@@ -50,6 +49,9 @@
50
49
  },
51
50
  "interactivity": {
52
51
  "clientNavigation": true
52
+ },
53
+ "filter": {
54
+ "duotone": true
53
55
  }
54
56
  },
55
57
  "styles": [
@@ -60,6 +62,11 @@
60
62
  },
61
63
  { "name": "rounded", "label": "Rounded" }
62
64
  ],
65
+ "selectors": {
66
+ "filter": {
67
+ "duotone": ".wp-block-site-logo img, .wp-block-site-logo .components-placeholder__illustration, .wp-block-site-logo .components-placeholder::before"
68
+ }
69
+ },
63
70
  "editorStyle": "wp-block-site-logo-editor",
64
71
  "style": "wp-block-site-logo"
65
72
  }
@@ -50,6 +50,7 @@
50
50
  "supports": {
51
51
  "align": [ "left", "center", "right" ],
52
52
  "anchor": true,
53
+ "html": false,
53
54
  "__experimentalExposeControlsToChildren": true,
54
55
  "layout": {
55
56
  "allowSwitching": false,
@@ -16,8 +16,8 @@ function getLatestHeadings( select, clientId ) {
16
16
  const {
17
17
  getBlockAttributes,
18
18
  getBlockName,
19
- getClientIdsWithDescendants,
20
19
  getBlocksByName,
20
+ getClientIdsOfDescendants,
21
21
  } = select( blockEditorStore );
22
22
 
23
23
  // FIXME: @wordpress/block-library should not depend on @wordpress/editor.
@@ -31,8 +31,11 @@ function getLatestHeadings( select, clientId ) {
31
31
  const isPaginated = getBlocksByName( 'core/nextpage' ).length !== 0;
32
32
  const { onlyIncludeCurrentPage } = getBlockAttributes( clientId ) ?? {};
33
33
 
34
+ // Get post-content block client ID.
35
+ const [ postContentClientId = '' ] = getBlocksByName( 'core/post-content' );
36
+
34
37
  // Get the client ids of all blocks in the editor.
35
- const allBlockClientIds = getClientIdsWithDescendants();
38
+ const allBlockClientIds = getClientIdsOfDescendants( postContentClientId );
36
39
 
37
40
  // If onlyIncludeCurrentPage is true, calculate the page (of a paginated post) this block is part of, so we know which headings to include; otherwise, skip the calculation.
38
41
  let tocPage = 1;