@wordpress/block-library 9.30.0 → 9.30.1-next.6f42e1382.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 (189) hide show
  1. package/build/accordion-content/edit.js +8 -8
  2. package/build/accordion-content/edit.js.map +1 -1
  3. package/build/accordion-content/index.js +2 -1
  4. package/build/accordion-content/index.js.map +1 -1
  5. package/build/accordion-panel/index.js +2 -1
  6. package/build/accordion-panel/index.js.map +1 -1
  7. package/build/buttons/index.js +2 -1
  8. package/build/buttons/index.js.map +1 -1
  9. package/build/categories/edit.js +3 -1
  10. package/build/categories/edit.js.map +1 -1
  11. package/build/index.js +4 -0
  12. package/build/index.js.map +1 -1
  13. package/build/navigation/constants.js +5 -1
  14. package/build/navigation/constants.js.map +1 -1
  15. package/build/navigation/edit/index.js +45 -1
  16. package/build/navigation/edit/index.js.map +1 -1
  17. package/build/navigation/edit/leaf-more-menu.js +0 -1
  18. package/build/navigation/edit/leaf-more-menu.js.map +1 -1
  19. package/build/navigation/edit/menu-inspector-controls.js +40 -5
  20. package/build/navigation/edit/menu-inspector-controls.js.map +1 -1
  21. package/build/navigation-link/block-inserter.js +69 -0
  22. package/build/navigation-link/block-inserter.js.map +1 -0
  23. package/build/navigation-link/dialog-wrapper.js +80 -0
  24. package/build/navigation-link/dialog-wrapper.js.map +1 -0
  25. package/build/navigation-link/link-ui.js +80 -120
  26. package/build/navigation-link/link-ui.js.map +1 -1
  27. package/build/navigation-link/page-creator.js +137 -0
  28. package/build/navigation-link/page-creator.js.map +1 -0
  29. package/build/search/edit.js +22 -14
  30. package/build/search/edit.js.map +1 -1
  31. package/build/social-links/index.js +2 -1
  32. package/build/social-links/index.js.map +1 -1
  33. package/build/table-of-contents/edit.js +33 -9
  34. package/build/table-of-contents/edit.js.map +1 -1
  35. package/build/table-of-contents/index.js +4 -0
  36. package/build/table-of-contents/index.js.map +1 -1
  37. package/build/table-of-contents/list.js +6 -3
  38. package/build/table-of-contents/list.js.map +1 -1
  39. package/build/table-of-contents/save.js +6 -3
  40. package/build/table-of-contents/save.js.map +1 -1
  41. package/build/term-template/edit.js +318 -0
  42. package/build/term-template/edit.js.map +1 -0
  43. package/build/term-template/index.js +109 -0
  44. package/build/term-template/index.js.map +1 -0
  45. package/build/term-template/save.js +16 -0
  46. package/build/term-template/save.js.map +1 -0
  47. package/build/term-template/variations.js +83 -0
  48. package/build/term-template/variations.js.map +1 -0
  49. package/build/terms-query/edit.js +20 -0
  50. package/build/terms-query/edit.js.map +1 -0
  51. package/build/terms-query/index.js +83 -0
  52. package/build/terms-query/index.js.map +1 -0
  53. package/build/terms-query/inspector-controls.js +246 -0
  54. package/build/terms-query/inspector-controls.js.map +1 -0
  55. package/build/terms-query/save.js +24 -0
  56. package/build/terms-query/save.js.map +1 -0
  57. package/build/terms-query/terms-query-content.js +71 -0
  58. package/build/terms-query/terms-query-content.js.map +1 -0
  59. package/build-module/accordion-content/edit.js +8 -8
  60. package/build-module/accordion-content/edit.js.map +1 -1
  61. package/build-module/accordion-content/index.js +2 -1
  62. package/build-module/accordion-content/index.js.map +1 -1
  63. package/build-module/accordion-panel/index.js +2 -1
  64. package/build-module/accordion-panel/index.js.map +1 -1
  65. package/build-module/buttons/index.js +2 -1
  66. package/build-module/buttons/index.js.map +1 -1
  67. package/build-module/categories/edit.js +3 -1
  68. package/build-module/categories/edit.js.map +1 -1
  69. package/build-module/index.js +4 -0
  70. package/build-module/index.js.map +1 -1
  71. package/build-module/navigation/constants.js +5 -1
  72. package/build-module/navigation/constants.js.map +1 -1
  73. package/build-module/navigation/edit/index.js +50 -4
  74. package/build-module/navigation/edit/index.js.map +1 -1
  75. package/build-module/navigation/edit/leaf-more-menu.js +0 -1
  76. package/build-module/navigation/edit/leaf-more-menu.js.map +1 -1
  77. package/build-module/navigation/edit/menu-inspector-controls.js +40 -5
  78. package/build-module/navigation/edit/menu-inspector-controls.js.map +1 -1
  79. package/build-module/navigation-link/block-inserter.js +61 -0
  80. package/build-module/navigation-link/block-inserter.js.map +1 -0
  81. package/build-module/navigation-link/dialog-wrapper.js +75 -0
  82. package/build-module/navigation-link/dialog-wrapper.js.map +1 -0
  83. package/build-module/navigation-link/link-ui.js +85 -125
  84. package/build-module/navigation-link/link-ui.js.map +1 -1
  85. package/build-module/navigation-link/page-creator.js +130 -0
  86. package/build-module/navigation-link/page-creator.js.map +1 -0
  87. package/build-module/search/edit.js +22 -14
  88. package/build-module/search/edit.js.map +1 -1
  89. package/build-module/social-links/index.js +2 -1
  90. package/build-module/social-links/index.js.map +1 -1
  91. package/build-module/table-of-contents/edit.js +35 -11
  92. package/build-module/table-of-contents/edit.js.map +1 -1
  93. package/build-module/table-of-contents/index.js +4 -0
  94. package/build-module/table-of-contents/index.js.map +1 -1
  95. package/build-module/table-of-contents/list.js +6 -3
  96. package/build-module/table-of-contents/list.js.map +1 -1
  97. package/build-module/table-of-contents/save.js +6 -3
  98. package/build-module/table-of-contents/save.js.map +1 -1
  99. package/build-module/term-template/edit.js +310 -0
  100. package/build-module/term-template/edit.js.map +1 -0
  101. package/build-module/term-template/index.js +102 -0
  102. package/build-module/term-template/index.js.map +1 -0
  103. package/build-module/term-template/save.js +9 -0
  104. package/build-module/term-template/save.js.map +1 -0
  105. package/build-module/term-template/variations.js +76 -0
  106. package/build-module/term-template/variations.js.map +1 -0
  107. package/build-module/terms-query/edit.js +12 -0
  108. package/build-module/terms-query/edit.js.map +1 -0
  109. package/build-module/terms-query/index.js +76 -0
  110. package/build-module/terms-query/index.js.map +1 -0
  111. package/build-module/terms-query/inspector-controls.js +239 -0
  112. package/build-module/terms-query/inspector-controls.js.map +1 -0
  113. package/build-module/terms-query/save.js +17 -0
  114. package/build-module/terms-query/save.js.map +1 -0
  115. package/build-module/terms-query/terms-query-content.js +63 -0
  116. package/build-module/terms-query/terms-query-content.js.map +1 -0
  117. package/build-style/accordion/style-rtl.css +5 -6
  118. package/build-style/accordion/style.css +5 -6
  119. package/build-style/editor-rtl.css +38 -0
  120. package/build-style/editor.css +38 -0
  121. package/build-style/form-input/style-rtl.css +4 -3
  122. package/build-style/form-input/style.css +4 -3
  123. package/build-style/navigation-link/editor-rtl.css +14 -0
  124. package/build-style/navigation-link/editor.css +14 -0
  125. package/build-style/navigation-link/style-rtl.css +1 -1
  126. package/build-style/navigation-link/style.css +1 -1
  127. package/build-style/post-comments-form/style-rtl.css +8 -5
  128. package/build-style/post-comments-form/style.css +8 -5
  129. package/build-style/search/style-rtl.css +11 -12
  130. package/build-style/search/style.css +11 -12
  131. package/build-style/style-rtl.css +45 -27
  132. package/build-style/style.css +45 -27
  133. package/build-style/term-template/editor-rtl.css +160 -0
  134. package/build-style/term-template/editor.css +160 -0
  135. package/build-style/term-template/style-rtl.css +146 -0
  136. package/build-style/term-template/style.css +146 -0
  137. package/build-style/terms-query/style-rtl.css +140 -0
  138. package/build-style/terms-query/style.css +140 -0
  139. package/build-types/table-of-contents/list.d.ts +2 -1
  140. package/build-types/table-of-contents/list.d.ts.map +1 -1
  141. package/package.json +35 -35
  142. package/src/accordion/style.scss +6 -6
  143. package/src/accordion-content/block.json +2 -1
  144. package/src/accordion-content/edit.js +21 -27
  145. package/src/accordion-panel/block.json +2 -1
  146. package/src/buttons/block.json +2 -1
  147. package/src/categories/edit.js +2 -1
  148. package/src/cover/test/edit.js +1 -5
  149. package/src/editor.scss +1 -0
  150. package/src/form-input/style.scss +3 -2
  151. package/src/index.js +4 -0
  152. package/src/navigation/constants.js +4 -0
  153. package/src/navigation/edit/index.js +50 -1
  154. package/src/navigation/edit/leaf-more-menu.js +0 -1
  155. package/src/navigation/edit/menu-inspector-controls.js +40 -5
  156. package/src/navigation-link/block-inserter.js +65 -0
  157. package/src/navigation-link/dialog-wrapper.js +74 -0
  158. package/src/navigation-link/editor.scss +17 -0
  159. package/src/navigation-link/link-ui.js +108 -164
  160. package/src/navigation-link/page-creator.js +157 -0
  161. package/src/navigation-link/style.scss +1 -1
  162. package/src/post-comments-form/style.scss +11 -11
  163. package/src/post-date/index.php +18 -13
  164. package/src/search/edit.js +44 -13
  165. package/src/search/index.php +16 -2
  166. package/src/search/style.scss +15 -16
  167. package/src/social-links/block.json +2 -1
  168. package/src/style.scss +2 -0
  169. package/src/table-of-contents/block.json +4 -0
  170. package/src/table-of-contents/edit.js +58 -21
  171. package/src/table-of-contents/list.tsx +7 -2
  172. package/src/table-of-contents/save.js +7 -3
  173. package/src/term-template/block.json +73 -0
  174. package/src/term-template/edit.js +391 -0
  175. package/src/term-template/editor.scss +26 -0
  176. package/src/term-template/index.js +26 -0
  177. package/src/term-template/index.php +224 -0
  178. package/src/term-template/save.js +8 -0
  179. package/src/term-template/style.scss +12 -0
  180. package/src/term-template/variations.js +87 -0
  181. package/src/terms-query/block.json +49 -0
  182. package/src/terms-query/edit.js +10 -0
  183. package/src/terms-query/index.js +24 -0
  184. package/src/terms-query/index.php +44 -0
  185. package/src/terms-query/inspector-controls.js +233 -0
  186. package/src/terms-query/save.js +10 -0
  187. package/src/terms-query/style.scss +6 -0
  188. package/src/terms-query/terms-query-content.js +74 -0
  189. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,391 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { memo, useMemo, useState } from '@wordpress/element';
10
+ import { useSelect, useDispatch } from '@wordpress/data';
11
+ import { layout } from '@wordpress/icons';
12
+ import { __ } from '@wordpress/i18n';
13
+ import {
14
+ BlockContextProvider,
15
+ __experimentalUseBlockPreview as useBlockPreview,
16
+ __experimentalBlockVariationPicker as BlockVariationPicker,
17
+ useBlockProps,
18
+ useInnerBlocksProps,
19
+ store as blockEditorStore,
20
+ } from '@wordpress/block-editor';
21
+ import { useEntityRecords } from '@wordpress/core-data';
22
+ import {
23
+ createBlocksFromInnerBlocksTemplate,
24
+ store as blocksStore,
25
+ } from '@wordpress/blocks';
26
+
27
+ const TEMPLATE = [
28
+ [
29
+ 'core/group',
30
+ {
31
+ layout: {
32
+ type: 'flex',
33
+ orientation: 'horizontal',
34
+ },
35
+ style: {
36
+ spacing: {
37
+ blockGap: '0.5rem',
38
+ },
39
+ },
40
+ metadata: {
41
+ name: __( 'Term Name with Count' ),
42
+ },
43
+ },
44
+ [
45
+ [
46
+ 'core/paragraph',
47
+ {
48
+ metadata: {
49
+ name: __( 'Term Name' ),
50
+ bindings: {
51
+ content: {
52
+ source: 'core/term-data',
53
+ args: {
54
+ key: 'name',
55
+ },
56
+ },
57
+ },
58
+ },
59
+ },
60
+ ],
61
+ [
62
+ 'core/paragraph',
63
+ {
64
+ placeholder: __( '(count)' ),
65
+ metadata: {
66
+ name: __( 'Term Count' ),
67
+ bindings: {
68
+ content: {
69
+ source: 'core/term-data',
70
+ args: {
71
+ key: 'count',
72
+ },
73
+ },
74
+ },
75
+ },
76
+ },
77
+ ],
78
+ ],
79
+ ],
80
+ ];
81
+
82
+ function TermTemplateInnerBlocks( { classList } ) {
83
+ const innerBlocksProps = useInnerBlocksProps(
84
+ { className: clsx( 'wp-block-term', classList ) },
85
+ { template: TEMPLATE, __unstableDisableLayoutClassNames: true }
86
+ );
87
+ return <li { ...innerBlocksProps } />;
88
+ }
89
+
90
+ function TermTemplateBlockPreview( {
91
+ blocks,
92
+ blockContextId,
93
+ classList,
94
+ isHidden,
95
+ setActiveBlockContextId,
96
+ } ) {
97
+ const blockPreviewProps = useBlockPreview( {
98
+ blocks,
99
+ props: {
100
+ className: clsx( 'wp-block-term', classList ),
101
+ },
102
+ } );
103
+
104
+ const handleOnClick = () => {
105
+ setActiveBlockContextId( blockContextId );
106
+ };
107
+
108
+ const style = {
109
+ display: isHidden ? 'none' : undefined,
110
+ };
111
+
112
+ return (
113
+ <li
114
+ { ...blockPreviewProps }
115
+ tabIndex={ 0 }
116
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-to-interactive-role
117
+ role="button"
118
+ onClick={ handleOnClick }
119
+ onKeyPress={ handleOnClick }
120
+ style={ style }
121
+ />
122
+ );
123
+ }
124
+
125
+ // Prevent re-rendering of the block preview when the terms data changes.
126
+ const MemoizedTermTemplateBlockPreview = memo( TermTemplateBlockPreview );
127
+
128
+ /**
129
+ * Builds a hierarchical tree structure from flat terms array.
130
+ *
131
+ * @param {Array} terms Array of term objects.
132
+ * @return {Array} Tree structure with parent/child relationships.
133
+ */
134
+ function buildTermsTree( terms ) {
135
+ const termsById = {};
136
+ const rootTerms = [];
137
+
138
+ terms.forEach( ( term ) => {
139
+ termsById[ term.id ] = {
140
+ term,
141
+ children: [],
142
+ };
143
+ } );
144
+
145
+ terms.forEach( ( term ) => {
146
+ if ( term.parent && termsById[ term.parent ] ) {
147
+ termsById[ term.parent ].children.push( termsById[ term.id ] );
148
+ } else {
149
+ rootTerms.push( termsById[ term.id ] );
150
+ }
151
+ } );
152
+
153
+ return rootTerms;
154
+ }
155
+
156
+ /**
157
+ * Renders a single term node and its children recursively.
158
+ *
159
+ * @param {Object} termNode Term node with term object and children.
160
+ * @param {Function} renderTerm Function to render individual terms.
161
+ * @return {JSX.Element} Rendered term node with children.
162
+ */
163
+ function renderTermNode( termNode, renderTerm ) {
164
+ return (
165
+ <li className="wp-block-term">
166
+ { renderTerm( termNode.term ) }
167
+ { termNode.children.length > 0 && (
168
+ <ul>
169
+ { termNode.children.map( ( child ) =>
170
+ renderTermNode( child, renderTerm )
171
+ ) }
172
+ </ul>
173
+ ) }
174
+ </li>
175
+ );
176
+ }
177
+
178
+ /**
179
+ * Checks if a term is the currently active term.
180
+ *
181
+ * @param {number} termId The term ID to check.
182
+ * @param {number} activeBlockContextId The currently active block context ID.
183
+ * @param {Array} blockContexts Array of block contexts.
184
+ * @return {boolean} True if the term is active, false otherwise.
185
+ */
186
+ function isActiveTerm( termId, activeBlockContextId, blockContexts ) {
187
+ return termId === ( activeBlockContextId || blockContexts[ 0 ]?.termId );
188
+ }
189
+
190
+ export default function TermTemplateEdit( {
191
+ clientId,
192
+ setAttributes,
193
+ context: {
194
+ termQuery: {
195
+ taxonomy,
196
+ order,
197
+ orderBy,
198
+ hideEmpty,
199
+ hierarchical,
200
+ parent,
201
+ perPage = 10,
202
+ } = {},
203
+ },
204
+ __unstableLayoutClassNames,
205
+ } ) {
206
+ const [ activeBlockContextId, setActiveBlockContextId ] = useState();
207
+ const { replaceInnerBlocks } = useDispatch( blockEditorStore );
208
+
209
+ const queryArgs = {
210
+ order,
211
+ orderby: orderBy,
212
+ hide_empty: hideEmpty,
213
+ // To preview the data the closest to the frontend, we fetch the largest number of terms
214
+ // and limit them during rendering. This is because WP_Term_Query fetches data in hierarchical manner,
215
+ // while in editor we build the hierarchy manually. It also allows us to avoid re-fetching data when max terms changes.
216
+ per_page: 100,
217
+ };
218
+
219
+ const { records: terms, isResolving } = useEntityRecords(
220
+ 'taxonomy',
221
+ taxonomy,
222
+ queryArgs
223
+ );
224
+
225
+ // Filter to show only top-level terms if "Show only top-level terms" is enabled.
226
+ const filteredTerms = useMemo( () => {
227
+ if ( ! terms || parent !== 0 ) {
228
+ return terms;
229
+ }
230
+ return terms.filter( ( term ) => ! term.parent );
231
+ }, [ terms, parent ] );
232
+
233
+ const { blocks, variations, defaultVariation } = useSelect(
234
+ ( select ) => {
235
+ const { getBlocks } = select( blockEditorStore );
236
+ const { getBlockVariations, getDefaultBlockVariation } =
237
+ select( blocksStore );
238
+
239
+ return {
240
+ blocks: getBlocks( clientId ),
241
+ variations: getBlockVariations( 'core/term-template', 'block' ),
242
+ defaultVariation: getDefaultBlockVariation(
243
+ 'core/term-template',
244
+ 'block'
245
+ ),
246
+ };
247
+ },
248
+ [ clientId ]
249
+ );
250
+
251
+ const blockProps = useBlockProps( {
252
+ className: __unstableLayoutClassNames,
253
+ } );
254
+
255
+ const blockContexts = useMemo(
256
+ () =>
257
+ filteredTerms?.map( ( term ) => ( {
258
+ taxonomy,
259
+ termId: term.id,
260
+ classList: `term-${ term.id }`,
261
+ termData: term,
262
+ } ) ),
263
+ [ filteredTerms, taxonomy ]
264
+ );
265
+
266
+ // Show variation picker if no blocks exist.
267
+ if ( ! blocks?.length ) {
268
+ return (
269
+ <div { ...blockProps }>
270
+ <BlockVariationPicker
271
+ icon={ layout }
272
+ label={ __( 'Term Template' ) }
273
+ variations={ variations }
274
+ instructions={ __(
275
+ 'Choose a layout for displaying terms:'
276
+ ) }
277
+ onSelect={ ( nextVariation = defaultVariation ) => {
278
+ if ( nextVariation.attributes ) {
279
+ setAttributes( nextVariation.attributes );
280
+ }
281
+ if ( nextVariation.innerBlocks ) {
282
+ replaceInnerBlocks(
283
+ clientId,
284
+ createBlocksFromInnerBlocksTemplate(
285
+ nextVariation.innerBlocks
286
+ ),
287
+ true
288
+ );
289
+ }
290
+ } }
291
+ allowSkip
292
+ />
293
+ </div>
294
+ );
295
+ }
296
+
297
+ if ( isResolving ) {
298
+ return (
299
+ <ul { ...blockProps }>
300
+ <li className="wp-block-term term-loading">
301
+ <div className="term-loading-placeholder" />
302
+ </li>
303
+ <li className="wp-block-term term-loading">
304
+ <div className="term-loading-placeholder" />
305
+ </li>
306
+ <li className="wp-block-term term-loading">
307
+ <div className="term-loading-placeholder" />
308
+ </li>
309
+ </ul>
310
+ );
311
+ }
312
+
313
+ if ( ! filteredTerms?.length ) {
314
+ return <p { ...blockProps }> { __( 'No terms found.' ) }</p>;
315
+ }
316
+
317
+ const renderTerm = ( term ) => {
318
+ const blockContext = {
319
+ taxonomy,
320
+ termId: term.id,
321
+ classList: `term-${ term.id }`,
322
+ termData: term,
323
+ };
324
+
325
+ return (
326
+ <BlockContextProvider key={ term.id } value={ blockContext }>
327
+ { isActiveTerm(
328
+ term.id,
329
+ activeBlockContextId,
330
+ blockContexts
331
+ ) ? (
332
+ <TermTemplateInnerBlocks
333
+ classList={ blockContext.classList }
334
+ />
335
+ ) : null }
336
+ <MemoizedTermTemplateBlockPreview
337
+ blocks={ blocks }
338
+ blockContextId={ term.id }
339
+ classList={ blockContext.classList }
340
+ setActiveBlockContextId={ setActiveBlockContextId }
341
+ isHidden={ isActiveTerm(
342
+ term.id,
343
+ activeBlockContextId,
344
+ blockContexts
345
+ ) }
346
+ />
347
+ </BlockContextProvider>
348
+ );
349
+ };
350
+
351
+ return (
352
+ <>
353
+ <ul { ...blockProps }>
354
+ { hierarchical
355
+ ? buildTermsTree( filteredTerms ).map( ( termNode ) =>
356
+ renderTermNode( termNode, renderTerm )
357
+ )
358
+ : blockContexts &&
359
+ blockContexts
360
+ .slice( 0, perPage )
361
+ .map( ( blockContext ) => (
362
+ <BlockContextProvider
363
+ key={ blockContext.termId }
364
+ value={ blockContext }
365
+ >
366
+ { blockContext.termId ===
367
+ ( activeBlockContextId ||
368
+ blockContexts[ 0 ]?.termId ) ? (
369
+ <TermTemplateInnerBlocks
370
+ classList={ blockContext.classList }
371
+ />
372
+ ) : null }
373
+ <MemoizedTermTemplateBlockPreview
374
+ blocks={ blocks }
375
+ blockContextId={ blockContext.termId }
376
+ classList={ blockContext.classList }
377
+ setActiveBlockContextId={
378
+ setActiveBlockContextId
379
+ }
380
+ isHidden={
381
+ blockContext.termId ===
382
+ ( activeBlockContextId ||
383
+ blockContexts[ 0 ]?.termId )
384
+ }
385
+ />
386
+ </BlockContextProvider>
387
+ ) ) }
388
+ </ul>
389
+ </>
390
+ );
391
+ }
@@ -0,0 +1,26 @@
1
+ .wp-block-term-template {
2
+ .term-loading {
3
+ .term-loading-placeholder {
4
+ width: 100%;
5
+ height: 1.5em;
6
+ margin-bottom: 0.25em;
7
+ background-color: $gray-100;
8
+ border-radius: 2px;
9
+ @media not ( prefers-reduced-motion ) {
10
+ animation: loadingpulse 1.5s ease-in-out infinite;
11
+ }
12
+ }
13
+ }
14
+ }
15
+
16
+ @keyframes loadingpulse {
17
+ 0% {
18
+ opacity: 1;
19
+ }
20
+ 50% {
21
+ opacity: 0.5;
22
+ }
23
+ 100% {
24
+ opacity: 1;
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { layout as icon } from '@wordpress/icons';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import initBlock from '../utils/init-block';
10
+ import metadata from './block.json';
11
+ import edit from './edit';
12
+ import save from './save';
13
+ import variations from './variations';
14
+
15
+ const { name } = metadata;
16
+ export { metadata, name };
17
+
18
+ export const settings = {
19
+ icon,
20
+ variations,
21
+ edit,
22
+ save,
23
+ example: {},
24
+ };
25
+
26
+ export const init = () => initBlock( { name, metadata, settings } );
@@ -0,0 +1,224 @@
1
+ <?php
2
+ /**
3
+ * Server-side rendering of the `core/term-template` block.
4
+ *
5
+ * @package WordPress
6
+ */
7
+
8
+ /**
9
+ * Renders the `core/term-template` block on the server.
10
+ *
11
+ * @since 6.9.0
12
+ *
13
+ * @param array $attributes Block attributes.
14
+ * @param string $content Block default content.
15
+ * @param WP_Block $block Block instance.
16
+ *
17
+ * @return string Returns the output of the term template.
18
+ */
19
+ function render_block_core_term_template( $attributes, $content, $block ) {
20
+ if ( ! isset( $block->context ) || ! isset( $attributes ) ) {
21
+ return '';
22
+ }
23
+
24
+ $query_block_context = $block->context;
25
+
26
+ if ( empty( $query_block_context['termQuery'] ) ) {
27
+ return '';
28
+ }
29
+
30
+ $query = $query_block_context['termQuery'];
31
+
32
+ $query_args = array(
33
+ 'taxonomy' => $query['taxonomy'] ?? 'category',
34
+ 'number' => $query['perPage'] ?? 10,
35
+ 'order' => $query['order'] ?? 'asc',
36
+ 'orderby' => $query['orderBy'] ?? 'name',
37
+ 'hide_empty' => $query['hideEmpty'] ?? true,
38
+ 'include' => $query['include'] ?? array(),
39
+ 'exclude' => $query['exclude'] ?? array(),
40
+ );
41
+
42
+ // Handle parent.
43
+ if ( ! empty( $query['hierarchical'] ) && isset( $query['parent'] ) ) {
44
+ $query_args['parent'] = $query['parent'];
45
+ } elseif ( ! empty( $query['hierarchical'] ) ) {
46
+ $query_args['parent'] = 0;
47
+ } elseif ( isset( $query['parent'] ) ) {
48
+ $query_args['parent'] = $query['parent'];
49
+ }
50
+
51
+ $terms_query = new WP_Term_Query( $query_args );
52
+ $terms = $terms_query->get_terms();
53
+
54
+ if ( ! $terms || is_wp_error( $terms ) ) {
55
+ return '';
56
+ }
57
+
58
+ // Handle hierarchical list.
59
+ $is_hierarchical = ! empty( $query['hierarchical'] );
60
+
61
+ if ( $is_hierarchical ) {
62
+ $content = render_block_core_term_template_hierarchical( $terms, $block, $query_args );
63
+ } else {
64
+ $content = render_block_core_term_template_flat( $terms, $block );
65
+ }
66
+
67
+ $classnames = 'wp-block-term-template';
68
+
69
+ if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) {
70
+ $classnames .= ' has-link-color';
71
+ }
72
+
73
+ $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => trim( $classnames ) ) );
74
+
75
+ // Default list layout.
76
+ return sprintf(
77
+ '<ul %s>%s</ul>',
78
+ $wrapper_attributes,
79
+ $content
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Renders terms in a flat list structure.
85
+ *
86
+ * @since 6.9.0
87
+ *
88
+ * @param array $terms Array of WP_Term objects.
89
+ * @param WP_Block $block Block instance.
90
+ *
91
+ * @return string HTML content for flat terms list.
92
+ */
93
+ function render_block_core_term_template_flat( $terms, $block ) {
94
+ $content = '';
95
+ foreach ( $terms as $term ) {
96
+ $content .= render_block_core_term_template_single( $term, $block );
97
+ }
98
+ return $content;
99
+ }
100
+
101
+ /**
102
+ * Renders terms in a hierarchical structure.
103
+ *
104
+ * @since 6.9.0
105
+ *
106
+ * @param array $terms Array of WP_Term objects.
107
+ * @param WP_Block $block Block instance.
108
+ * @param array $base_query_args Base query arguments.
109
+ *
110
+ * @return string HTML content for hierarchical terms list.
111
+ */
112
+ function render_block_core_term_template_hierarchical( $terms, $block, $base_query_args ) {
113
+ $content = '';
114
+
115
+ foreach ( $terms as $term ) {
116
+ $term_content = render_block_core_term_template_single( $term, $block );
117
+ $children_content = render_block_core_term_template_get_children( $term->term_id, $block, $base_query_args );
118
+
119
+ if ( ! empty( $children_content ) ) {
120
+ $term_content = str_replace( '</li>', '<ul>' . $children_content . '</ul></li>', $term_content );
121
+ }
122
+
123
+ $content .= $term_content;
124
+ }
125
+
126
+ return $content;
127
+ }
128
+
129
+ /**
130
+ * Gets and renders children of a specific term.
131
+ *
132
+ * @since 6.9.0
133
+ *
134
+ * @param int $parent_term_id Parent term ID.
135
+ * @param WP_Block $block Block instance.
136
+ * @param array $base_query_args Base query arguments.
137
+ *
138
+ * @return string HTML content for children terms.
139
+ */
140
+ function render_block_core_term_template_get_children( $parent_term_id, $block, $base_query_args ) {
141
+ $child_query_args = $base_query_args;
142
+ $child_query_args['parent'] = $parent_term_id;
143
+
144
+ $child_terms_query = new WP_Term_Query( $child_query_args );
145
+ $child_terms = $child_terms_query->get_terms();
146
+
147
+ if ( ! $child_terms || is_wp_error( $child_terms ) ) {
148
+ return '';
149
+ }
150
+
151
+ $content = '';
152
+
153
+ foreach ( $child_terms as $child_term ) {
154
+ $term_content = render_block_core_term_template_single( $child_term, $block );
155
+ $children_content = render_block_core_term_template_get_children( $child_term->term_id, $block, $base_query_args );
156
+
157
+ if ( ! empty( $children_content ) ) {
158
+ $term_content = str_replace( '</li>', '<ul>' . $children_content . '</ul></li>', $term_content );
159
+ }
160
+
161
+ $content .= $term_content;
162
+ }
163
+
164
+ return $content;
165
+ }
166
+
167
+ /**
168
+ * Renders a single term with its inner blocks.
169
+ *
170
+ * @since 6.9.0
171
+ *
172
+ * @param WP_Term $term Term object.
173
+ * @param WP_Block $block Block instance.
174
+ *
175
+ * @return string HTML content for a single term.
176
+ */
177
+ function render_block_core_term_template_single( $term, $block ) {
178
+ $inner_blocks = $block->inner_blocks;
179
+ $block_content = '';
180
+
181
+ if ( ! empty( $inner_blocks ) ) {
182
+ $term_id = $term->term_id;
183
+ $taxonomy = $term->taxonomy;
184
+
185
+ $filter_block_context = static function ( $context ) use ( $term_id, $taxonomy ) {
186
+ $context['termId'] = $term_id;
187
+ $context['taxonomy'] = $taxonomy;
188
+ return $context;
189
+ };
190
+
191
+ add_filter( 'render_block_context', $filter_block_context, 1 );
192
+
193
+ foreach ( $inner_blocks as $inner_block ) {
194
+ if ( method_exists( $inner_block, 'refresh_context_dependents' ) ) {
195
+ // WP_Block::refresh_context_dependents() was introduced in WordPress 6.8.
196
+ $inner_block->refresh_context_dependents();
197
+ $block_content .= $inner_block->render( array( 'dynamic' => true ) );
198
+ } else {
199
+ $block_content = ( new WP_Block( $inner_block->parsed_block ) )->render( array( 'dynamic' => false ) );
200
+ }
201
+ }
202
+ remove_filter( 'render_block_context', $filter_block_context, 1 );
203
+ }
204
+
205
+ $term_classes = implode( ' ', array( 'wp-block-term', 'term-' . $term->term_id ) );
206
+
207
+ // Default list layout
208
+ return '<li class="' . esc_attr( $term_classes ) . '">' . $block_content . '</li>';
209
+ }
210
+
211
+ /**
212
+ * Registers the `core/term-template` block on the server.
213
+ *
214
+ * @since 6.9.0
215
+ */
216
+ function register_block_core_term_template() {
217
+ register_block_type_from_metadata(
218
+ __DIR__ . '/term-template',
219
+ array(
220
+ 'render_callback' => 'render_block_core_term_template',
221
+ )
222
+ );
223
+ }
224
+ add_action( 'init', 'register_block_core_term_template' );
@@ -0,0 +1,8 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { InnerBlocks } from '@wordpress/block-editor';
5
+
6
+ export default function TermTemplateSave() {
7
+ return <InnerBlocks.Content />;
8
+ }
@@ -0,0 +1,12 @@
1
+ .wp-block-term-template {
2
+ margin-top: 0;
3
+ margin-bottom: 0;
4
+ max-width: 100%;
5
+ box-sizing: border-box;
6
+
7
+ &.is-layout-grid,
8
+ &.is-layout-flex {
9
+ list-style: none;
10
+ padding: 0;
11
+ }
12
+ }