@wordpress/block-library 9.33.1-next.b8c8708f3.0 → 9.33.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.
Files changed (158) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +1 -1
  3. package/build/breadcrumbs/block.json +8 -2
  4. package/build/breadcrumbs/edit.js +113 -12
  5. package/build/breadcrumbs/edit.js.map +2 -2
  6. package/build/buttons/transforms.js +7 -7
  7. package/build/buttons/transforms.js.map +2 -2
  8. package/build/code/transforms.js +19 -15
  9. package/build/code/transforms.js.map +2 -2
  10. package/build/heading/index.js +1 -3
  11. package/build/heading/index.js.map +3 -3
  12. package/build/heading/transforms.js +22 -20
  13. package/build/heading/transforms.js.map +2 -2
  14. package/build/index.js +5 -1
  15. package/build/index.js.map +2 -2
  16. package/build/math/block.json +21 -0
  17. package/build/math/edit.js +132 -0
  18. package/build/math/edit.js.map +7 -0
  19. package/build/math/index.js +63 -0
  20. package/build/math/index.js.map +7 -0
  21. package/build/math/init.js +35 -0
  22. package/build/math/init.js.map +7 -0
  23. package/build/math/save.js +40 -0
  24. package/build/math/save.js.map +7 -0
  25. package/build/navigation/edit/menu-inspector-controls.js +3 -3
  26. package/build/navigation/edit/menu-inspector-controls.js.map +2 -2
  27. package/build/navigation-link/edit.js +6 -3
  28. package/build/navigation-link/edit.js.map +2 -2
  29. package/build/navigation-link/link-ui/index.js +1 -1
  30. package/build/navigation-link/link-ui/index.js.map +2 -2
  31. package/build/navigation-link/shared/controls.js +4 -1
  32. package/build/navigation-link/shared/controls.js.map +3 -3
  33. package/build/navigation-link/shared/index.js +2 -0
  34. package/build/navigation-link/shared/index.js.map +2 -2
  35. package/build/navigation-link/shared/update-attributes.js +3 -1
  36. package/build/navigation-link/shared/update-attributes.js.map +2 -2
  37. package/build/navigation-link/shared/use-entity-binding.js +46 -13
  38. package/build/navigation-link/shared/use-entity-binding.js.map +2 -2
  39. package/build/navigation-submenu/edit.js +6 -3
  40. package/build/navigation-submenu/edit.js.map +2 -2
  41. package/build/page-list/use-convert-to-navigation-links.js +6 -1
  42. package/build/page-list/use-convert-to-navigation-links.js.map +2 -2
  43. package/build/paragraph/index.js +1 -3
  44. package/build/paragraph/index.js.map +3 -3
  45. package/build/post-date/deprecated.js +98 -2
  46. package/build/post-date/deprecated.js.map +2 -2
  47. package/build/post-date/edit.js +1 -1
  48. package/build/post-date/edit.js.map +1 -1
  49. package/build/post-date/variations.js +4 -4
  50. package/build/post-date/variations.js.map +2 -2
  51. package/build/term-template/edit.js +4 -1
  52. package/build/term-template/edit.js.map +2 -2
  53. package/build/utils/get-transformed-attributes.js +82 -0
  54. package/build/utils/get-transformed-attributes.js.map +7 -0
  55. package/build-module/breadcrumbs/block.json +8 -2
  56. package/build-module/breadcrumbs/edit.js +117 -14
  57. package/build-module/breadcrumbs/edit.js.map +2 -2
  58. package/build-module/buttons/transforms.js +7 -7
  59. package/build-module/buttons/transforms.js.map +2 -2
  60. package/build-module/code/transforms.js +19 -15
  61. package/build-module/code/transforms.js.map +2 -2
  62. package/build-module/heading/index.js +1 -3
  63. package/build-module/heading/index.js.map +2 -2
  64. package/build-module/heading/transforms.js +22 -20
  65. package/build-module/heading/transforms.js.map +2 -2
  66. package/build-module/index.js +5 -1
  67. package/build-module/index.js.map +2 -2
  68. package/build-module/math/block.json +21 -0
  69. package/build-module/math/edit.js +110 -0
  70. package/build-module/math/edit.js.map +7 -0
  71. package/build-module/math/index.js +26 -0
  72. package/build-module/math/index.js.map +7 -0
  73. package/build-module/math/init.js +8 -0
  74. package/build-module/math/init.js.map +7 -0
  75. package/build-module/math/save.js +20 -0
  76. package/build-module/math/save.js.map +7 -0
  77. package/build-module/navigation/edit/menu-inspector-controls.js +3 -3
  78. package/build-module/navigation/edit/menu-inspector-controls.js.map +2 -2
  79. package/build-module/navigation-link/edit.js +6 -3
  80. package/build-module/navigation-link/edit.js.map +2 -2
  81. package/build-module/navigation-link/link-ui/index.js +1 -1
  82. package/build-module/navigation-link/link-ui/index.js.map +2 -2
  83. package/build-module/navigation-link/shared/controls.js +4 -1
  84. package/build-module/navigation-link/shared/controls.js.map +2 -2
  85. package/build-module/navigation-link/shared/index.js +5 -1
  86. package/build-module/navigation-link/shared/index.js.map +2 -2
  87. package/build-module/navigation-link/shared/update-attributes.js +3 -1
  88. package/build-module/navigation-link/shared/update-attributes.js.map +2 -2
  89. package/build-module/navigation-link/shared/use-entity-binding.js +45 -13
  90. package/build-module/navigation-link/shared/use-entity-binding.js.map +2 -2
  91. package/build-module/navigation-submenu/edit.js +6 -3
  92. package/build-module/navigation-submenu/edit.js.map +2 -2
  93. package/build-module/page-list/use-convert-to-navigation-links.js +6 -1
  94. package/build-module/page-list/use-convert-to-navigation-links.js.map +2 -2
  95. package/build-module/paragraph/index.js +1 -3
  96. package/build-module/paragraph/index.js.map +2 -2
  97. package/build-module/post-date/deprecated.js +98 -2
  98. package/build-module/post-date/deprecated.js.map +2 -2
  99. package/build-module/post-date/edit.js +1 -1
  100. package/build-module/post-date/edit.js.map +1 -1
  101. package/build-module/post-date/variations.js +4 -4
  102. package/build-module/post-date/variations.js.map +2 -2
  103. package/build-module/term-template/edit.js +4 -1
  104. package/build-module/term-template/edit.js.map +2 -2
  105. package/build-module/utils/get-transformed-attributes.js +58 -0
  106. package/build-module/utils/get-transformed-attributes.js.map +7 -0
  107. package/package.json +61 -36
  108. package/src/breadcrumbs/block.json +8 -2
  109. package/src/breadcrumbs/edit.js +163 -18
  110. package/src/breadcrumbs/index.php +118 -16
  111. package/src/buttons/transforms.js +6 -6
  112. package/src/code/transforms.js +16 -14
  113. package/src/heading/index.js +0 -2
  114. package/src/heading/transforms.js +25 -24
  115. package/src/index.js +6 -1
  116. package/src/math/block.json +21 -0
  117. package/src/math/edit.js +123 -0
  118. package/src/math/index.js +31 -0
  119. package/src/math/init.js +4 -0
  120. package/src/math/save.js +20 -0
  121. package/src/navigation/edit/menu-inspector-controls.js +8 -7
  122. package/src/navigation-link/edit.js +6 -3
  123. package/src/navigation-link/index.php +4 -18
  124. package/src/navigation-link/link-ui/index.js +4 -2
  125. package/src/navigation-link/shared/controls.js +12 -3
  126. package/src/navigation-link/shared/index.js +4 -1
  127. package/src/navigation-link/shared/test/update-attributes.test.js +8 -0
  128. package/src/navigation-link/shared/test/use-entity-binding.js +132 -17
  129. package/src/navigation-link/shared/update-attributes.js +1 -0
  130. package/src/navigation-link/shared/use-entity-binding.js +74 -19
  131. package/src/navigation-submenu/edit.js +6 -3
  132. package/src/navigation-submenu/index.php +3 -17
  133. package/src/page-list/test/{convert-to-links-modal.js → convert-to-navigation-links.js} +67 -0
  134. package/src/page-list/use-convert-to-navigation-links.js +11 -1
  135. package/src/paragraph/index.js +0 -2
  136. package/src/post-date/deprecated.js +100 -2
  137. package/src/post-date/edit.js +1 -1
  138. package/src/post-date/index.php +3 -3
  139. package/src/post-date/variations.js +5 -4
  140. package/src/term-template/edit.js +4 -1
  141. package/src/term-template/index.php +4 -6
  142. package/src/utils/get-transformed-attributes.js +98 -0
  143. package/tsconfig.json +1 -0
  144. package/build/heading/variations.js +0 -48
  145. package/build/heading/variations.js.map +0 -7
  146. package/build/paragraph/variations.js +0 -48
  147. package/build/paragraph/variations.js.map +0 -7
  148. package/build/utils/get-transformed-metadata.js +0 -59
  149. package/build/utils/get-transformed-metadata.js.map +0 -7
  150. package/build-module/heading/variations.js +0 -28
  151. package/build-module/heading/variations.js.map +0 -7
  152. package/build-module/paragraph/variations.js +0 -28
  153. package/build-module/paragraph/variations.js.map +0 -7
  154. package/build-module/utils/get-transformed-metadata.js +0 -35
  155. package/build-module/utils/get-transformed-metadata.js.map +0 -7
  156. package/src/heading/variations.js +0 -29
  157. package/src/paragraph/variations.js +0 -29
  158. package/src/utils/get-transformed-metadata.js +0 -60
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/get-transformed-attributes.js"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { getBlockType, hasBlockSupport } from '@wordpress/blocks';\n\n/**\n * Transform block support attributes and metadata during block transforms.\n *\n * @param {Object} attributes Original attributes from the block being transformed.\n * @param {string} newBlockName Name of the target block after transformation.\n * @param {Function} bindingsCallback Optional callback to transform the `bindings` property object.\n * @return {Object} New attributes object with preserved block support attributes and metadata.\n */\nexport function getTransformedAttributes(\n\tattributes,\n\tnewBlockName,\n\tbindingsCallback = null\n) {\n\tif ( ! attributes ) {\n\t\treturn undefined;\n\t}\n\n\tconst newBlockType = getBlockType( newBlockName );\n\tif ( ! newBlockType ) {\n\t\treturn undefined;\n\t}\n\n\tconst transformedAttributes = {};\n\n\t// Handle attributes derived from block support.\n\tif (\n\t\thasBlockSupport( newBlockType, 'allowedBlocks' ) &&\n\t\tattributes.allowedBlocks\n\t) {\n\t\ttransformedAttributes.allowedBlocks = attributes.allowedBlocks;\n\t}\n\tif ( hasBlockSupport( newBlockType, 'anchor' ) && attributes.anchor ) {\n\t\ttransformedAttributes.anchor = attributes.anchor;\n\t}\n\tif (\n\t\thasBlockSupport( newBlockType, 'ariaLabel' ) &&\n\t\tattributes.ariaLabel\n\t) {\n\t\ttransformedAttributes.ariaLabel = attributes.ariaLabel;\n\t}\n\tif (\n\t\thasBlockSupport( newBlockType, 'className' ) &&\n\t\tattributes.className\n\t) {\n\t\ttransformedAttributes.className = attributes.className;\n\t}\n\n\t// Handle metadata transformation.\n\tif ( attributes.metadata ) {\n\t\t// The metadata properties that should be preserved after the transform.\n\t\tconst transformedMetadata = [ 'noteId' ];\n\n\t\t// If there is a transform bindings callback, add the `id` and `bindings` properties.\n\t\tif ( bindingsCallback ) {\n\t\t\ttransformedMetadata.push( 'id', 'bindings' );\n\t\t}\n\n\t\t// Handle metadata properties derived from block support.\n\t\tif ( hasBlockSupport( newBlockType, 'renaming', true ) ) {\n\t\t\ttransformedMetadata.push( 'name' );\n\t\t}\n\t\tif ( hasBlockSupport( newBlockType, 'blockVisibility', true ) ) {\n\t\t\ttransformedMetadata.push( 'blockVisibility' );\n\t\t}\n\n\t\t// Only process metadata if there are supported properties.\n\t\tif ( transformedMetadata.length > 0 ) {\n\t\t\tconst newMetadata = Object.entries( attributes.metadata ).reduce(\n\t\t\t\t( obj, [ prop, value ] ) => {\n\t\t\t\t\t// If prop is not supported, don't add it to the new metadata object.\n\t\t\t\t\tif ( ! transformedMetadata.includes( prop ) ) {\n\t\t\t\t\t\treturn obj;\n\t\t\t\t\t}\n\t\t\t\t\tobj[ prop ] =\n\t\t\t\t\t\tprop === 'bindings' ? bindingsCallback( value ) : value;\n\t\t\t\t\treturn obj;\n\t\t\t\t},\n\t\t\t\t{}\n\t\t\t);\n\n\t\t\t// Only add metadata if object is not empty.\n\t\t\tif ( Object.keys( newMetadata ).length > 0 ) {\n\t\t\t\ttransformedAttributes.metadata = newMetadata;\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( Object.keys( transformedAttributes ).length === 0 ) {\n\t\treturn undefined;\n\t}\n\n\treturn transformedAttributes;\n}\n"],
5
+ "mappings": "AAGA,SAAS,cAAc,uBAAuB;AAUvC,SAAS,yBACf,YACA,cACA,mBAAmB,MAClB;AACD,MAAK,CAAE,YAAa;AACnB,WAAO;AAAA,EACR;AAEA,QAAM,eAAe,aAAc,YAAa;AAChD,MAAK,CAAE,cAAe;AACrB,WAAO;AAAA,EACR;AAEA,QAAM,wBAAwB,CAAC;AAG/B,MACC,gBAAiB,cAAc,eAAgB,KAC/C,WAAW,eACV;AACD,0BAAsB,gBAAgB,WAAW;AAAA,EAClD;AACA,MAAK,gBAAiB,cAAc,QAAS,KAAK,WAAW,QAAS;AACrE,0BAAsB,SAAS,WAAW;AAAA,EAC3C;AACA,MACC,gBAAiB,cAAc,WAAY,KAC3C,WAAW,WACV;AACD,0BAAsB,YAAY,WAAW;AAAA,EAC9C;AACA,MACC,gBAAiB,cAAc,WAAY,KAC3C,WAAW,WACV;AACD,0BAAsB,YAAY,WAAW;AAAA,EAC9C;AAGA,MAAK,WAAW,UAAW;AAE1B,UAAM,sBAAsB,CAAE,QAAS;AAGvC,QAAK,kBAAmB;AACvB,0BAAoB,KAAM,MAAM,UAAW;AAAA,IAC5C;AAGA,QAAK,gBAAiB,cAAc,YAAY,IAAK,GAAI;AACxD,0BAAoB,KAAM,MAAO;AAAA,IAClC;AACA,QAAK,gBAAiB,cAAc,mBAAmB,IAAK,GAAI;AAC/D,0BAAoB,KAAM,iBAAkB;AAAA,IAC7C;AAGA,QAAK,oBAAoB,SAAS,GAAI;AACrC,YAAM,cAAc,OAAO,QAAS,WAAW,QAAS,EAAE;AAAA,QACzD,CAAE,KAAK,CAAE,MAAM,KAAM,MAAO;AAE3B,cAAK,CAAE,oBAAoB,SAAU,IAAK,GAAI;AAC7C,mBAAO;AAAA,UACR;AACA,cAAK,IAAK,IACT,SAAS,aAAa,iBAAkB,KAAM,IAAI;AACnD,iBAAO;AAAA,QACR;AAAA,QACA,CAAC;AAAA,MACF;AAGA,UAAK,OAAO,KAAM,WAAY,EAAE,SAAS,GAAI;AAC5C,8BAAsB,WAAW;AAAA,MAClC;AAAA,IACD;AAAA,EACD;AAEA,MAAK,OAAO,KAAM,qBAAsB,EAAE,WAAW,GAAI;AACxD,WAAO;AAAA,EACR;AAEA,SAAO;AACR;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/block-library",
3
- "version": "9.33.1-next.b8c8708f3.0",
3
+ "version": "9.33.1",
4
4
  "description": "Block library for the WordPress editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -51,46 +51,71 @@
51
51
  "src/*/editor.scss",
52
52
  "src/*/theme.scss"
53
53
  ],
54
+ "wpCopyFiles": {
55
+ "files": [
56
+ "src/**/*.php",
57
+ "src/*/block.json"
58
+ ],
59
+ "transforms": {
60
+ "php": {
61
+ "functionPrefix": "gutenberg_",
62
+ "classSuffix": "_Gutenberg",
63
+ "prefixFunctions": [
64
+ "wp_apply_colors_support",
65
+ "wp_enqueue_block_support_styles",
66
+ "wp_get_typography_font_size_value",
67
+ "wp_style_engine_get_styles",
68
+ "wp_get_global_settings"
69
+ ],
70
+ "suffixClasses": [
71
+ "WP_Navigation_Block_Renderer"
72
+ ],
73
+ "addActionPriority": 20,
74
+ "flattenIndexFiles": true
75
+ }
76
+ }
77
+ },
54
78
  "sideEffects": [
55
79
  "build-style/**",
56
80
  "src/**/*.scss",
57
81
  "{src,build,build-module}/*/init.js"
58
82
  ],
59
83
  "dependencies": {
60
- "@wordpress/a11y": "^4.32.1-next.b8c8708f3.0",
61
- "@wordpress/api-fetch": "^7.32.1-next.b8c8708f3.0",
62
- "@wordpress/autop": "^4.32.1-next.b8c8708f3.0",
63
- "@wordpress/base-styles": "^6.8.1-next.b8c8708f3.0",
64
- "@wordpress/blob": "^4.32.1-next.b8c8708f3.0",
65
- "@wordpress/block-editor": "^15.5.1-next.b8c8708f3.0",
66
- "@wordpress/blocks": "^15.5.1-next.b8c8708f3.0",
67
- "@wordpress/components": "^30.6.1-next.b8c8708f3.0",
68
- "@wordpress/compose": "^7.32.1-next.b8c8708f3.0",
69
- "@wordpress/core-data": "^7.32.1-next.b8c8708f3.0",
70
- "@wordpress/data": "^10.32.1-next.b8c8708f3.0",
71
- "@wordpress/date": "^5.32.1-next.b8c8708f3.0",
72
- "@wordpress/deprecated": "^4.32.1-next.b8c8708f3.0",
73
- "@wordpress/dom": "^4.32.1-next.b8c8708f3.0",
74
- "@wordpress/element": "^6.32.1-next.b8c8708f3.0",
75
- "@wordpress/escape-html": "^3.32.1-next.b8c8708f3.0",
76
- "@wordpress/hooks": "^4.32.1-next.b8c8708f3.0",
77
- "@wordpress/html-entities": "^4.32.1-next.b8c8708f3.0",
78
- "@wordpress/i18n": "^6.5.1-next.b8c8708f3.0",
79
- "@wordpress/icons": "^11.0.1-next.b8c8708f3.0",
80
- "@wordpress/interactivity": "^6.33.1-next.b8c8708f3.0",
81
- "@wordpress/interactivity-router": "^2.32.1-next.b8c8708f3.0",
82
- "@wordpress/keyboard-shortcuts": "^5.32.1-next.b8c8708f3.0",
83
- "@wordpress/keycodes": "^4.32.1-next.b8c8708f3.0",
84
- "@wordpress/notices": "^5.32.1-next.b8c8708f3.0",
85
- "@wordpress/patterns": "^2.32.1-next.b8c8708f3.0",
86
- "@wordpress/primitives": "^4.32.1-next.b8c8708f3.0",
87
- "@wordpress/private-apis": "^1.32.1-next.b8c8708f3.0",
88
- "@wordpress/reusable-blocks": "^5.32.1-next.b8c8708f3.0",
89
- "@wordpress/rich-text": "^7.32.1-next.b8c8708f3.0",
90
- "@wordpress/server-side-render": "^6.8.1-next.b8c8708f3.0",
91
- "@wordpress/url": "^4.32.1-next.b8c8708f3.0",
92
- "@wordpress/viewport": "^6.32.1-next.b8c8708f3.0",
93
- "@wordpress/wordcount": "^4.32.1-next.b8c8708f3.0",
84
+ "@wordpress/a11y": "^4.33.0",
85
+ "@wordpress/api-fetch": "^7.33.0",
86
+ "@wordpress/autop": "^4.33.0",
87
+ "@wordpress/base-styles": "^6.9.0",
88
+ "@wordpress/blob": "^4.33.0",
89
+ "@wordpress/block-editor": "^15.6.0",
90
+ "@wordpress/blocks": "^15.6.0",
91
+ "@wordpress/components": "^30.6.0",
92
+ "@wordpress/compose": "^7.33.0",
93
+ "@wordpress/core-data": "^7.33.0",
94
+ "@wordpress/data": "^10.33.0",
95
+ "@wordpress/date": "^5.33.0",
96
+ "@wordpress/deprecated": "^4.33.0",
97
+ "@wordpress/dom": "^4.33.0",
98
+ "@wordpress/element": "^6.33.0",
99
+ "@wordpress/escape-html": "^3.33.0",
100
+ "@wordpress/hooks": "^4.33.0",
101
+ "@wordpress/html-entities": "^4.33.0",
102
+ "@wordpress/i18n": "^6.6.0",
103
+ "@wordpress/icons": "^11.0.0",
104
+ "@wordpress/interactivity": "^6.33.0",
105
+ "@wordpress/interactivity-router": "^2.33.0",
106
+ "@wordpress/keyboard-shortcuts": "^5.33.0",
107
+ "@wordpress/keycodes": "^4.33.0",
108
+ "@wordpress/latex-to-mathml": "^1.1.0",
109
+ "@wordpress/notices": "^5.33.0",
110
+ "@wordpress/patterns": "^2.33.0",
111
+ "@wordpress/primitives": "^4.33.0",
112
+ "@wordpress/private-apis": "^1.33.0",
113
+ "@wordpress/reusable-blocks": "^5.33.0",
114
+ "@wordpress/rich-text": "^7.33.0",
115
+ "@wordpress/server-side-render": "^6.9.0",
116
+ "@wordpress/url": "^4.33.0",
117
+ "@wordpress/viewport": "^6.33.0",
118
+ "@wordpress/wordcount": "^4.33.0",
94
119
  "change-case": "^4.1.2",
95
120
  "clsx": "^2.1.1",
96
121
  "colord": "^2.7.0",
@@ -108,5 +133,5 @@
108
133
  "publishConfig": {
109
134
  "access": "public"
110
135
  },
111
- "gitHead": "67cfd7e661931aeb0d06bec894599d287a4f8d0f"
136
+ "gitHead": "0eca9b157f332afd4d1a53c62bd862ae9a159c57"
112
137
  }
@@ -3,10 +3,16 @@
3
3
  "apiVersion": 3,
4
4
  "name": "core/breadcrumbs",
5
5
  "title": "Breadcrumbs",
6
+ "__experimental": true,
6
7
  "category": "theme",
7
- "description": "Display a breadcrumb trail only for Pages, or for hierarchical post types. The block is useful to insert in the Pages template.",
8
+ "description": "Display a breadcrumb trail for hierarchical post types or based on taxonomy terms.",
8
9
  "textdomain": "default",
9
10
  "attributes": {
11
+ "type": {
12
+ "type": "string",
13
+ "default": "auto",
14
+ "enum": [ "auto", "postWithTerms", "postWithAncestors" ]
15
+ },
10
16
  "separator": {
11
17
  "type": "string",
12
18
  "default": "/"
@@ -16,7 +22,7 @@
16
22
  "default": true
17
23
  }
18
24
  },
19
- "usesContext": [ "postId", "postType" ],
25
+ "usesContext": [ "postId", "postType", "templateSlug" ],
20
26
  "supports": {
21
27
  "html": false,
22
28
  "spacing": {
@@ -6,52 +6,157 @@ import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
6
6
  import {
7
7
  ToggleControl,
8
8
  TextControl,
9
+ SelectControl,
9
10
  __experimentalToolsPanel as ToolsPanel,
10
11
  __experimentalToolsPanelItem as ToolsPanelItem,
12
+ Spinner,
11
13
  } from '@wordpress/components';
12
14
  import { useSelect } from '@wordpress/data';
13
15
  import { store as coreStore } from '@wordpress/core-data';
14
- import { RawHTML } from '@wordpress/element';
16
+ import { useEffect, useState, RawHTML } from '@wordpress/element';
15
17
  import { useServerSideRender } from '@wordpress/server-side-render';
16
18
 
17
19
  /**
18
20
  * Internal dependencies
19
21
  */
20
22
  import { useToolsPanelDropdownMenuProps } from '../utils/hooks';
23
+
21
24
  const separatorDefaultValue = '/';
25
+ const typeDefaultValue = 'auto';
26
+
27
+ const BREADCRUMB_TYPES = {
28
+ auto: {
29
+ help: __(
30
+ 'Try to automatically determine the best type of breadcrumb for the template.'
31
+ ),
32
+ },
33
+ postWithAncestors: {
34
+ help: __(
35
+ 'Shows breadcrumbs based on post hierarchy. Only works for hierarchical post types.'
36
+ ),
37
+ placeholderItems: [ __( 'Ancestor' ), __( 'Parent' ) ],
38
+ },
39
+ postWithTerms: {
40
+ help: __(
41
+ 'Shows breadcrumbs based on taxonomy terms. Chooses the first taxonomy with assigned terms and includes ancestors if the taxonomy is hierarchical.'
42
+ ),
43
+ placeholderItems: [ __( 'Category' ) ],
44
+ },
45
+ };
22
46
 
23
47
  export default function BreadcrumbEdit( {
24
48
  attributes,
25
49
  setAttributes,
26
- context: { postId, postType },
50
+ context: { postId, postType, templateSlug },
27
51
  } ) {
28
- const { separator, showHomeLink } = attributes;
29
- const isPostTypeHierarchical = useSelect(
30
- ( select ) => {
31
- if ( ! postType ) {
32
- return null;
33
- }
34
- return select( coreStore ).getPostType( postType )?.hierarchical;
35
- },
36
- [ postType ]
37
- );
52
+ const { separator, showHomeLink, type } = attributes;
53
+ const { post, isPostTypeHierarchical, hasTermsAssigned, isLoading } =
54
+ useSelect(
55
+ ( select ) => {
56
+ if ( ! postType ) {
57
+ return {};
58
+ }
59
+ const _post = select( coreStore ).getEntityRecord(
60
+ 'postType',
61
+ postType,
62
+ postId
63
+ );
64
+ const postTypeObject =
65
+ select( coreStore ).getPostType( postType );
66
+ const postTypeHasTaxonomies =
67
+ postTypeObject && postTypeObject.taxonomies.length;
68
+ let taxonomies;
69
+ if ( postTypeHasTaxonomies ) {
70
+ taxonomies = select( coreStore ).getTaxonomies( {
71
+ type: postType,
72
+ per_page: -1,
73
+ } );
74
+ }
75
+ return {
76
+ post: _post,
77
+ isPostTypeHierarchical: postTypeObject?.hierarchical,
78
+ hasTermsAssigned:
79
+ _post &&
80
+ ( taxonomies || [] )
81
+ .filter(
82
+ ( { visibility } ) =>
83
+ visibility?.publicly_queryable
84
+ )
85
+ .some( ( taxonomy ) => {
86
+ return !! _post[ taxonomy.rest_base ]?.length;
87
+ } ),
88
+ isLoading:
89
+ ! _post ||
90
+ ! postTypeObject ||
91
+ ( postTypeHasTaxonomies && ! taxonomies ),
92
+ };
93
+ },
94
+ [ postType, postId ]
95
+ );
96
+
97
+ // Counter used to cache-bust `useServerSideRender`
98
+ //
99
+ // This is a catch-all signal to re-render the block when a post's title,
100
+ // parent ID, or terms change.
101
+ //
102
+ // This is fundamentally imperfect, because there are other entities which
103
+ // could change in the meantime (the titles of ancestor posts, or the
104
+ // labels of taxonomy terms), hence the choice to re-render systematically
105
+ // upon saving.
106
+ const [ invalidationKey, setInvalidationKey ] = useState( 0 );
107
+ useEffect( () => {
108
+ setInvalidationKey( ( c ) => c + 1 );
109
+ }, [ post ] );
110
+
38
111
  const blockProps = useBlockProps();
39
112
  const dropdownMenuProps = useToolsPanelDropdownMenuProps();
40
113
  const { content } = useServerSideRender( {
41
114
  attributes,
42
115
  skipBlockSupportAttributes: true,
43
116
  block: 'core/breadcrumbs',
44
- urlQueryArgs: { post_id: postId },
117
+ urlQueryArgs: { post_id: postId, invalidationKey },
45
118
  } );
119
+
120
+ if ( isLoading ) {
121
+ return (
122
+ <div { ...blockProps }>
123
+ <Spinner />
124
+ </div>
125
+ );
126
+ }
127
+ // TODO: this should be handled better when we add more types.
128
+ let breadcrumbsType;
129
+ const isSpecificSupportedTypeSet = [
130
+ 'postWithAncestors',
131
+ 'postWithTerms',
132
+ ].includes( type );
133
+ if ( isSpecificSupportedTypeSet ) {
134
+ breadcrumbsType = type;
135
+ } else {
136
+ breadcrumbsType = isPostTypeHierarchical
137
+ ? 'postWithAncestors'
138
+ : 'postWithTerms';
139
+ }
46
140
  let placeholder = null;
47
- // If no post context or the post type is not hierarchical, show placeholder.
48
141
  // This is fragile because this block is server side rendered and we'll have to
49
142
  // update the placeholder html if the server side rendering output changes.
50
- if ( ! postId || ! postType || ! isPostTypeHierarchical ) {
143
+ const showPlaceholder =
144
+ ! postId ||
145
+ ! postType ||
146
+ // When `templateSlug` is set only show placeholder if the post type is not.
147
+ // This is needed because when we are showing the template in post editor we
148
+ // want to show the real breadcrumbs if we have the post type.
149
+ ( templateSlug && ! postType ) ||
150
+ ( breadcrumbsType === 'postWithAncestors' &&
151
+ ! isPostTypeHierarchical ) ||
152
+ ( breadcrumbsType === 'postWithTerms' && ! hasTermsAssigned );
153
+ if ( showPlaceholder ) {
51
154
  const placeholderItems = [
52
155
  showHomeLink && __( 'Home' ),
53
- __( 'Ancestor' ),
54
- __( 'Parent' ),
156
+ // For now if we are adding this in a template show a generic placeholder.
157
+ ...( templateSlug && ! isSpecificSupportedTypeSet
158
+ ? [ __( 'Page' ) ]
159
+ : BREADCRUMB_TYPES[ breadcrumbsType ].placeholderItems ),
55
160
  ].filter( Boolean );
56
161
  placeholder = (
57
162
  <nav
@@ -84,10 +189,46 @@ export default function BreadcrumbEdit( {
84
189
  setAttributes( {
85
190
  separator: separatorDefaultValue,
86
191
  showHomeLink: true,
192
+ type: typeDefaultValue,
87
193
  } );
88
194
  } }
89
195
  dropdownMenuProps={ dropdownMenuProps }
90
196
  >
197
+ <ToolsPanelItem
198
+ label={ __( 'Type' ) }
199
+ isShownByDefault
200
+ hasValue={ () => type !== typeDefaultValue }
201
+ onDeselect={ () =>
202
+ setAttributes( {
203
+ type: typeDefaultValue,
204
+ } )
205
+ }
206
+ >
207
+ <SelectControl
208
+ __nextHasNoMarginBottom
209
+ __next40pxDefaultSize
210
+ label={ __( 'Type' ) }
211
+ value={ type }
212
+ onChange={ ( value ) =>
213
+ setAttributes( { type: value } )
214
+ }
215
+ options={ [
216
+ {
217
+ label: __( 'Auto' ),
218
+ value: 'auto',
219
+ },
220
+ {
221
+ label: __( 'Post with ancestors' ),
222
+ value: 'postWithAncestors',
223
+ },
224
+ {
225
+ label: __( 'Post with terms' ),
226
+ value: 'postWithTerms',
227
+ },
228
+ ] }
229
+ help={ BREADCRUMB_TYPES[ type ].help }
230
+ />
231
+ </ToolsPanelItem>
91
232
  <ToolsPanelItem
92
233
  label={ __( 'Show home link' ) }
93
234
  isShownByDefault
@@ -138,7 +279,11 @@ export default function BreadcrumbEdit( {
138
279
  </ToolsPanel>
139
280
  </InspectorControls>
140
281
  <div { ...blockProps }>
141
- { placeholder || <RawHTML inert="true">{ content }</RawHTML> }
282
+ { showPlaceholder ? (
283
+ placeholder
284
+ ) : (
285
+ <RawHTML inert="true">{ content }</RawHTML>
286
+ ) }
142
287
  </div>
143
288
  </>
144
289
  );
@@ -17,6 +17,11 @@
17
17
  * @return string Returns the post breadcrumb for hierarchical post types.
18
18
  */
19
19
  function render_block_core_breadcrumbs( $attributes, $content, $block ) {
20
+ // Exclude breadcrumbs from special contexts like archives, search, 404, etc.
21
+ // until they are explicitly supported.
22
+ if ( is_archive() || is_search() || is_404() || is_home() || is_front_page() ) {
23
+ return '';
24
+ }
20
25
  if ( ! isset( $block->context['postId'] ) || ! isset( $block->context['postType'] ) ) {
21
26
  return '';
22
27
  }
@@ -24,16 +29,12 @@ function render_block_core_breadcrumbs( $attributes, $content, $block ) {
24
29
  $post_id = $block->context['postId'];
25
30
  $post_type = $block->context['postType'];
26
31
 
27
- // Only render for hierarchical post types.
28
- if ( ! is_post_type_hierarchical( $post_type ) ) {
29
- return '';
30
- }
31
-
32
32
  $post = get_post( $post_id );
33
33
  if ( ! $post ) {
34
34
  return '';
35
35
  }
36
36
 
37
+ $type = $attributes['type'];
37
38
  $breadcrumb_items = array();
38
39
  if ( $attributes['showHomeLink'] ) {
39
40
  $breadcrumb_items[] = sprintf(
@@ -42,18 +43,14 @@ function render_block_core_breadcrumbs( $attributes, $content, $block ) {
42
43
  esc_html__( 'Home' )
43
44
  );
44
45
  }
45
-
46
- $ancestors = get_post_ancestors( $post_id );
47
- $ancestors = array_reverse( $ancestors );
48
-
49
- foreach ( $ancestors as $ancestor_id ) {
50
- $breadcrumb_items[] = sprintf(
51
- '<a href="%s">%s</a>',
52
- esc_url( get_permalink( $ancestor_id ) ),
53
- get_the_title( $ancestor_id )
54
- );
46
+ $supported_types = array( 'postWithAncestors', 'postWithTerms' );
47
+ // If `type` is not set to a specific breadcrumb type, determine it based on the block's default heuristics.
48
+ $breadcrumbs_type = in_array( $type, $supported_types, true ) ? $type : block_core_breadcrumbs_get_breadcrumbs_type( $post_type );
49
+ if ( 'postWithAncestors' === $breadcrumbs_type ) {
50
+ $breadcrumb_items = array_merge( $breadcrumb_items, block_core_breadcrumbs_get_hierarchical_post_type_breadcrumbs( $post_id ) );
51
+ } else {
52
+ $breadcrumb_items = array_merge( $breadcrumb_items, block_core_breadcrumbs_get_terms_breadcrumbs( $post_id, $post_type ) );
55
53
  }
56
-
57
54
  // Add current post title (not linked).
58
55
  $breadcrumb_items[] = sprintf( '<span aria-current="page">%s</span>', get_the_title( $post ) );
59
56
  $wrapper_attributes = get_block_wrapper_attributes(
@@ -80,6 +77,111 @@ function render_block_core_breadcrumbs( $attributes, $content, $block ) {
80
77
  return $breadcrumb_html;
81
78
  }
82
79
 
80
+ /**
81
+ * Determines the breadcrumb type based on the block's default heuristics.
82
+ *
83
+ * @since 6.9.0
84
+ *
85
+ * @param string $post_type The post type name.
86
+ *
87
+ * @return string The breadcrumb type.
88
+ */
89
+ function block_core_breadcrumbs_get_breadcrumbs_type( $post_type ) {
90
+ return is_post_type_hierarchical( $post_type ) ? 'postWithAncestors' : 'postWithTerms';
91
+ }
92
+
93
+ /**
94
+ * Generates breadcrumb items from hierarchical post type ancestors.
95
+ *
96
+ * @since 6.9.0
97
+ *
98
+ * @param int $post_id The post ID.
99
+ *
100
+ * @return array Array of breadcrumb HTML items.
101
+ */
102
+ function block_core_breadcrumbs_get_hierarchical_post_type_breadcrumbs( $post_id ) {
103
+ $breadcrumb_items = array();
104
+ $ancestors = get_post_ancestors( $post_id );
105
+ $ancestors = array_reverse( $ancestors );
106
+
107
+ foreach ( $ancestors as $ancestor_id ) {
108
+ $breadcrumb_items[] = sprintf(
109
+ '<a href="%s">%s</a>',
110
+ esc_url( get_permalink( $ancestor_id ) ),
111
+ get_the_title( $ancestor_id )
112
+ );
113
+ }
114
+ return $breadcrumb_items;
115
+ }
116
+
117
+
118
+ /**
119
+ * Generates breadcrumb items from taxonomy terms.
120
+ *
121
+ * Finds the first publicly queryable taxonomy with terms assigned to the post
122
+ * and generates breadcrumb links, including hierarchical term ancestors if applicable.
123
+ *
124
+ * @since 6.9.0
125
+ *
126
+ * @param int $post_id The post ID.
127
+ * @param string $post_type The post type name.
128
+ *
129
+ * @return array Array of breadcrumb HTML items.
130
+ */
131
+ function block_core_breadcrumbs_get_terms_breadcrumbs( $post_id, $post_type ) {
132
+ $breadcrumb_items = array();
133
+ // Get public taxonomies for this post type.
134
+ $taxonomies = wp_filter_object_list(
135
+ get_object_taxonomies( $post_type, 'objects' ),
136
+ array(
137
+ 'publicly_queryable' => true,
138
+ 'show_in_rest' => true,
139
+ )
140
+ );
141
+
142
+ if ( empty( $taxonomies ) ) {
143
+ return array();
144
+ }
145
+
146
+ // Find the first taxonomy that has terms assigned to this post.
147
+ $taxonomy_name = null;
148
+ $terms = array();
149
+ foreach ( $taxonomies as $taxonomy ) {
150
+ $post_terms = get_the_terms( $post_id, $taxonomy->name );
151
+ if ( ! empty( $post_terms ) && ! is_wp_error( $post_terms ) ) {
152
+ $taxonomy_name = $taxonomy->name;
153
+ $terms = $post_terms;
154
+ break;
155
+ }
156
+ }
157
+
158
+ if ( ! empty( $terms ) ) {
159
+ // Use the first term (if multiple are assigned).
160
+ $term = reset( $terms );
161
+ // Check if taxonomy is hierarchical also add ancestor term links
162
+ if ( is_taxonomy_hierarchical( $taxonomy_name ) ) {
163
+ $term_ancestors = get_ancestors( $term->term_id, $taxonomy_name, 'taxonomy' );
164
+ $term_ancestors = array_reverse( $term_ancestors );
165
+ foreach ( $term_ancestors as $ancestor_id ) {
166
+ $ancestor_term = get_term( $ancestor_id, $taxonomy_name );
167
+ if ( $ancestor_term && ! is_wp_error( $ancestor_term ) ) {
168
+ $breadcrumb_items[] = sprintf(
169
+ '<a href="%s">%s</a>',
170
+ esc_url( get_term_link( $ancestor_term ) ),
171
+ esc_html( $ancestor_term->name )
172
+ );
173
+ }
174
+ }
175
+ }
176
+ $breadcrumb_items[] = sprintf(
177
+ '<a href="%s">%s</a>',
178
+ esc_url( get_term_link( $term ) ),
179
+ esc_html( $term->name )
180
+ );
181
+ }
182
+ return $breadcrumb_items;
183
+ }
184
+
83
185
  /**
84
186
  * Registers the `core/breadcrumbs` block on the server.
85
187
  *
@@ -7,7 +7,7 @@ import { __unstableCreateElement as createElement } from '@wordpress/rich-text';
7
7
  /**
8
8
  * Internal dependencies
9
9
  */
10
- import { getTransformedMetadata } from '../utils/get-transformed-metadata';
10
+ import { getTransformedAttributes } from '../utils/get-transformed-attributes';
11
11
 
12
12
  const transforms = {
13
13
  from: [
@@ -38,7 +38,7 @@ const transforms = {
38
38
  {},
39
39
  // Loop the selected buttons.
40
40
  buttons.map( ( attributes ) => {
41
- const { content, metadata } = attributes;
41
+ const { content } = attributes;
42
42
  const element = createElement( document, content );
43
43
  // Remove any HTML tags.
44
44
  const text = element.innerText || '';
@@ -47,15 +47,15 @@ const transforms = {
47
47
  const url = link?.getAttribute( 'href' );
48
48
  // Create singular button in the buttons block.
49
49
  return createBlock( 'core/button', {
50
- text,
51
- url,
52
- metadata: getTransformedMetadata(
53
- metadata,
50
+ ...getTransformedAttributes(
51
+ attributes,
54
52
  'core/button',
55
53
  ( { content: contentBinding } ) => ( {
56
54
  text: contentBinding,
57
55
  } )
58
56
  ),
57
+ text,
58
+ url,
59
59
  } );
60
60
  } )
61
61
  ),
@@ -7,7 +7,7 @@ import { create, toHTMLString } from '@wordpress/rich-text';
7
7
  /**
8
8
  * Internal dependencies
9
9
  */
10
- import { getTransformedMetadata } from '../utils/get-transformed-metadata';
10
+ import { getTransformedAttributes } from '../utils/get-transformed-attributes';
11
11
 
12
12
  const transforms = {
13
13
  from: [
@@ -19,21 +19,24 @@ const transforms = {
19
19
  {
20
20
  type: 'block',
21
21
  blocks: [ 'core/paragraph' ],
22
- transform: ( { content, metadata } ) =>
23
- createBlock( 'core/code', {
22
+ transform: ( attributes ) => {
23
+ const { content } = attributes;
24
+ return createBlock( 'core/code', {
25
+ ...getTransformedAttributes( attributes, 'core/code' ),
24
26
  content,
25
- metadata: getTransformedMetadata( metadata, 'core/code' ),
26
- } ),
27
+ } );
28
+ },
27
29
  },
28
30
  {
29
31
  type: 'block',
30
32
  blocks: [ 'core/html' ],
31
- transform: ( { content: text, metadata } ) => {
33
+ transform: ( attributes ) => {
34
+ const { content: text } = attributes;
32
35
  return createBlock( 'core/code', {
36
+ ...getTransformedAttributes( attributes, 'core/code' ),
33
37
  // The HTML is plain text (with plain line breaks), so
34
38
  // convert it to rich text.
35
39
  content: toHTMLString( { value: create( { text } ) } ),
36
- metadata: getTransformedMetadata( metadata, 'core/code' ),
37
40
  } );
38
41
  },
39
42
  },
@@ -60,14 +63,13 @@ const transforms = {
60
63
  {
61
64
  type: 'block',
62
65
  blocks: [ 'core/paragraph' ],
63
- transform: ( { content, metadata } ) =>
64
- createBlock( 'core/paragraph', {
66
+ transform: ( attributes ) => {
67
+ const { content } = attributes;
68
+ return createBlock( 'core/paragraph', {
69
+ ...getTransformedAttributes( attributes, 'core/paragraph' ),
65
70
  content,
66
- metadata: getTransformedMetadata(
67
- metadata,
68
- 'core/paragraph'
69
- ),
70
- } ),
71
+ } );
72
+ },
71
73
  },
72
74
  ],
73
75
  };