@secretstache/wordpress-gutenberg 0.6.15 → 0.7.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 (38) hide show
  1. package/build/editor-canvas.css +1 -1
  2. package/build/index.js +3208 -3187
  3. package/build/index.js.map +1 -1
  4. package/package.json +8 -2
  5. package/src/components/EmptyBlockPlaceholder.jsx +67 -0
  6. package/src/components/IconPicker.jsx +4 -5
  7. package/src/components/MediaControl.jsx +3 -3
  8. package/src/components/PatternAppender.jsx +24 -0
  9. package/src/components/PatternsModal.jsx +33 -35
  10. package/src/components/SpacingControl.jsx +5 -6
  11. package/src/components/index.js +19 -16
  12. package/src/filters/block-categories.js +50 -0
  13. package/src/filters/hide-root-block-for-other.js +53 -0
  14. package/src/filters/index.js +24 -0
  15. package/src/filters/inner-blocks-cleanup.jsx +53 -0
  16. package/src/filters/root-block.js +41 -0
  17. package/src/hooks/useTabs.jsx +2 -2
  18. package/src/icons/index.jsx +74 -38
  19. package/src/index.js +2 -0
  20. package/src/plugins/index.js +22 -0
  21. package/src/plugins/root-block-appender.jsx +51 -0
  22. package/src/plugins/root-pattern-appender.jsx +53 -0
  23. package/src/styles/{_empty-block-appender.scss → _empty-block-placeholder.scss} +6 -1
  24. package/src/styles/_root-block-appender.scss +13 -0
  25. package/src/styles/_root-pattern-appender.scss +58 -0
  26. package/src/styles/editor-canvas.scss +9 -3
  27. package/src/utils/helpers.js +53 -0
  28. package/src/utils/index.js +0 -3
  29. package/src/components/EmptyBlockAppender.jsx +0 -38
  30. package/src/utils/attributes.js +0 -252
  31. package/src/utils/filters.jsx +0 -40
  32. package/src/utils/rootBlock/appender.js +0 -62
  33. package/src/utils/rootBlock/hideRootBlockForInlineInserter.js +0 -24
  34. package/src/utils/rootBlock/index.js +0 -6
  35. package/src/utils/rootBlock/rootBlockVisibilityFilter.js +0 -35
  36. package/src/utils/rootBlock/setRootBlockFilter.js +0 -30
  37. package/src/utils/rootBlock/setRootBlockForPostTypes.js +0 -73
  38. package/src/utils/rootBlock/unsetRootBlockFilter.js +0 -26
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secretstache/wordpress-gutenberg",
3
- "version": "0.6.15",
3
+ "version": "0.7.0",
4
4
  "description": "",
5
5
  "author": "Secret Stache",
6
6
  "license": "GPL-2.0-or-later",
@@ -41,10 +41,13 @@
41
41
  "@wordpress/block-editor": "^14.19.0",
42
42
  "@wordpress/blocks": "^14.13.0",
43
43
  "@wordpress/components": "^29.10.0",
44
+ "@wordpress/compose": "^7.38.0",
44
45
  "@wordpress/data": "^10.24.0",
45
46
  "@wordpress/dom-ready": "^4.24.0",
46
47
  "@wordpress/element": "^6.24.0",
47
48
  "@wordpress/hooks": "^4.24.0",
49
+ "@wordpress/icons": "^11.5.0",
50
+ "@wordpress/plugins": "^7.38.0",
48
51
  "babel-core": "^6.26.3",
49
52
  "babel-loader": "^9.1.3",
50
53
  "node-sass": "^9.0.0",
@@ -52,7 +55,7 @@
52
55
  "react": "^18.0.0",
53
56
  "react-dom": "^18.0.0",
54
57
  "sass": "^1.90.0",
55
- "vite": "^7.0.6",
58
+ "vite": "^7.3.1",
56
59
  "vite-plugin-externals": "^0.6.2"
57
60
  },
58
61
  "dependencies": {
@@ -68,10 +71,13 @@
68
71
  "@wordpress/block-editor": "^14.19.0",
69
72
  "@wordpress/blocks": "^14.13.0",
70
73
  "@wordpress/components": "^29.10.0",
74
+ "@wordpress/compose": "^7.38.0",
71
75
  "@wordpress/data": "^10.24.0",
72
76
  "@wordpress/dom-ready": "^4.24.0",
73
77
  "@wordpress/element": "^6.24.0",
74
78
  "@wordpress/hooks": "^4.24.0",
79
+ "@wordpress/icons": "^11.5.0",
80
+ "@wordpress/plugins": "^7.38.0",
75
81
  "react": "^18.0.0",
76
82
  "react-dom": "^18.0.0"
77
83
  },
@@ -0,0 +1,67 @@
1
+ import { InnerBlocks } from '@wordpress/block-editor';
2
+ import { Icon, addTemplate } from '@wordpress/icons';
3
+ import { Button } from '@wordpress/components';
4
+ import classNames from 'classnames';
5
+
6
+ import { __experimentalPatternAppender as PatternAppender } from './PatternAppender.jsx';
7
+
8
+ export const __experimentalEmptyBlockPlaceholder = (props) => {
9
+ const {
10
+ title = 'This block is empty',
11
+ text = 'Use the "+" button below to add content blocks',
12
+ showIcon = true,
13
+ hasBlockAppender = true,
14
+ hasPatternAppender = true,
15
+ clientId,
16
+ isLight = false,
17
+ className,
18
+ } = props;
19
+
20
+ return (
21
+ <div className={classNames('empty-block-placeholder', className, { 'empty-block-placeholder--light': isLight })}>
22
+ <div className="empty-block-placeholder__content">
23
+ {
24
+ showIcon && (
25
+ <svg className="empty-block-placeholder__icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
26
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
27
+ </svg>
28
+ )
29
+ }
30
+
31
+ <h3 className="empty-block-placeholder__title">{title}</h3>
32
+ <p className="empty-block-placeholder__text">{text}</p>
33
+ </div>
34
+
35
+ <div className="empty-block-placeholder__buttons-wrapper">
36
+ {
37
+ hasBlockAppender && (
38
+ <div className="empty-block-placeholder__button">
39
+ <InnerBlocks.ButtonBlockAppender />
40
+ </div>
41
+ )
42
+ }
43
+
44
+ {
45
+ hasPatternAppender && (
46
+ <div className="empty-block-placeholder__button">
47
+ <PatternAppender
48
+ render={({ setModalOpen }) => (
49
+ <Button
50
+ __next40pxDefaultSize
51
+ className="block-editor-button-block-appender"
52
+ onClick={() => setModalOpen(true)}
53
+ label="Add Pattern"
54
+ showTooltip
55
+ >
56
+ <Icon icon={addTemplate} />
57
+ </Button>
58
+ )}
59
+ rootClientId={clientId}
60
+ />
61
+ </div>
62
+ )
63
+ }
64
+ </div>
65
+ </div>
66
+ );
67
+ };
@@ -3,9 +3,8 @@ import {
3
3
  MediaUpload,
4
4
  MediaUploadCheck,
5
5
  } from '@wordpress/block-editor';
6
- import { Button, Icon } from '@wordpress/components';
7
-
8
- import { editIcon, trashIcon } from '../icons/index.jsx';
6
+ import { Button } from '@wordpress/components';
7
+ import { Icon, pencil, trash } from '@wordpress/icons';
9
8
 
10
9
  export const IconPicker = ({ imageId, imageUrl, imageAlt, svgCode, onSelect, onRemove }) => {
11
10
  const hasImage = imageId && imageUrl;
@@ -38,7 +37,7 @@ export const IconPicker = ({ imageId, imageUrl, imageAlt, svgCode, onSelect, onR
38
37
  onClick={open}
39
38
  >
40
39
  <Icon
41
- icon={editIcon}
40
+ icon={pencil}
42
41
  size={20}
43
42
  className="bc-image-wrapper__btn-icon"
44
43
  />
@@ -50,7 +49,7 @@ export const IconPicker = ({ imageId, imageUrl, imageAlt, svgCode, onSelect, onR
50
49
  onClick={onRemove}
51
50
  >
52
51
  <Icon
53
- icon={trashIcon}
52
+ icon={trash}
54
53
  size={20}
55
54
  className="bc-image-wrapper__btn-icon"
56
55
  />
@@ -1,7 +1,7 @@
1
- import { BaseControl, Button, FocalPointPicker, Icon as WPIcon } from '@wordpress/components';
1
+ import { BaseControl, Button, FocalPointPicker } from '@wordpress/components';
2
+ import { Icon, page } from '@wordpress/icons';
2
3
  import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
3
4
 
4
- import { pageIcon } from '../icons/index.jsx';
5
5
  import { MEDIA_TYPE } from '../utils/index.js';
6
6
 
7
7
  export const ImageRenderer = ({
@@ -90,7 +90,7 @@ export const AnimationRenderer = ({
90
90
  return animationFileId && animationFileUrl ? (
91
91
  <>
92
92
  <div className="bc-animation-block-json-file" onClick={onSelectClick}>
93
- <WPIcon icon={pageIcon} size={36} />
93
+ <Icon icon={page} size={36} />
94
94
  <span>{animationFileName}</span>
95
95
  </div>
96
96
  <Button
@@ -0,0 +1,24 @@
1
+ import { useState } from '@wordpress/element';
2
+
3
+ import { __experimentalPatternsModal as PatternsModal } from './PatternsModal.jsx';
4
+
5
+ export const __experimentalPatternAppender = (props) => {
6
+ const { render, rootClientId } = props;
7
+
8
+ const [ isModalOpen, setModalOpen ] = useState(false);
9
+
10
+ return (
11
+ <>
12
+ { render({ isModalOpen, setModalOpen }) }
13
+
14
+ {
15
+ isModalOpen && (
16
+ <PatternsModal
17
+ onClose={() => setModalOpen(false)}
18
+ rootClientId={rootClientId}
19
+ />
20
+ )
21
+ }
22
+ </>
23
+ );
24
+ };
@@ -1,24 +1,22 @@
1
- import { useDispatch, useSelect } from '@wordpress/data';
2
- import { useState } from '@wordpress/element';
3
- import { rawHandler } from '@wordpress/blocks';
1
+ import { useDispatch, useSelect, select } from '@wordpress/data';
2
+ import { cloneBlock, createBlock, rawHandler } from '@wordpress/blocks';
4
3
  import { Button, Modal, TextControl } from '@wordpress/components';
4
+ import { useMemo, useState } from '@wordpress/element';
5
5
  import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor';
6
6
 
7
- export const PatternsModal = ({ isOpen, onRequestClose, rootClientId }) => {
7
+ export const __experimentalPatternsModal = ({ onClose, rootClientId }) => {
8
8
  const patterns = useSelect(
9
- (select) => select('core/block-editor').__experimentalGetAllowedPatterns?.(rootClientId) || [],
10
- [rootClientId]
9
+ (select) => select('core/block-editor').__experimentalGetAllowedPatterns?.(rootClientId),
10
+ [ rootClientId ]
11
11
  );
12
12
 
13
- const { insertBlocks, replaceBlock } = useDispatch('core/block-editor');
14
- const { selectedClientId } = useSelect((select) => ({
15
- selectedClientId: select('core/block-editor').getSelectedBlockClientId(),
16
- }), []);
13
+ const { insertBlocks } = useDispatch('core/block-editor');
14
+ const getBlockOrder = select('core/block-editor').getBlockOrder;
17
15
 
18
16
  const [ searchValue, setSearchValue ] = useState('');
19
17
  const [ selectedCategory, setSelectedCategory ] = useState('all');
20
18
 
21
- const normalizedPatterns = (patterns || []).map((pattern, index) => ({
19
+ const normalizedPatterns = useMemo(() => (patterns || []).map((pattern, index) => ({
22
20
  ...pattern,
23
21
  name: typeof pattern?.name === 'string' ? pattern.name : `pattern-${index}`,
24
22
  title: typeof pattern?.title === 'string'
@@ -29,7 +27,7 @@ export const PatternsModal = ({ isOpen, onRequestClose, rootClientId }) => {
29
27
  : typeof pattern?.content === 'string'
30
28
  ? rawHandler({ HTML: pattern.content })
31
29
  : [],
32
- }));
30
+ })), [ patterns ]);
33
31
 
34
32
  const categories = [
35
33
  { name: 'all', label: 'All Patterns' },
@@ -39,39 +37,39 @@ export const PatternsModal = ({ isOpen, onRequestClose, rootClientId }) => {
39
37
  })),
40
38
  ];
41
39
 
42
- const filteredPatterns = normalizedPatterns.filter((pattern) => {
43
- const matchesSearch = !searchValue || (pattern.title || '').toLowerCase().includes(searchValue.toLowerCase());
44
- const inCategory = selectedCategory === 'all' || (pattern.categories || []).includes(selectedCategory);
40
+ const filteredPatterns = useMemo(() => {
41
+ return normalizedPatterns.filter((pattern) => {
42
+ const matchesSearch = !searchValue || (pattern.title || '').toLowerCase().includes(searchValue.toLowerCase());
43
+ const inCategory = selectedCategory === 'all' || (pattern.categories || []).includes(selectedCategory);
45
44
 
46
- return matchesSearch && inCategory;
47
- });
45
+ return matchesSearch && inCategory;
46
+ });
47
+ }, [ normalizedPatterns, searchValue, selectedCategory ]);
48
48
 
49
49
  const onClickPattern = (pattern) => {
50
- const blocks = Array.isArray(pattern.blocks)
51
- ? pattern.blocks
52
- : typeof pattern.content === 'string'
53
- ? rawHandler({ HTML: pattern.content })
54
- : [];
50
+ const isSyncedUserPattern = pattern?.type === 'user' && pattern?.syncStatus !== 'unsynced' && !!pattern?.id;
51
+ const blocks = isSyncedUserPattern
52
+ ? [createBlock('core/block', { ref: pattern.id })]
53
+ : Array.isArray(pattern.blocks)
54
+ ? pattern.blocks.map((block) => cloneBlock(block))
55
+ : typeof pattern.content === 'string'
56
+ ? rawHandler({ HTML: pattern.content })
57
+ : [];
55
58
 
56
- const allRootAreSections = blocks.length > 0 && blocks.every((b) => b.name === 'ssm/section-wrapper');
57
- const targetClientId = selectedClientId || rootClientId;
59
+ const isInsideBlock = rootClientId !== null && rootClientId !== undefined;
60
+ const targetClientId = isInsideBlock ? rootClientId : undefined;
61
+ const blockOrder = isInsideBlock ? getBlockOrder(targetClientId) : getBlockOrder();
62
+ const insertIndex = Array.isArray(blockOrder) ? blockOrder.length : 0;
58
63
 
59
- if (allRootAreSections) {
60
- replaceBlock(targetClientId, blocks);
61
- } else {
62
- insertBlocks(blocks, 0, targetClientId);
63
- }
64
- onRequestClose();
65
- };
64
+ insertBlocks(blocks, insertIndex, targetClientId);
66
65
 
67
- if (!isOpen) {
68
- return null;
69
- }
66
+ onClose();
67
+ };
70
68
 
71
69
  return (
72
70
  <Modal
73
71
  title="Patterns"
74
- onRequestClose={onRequestClose}
72
+ onRequestClose={onClose}
75
73
  isFullScreen
76
74
  >
77
75
  <div className="block-editor-block-patterns-explorer">
@@ -1,7 +1,6 @@
1
1
  import { RangeControl, Tooltip } from '@wordpress/components';
2
2
  import { useCallback } from '@wordpress/element';
3
-
4
- import { sidesBottomIcon, sidesTopIcon } from '../icons/index.jsx';
3
+ import { sidesBottom, sidesTop } from '@wordpress/icons';
5
4
 
6
5
  const generateMarks = (min, max) => [
7
6
  { value: min, label: min === -1 ? 'Default' : min.toString() },
@@ -78,7 +77,7 @@ export const SpacingControl = ({
78
77
  onChange={handleChange('margin', 'top')}
79
78
  disabled={disabledMargin.top}
80
79
  tooltip={marginTooltips.top}
81
- afterIcon={sidesTopIcon}
80
+ afterIcon={sidesTop}
82
81
  withInputField={false}
83
82
  />
84
83
 
@@ -90,7 +89,7 @@ export const SpacingControl = ({
90
89
  onChange={handleChange('margin', 'bottom')}
91
90
  disabled={disabledMargin.bottom}
92
91
  tooltip={marginTooltips.bottom}
93
- afterIcon={sidesBottomIcon}
92
+ afterIcon={sidesBottom}
94
93
  withInputField={false}
95
94
  />
96
95
  </>
@@ -106,7 +105,7 @@ export const SpacingControl = ({
106
105
  onChange={handleChange('padding', 'top')}
107
106
  disabled={disabledPadding.top}
108
107
  tooltip={paddingTooltips.top}
109
- afterIcon={sidesTopIcon}
108
+ afterIcon={sidesTop}
110
109
  withInputField={false}
111
110
  />
112
111
 
@@ -118,7 +117,7 @@ export const SpacingControl = ({
118
117
  onChange={handleChange('padding', 'bottom')}
119
118
  disabled={disabledPadding.bottom}
120
119
  tooltip={paddingTooltips.bottom}
121
- afterIcon={sidesBottomIcon}
120
+ afterIcon={sidesBottom}
122
121
  withInputField={false}
123
122
  />
124
123
  </>
@@ -1,16 +1,19 @@
1
- export { ColorPaletteControl } from './ColorPaletteControl.jsx';
2
- export { IconPicker } from './IconPicker.jsx';
3
- export { ImageActions } from './ImageActions.jsx';
4
- export { LinkControl } from './LinkControl.jsx';
5
- export { ImageRenderer, VideoRenderer, AnimationRenderer, MediaControl } from './MediaControl.jsx';
6
- export { SortableSelect, SortableSelectAsync } from './SortableSelect.jsx';
7
- export { DataQueryControls } from './DataQueryControls.jsx';
8
- export { SpacingControl } from './SpacingControl.jsx';
9
- export { ResponsiveSpacingControl } from './ResponsiveSpacingControl.jsx';
10
- export { ResourcesWrapper } from './ResourcesWrapper.jsx';
11
- export { DividersControl } from './DividersControl.jsx';
12
- export { MediaTypeControl } from './MediaTypeControl.jsx';
13
- export { InsertBlockToolbar } from './InsertBlockToolbar.jsx';
14
- export { PreviewControl } from './PreviewControl.jsx';
15
- export { EmptyBlockAppender } from './EmptyBlockAppender.jsx';
16
- export { MediaWithFocalPointControl } from './MediaWithFocalPointControl.jsx';
1
+ export * from './ColorPaletteControl.jsx';
2
+ export * from './IconPicker.jsx';
3
+ export * from './ImageActions.jsx';
4
+ export * from './LinkControl.jsx';
5
+ export * from './MediaControl.jsx';
6
+ export * from './SortableSelect.jsx';
7
+ export * from './DataQueryControls.jsx';
8
+ export * from './SpacingControl.jsx';
9
+ export * from './ResponsiveSpacingControl.jsx';
10
+ export * from './ResourcesWrapper.jsx';
11
+ export * from './DividersControl.jsx';
12
+ export * from './MediaTypeControl.jsx';
13
+ export * from './InsertBlockToolbar.jsx';
14
+ export * from './PreviewControl.jsx';
15
+ export * from './MediaWithFocalPointControl.jsx';
16
+ export * from './MediaWithFocalPointControl.jsx';
17
+ export * from './EmptyBlockPlaceholder.jsx';
18
+ export * from './PatternAppender.jsx';
19
+ export * from './PatternsModal.jsx';
@@ -0,0 +1,50 @@
1
+ import { addFilter, removeFilter } from '@wordpress/hooks';
2
+
3
+ export class BlockCategoriesFilter {
4
+ name = 'ssm/block-categories';
5
+ isAdded = false;
6
+
7
+ constructor(
8
+ categories = {},
9
+ postTypes = ['page', 'post', 'ssm_design_system', 'wp_block'],
10
+ ) {
11
+ this.categories = categories;
12
+ this.postTypes = postTypes;
13
+ }
14
+
15
+ add() {
16
+ addFilter(
17
+ 'blocks.registerBlockType',
18
+ 'ssm/block-categories',
19
+ (settings, name) => {
20
+ const movedBlocks = Object.values(this.categories).flat();
21
+
22
+ if (!movedBlocks.includes(name)) {
23
+ return settings;
24
+ }
25
+
26
+ let newCategoryName = '';
27
+
28
+ for (const [category, blocks] of Object.entries(this.categories)) {
29
+ if (blocks.includes(name)) {
30
+ newCategoryName = category;
31
+ break;
32
+ }
33
+ }
34
+
35
+ return {
36
+ ...settings,
37
+ 'category': newCategoryName,
38
+ };
39
+ },
40
+ 10,
41
+ );
42
+
43
+ this.isAdded = true;
44
+ }
45
+
46
+ remove() {
47
+ removeFilter('blocks.registerBlockType', this.name);
48
+ this.isAdded = false;
49
+ }
50
+ }
@@ -0,0 +1,53 @@
1
+ import { addFilter, removeFilter } from '@wordpress/hooks';
2
+ import { getBlockTypes } from '@wordpress/blocks';
3
+
4
+ export class HideRootBlockForOtherFilter {
5
+ name = 'ssm/hide-root-block-for-other';
6
+ isAdded = false;
7
+
8
+ constructor(
9
+ rootBlockName = 'ssm/section-wrapper',
10
+ postTypes = ['page', 'post', 'ssm_design_system', 'wp_block'],
11
+ ) {
12
+ this.rootBlockName = rootBlockName;
13
+ this.postTypes = postTypes;
14
+ }
15
+
16
+ add() {
17
+ addFilter(
18
+ 'blocks.registerBlockType',
19
+ this.name,
20
+ (blockSettings, blockName) => {
21
+ const isRootBlock = blockName === this.rootBlockName;
22
+ const isBaseBlock = blockName === 'core/block';
23
+ const hasOwnAllowedBlocks = !!blockSettings?.allowedBlocks;
24
+
25
+ // skip specific blocks
26
+ if (isRootBlock || isBaseBlock || hasOwnAllowedBlocks) {
27
+ return blockSettings;
28
+ }
29
+
30
+ // get all blocks
31
+ const allowedBlocks = getBlockTypes()
32
+ ?.filter((block) => {
33
+ const isRootBlock = block.name === this.rootBlockName;
34
+ const hasParent = !!block?.parent;
35
+
36
+ return !isRootBlock && !hasParent;
37
+ })
38
+ ?.map((block) => block.name);
39
+
40
+ blockSettings.allowedBlocks = allowedBlocks;
41
+
42
+ return blockSettings;
43
+ },
44
+ );
45
+
46
+ this.isAdded = true;
47
+ }
48
+
49
+ remove() {
50
+ removeFilter('blocks.registerBlockType', this.name);
51
+ this.isAdded = false;
52
+ }
53
+ }
@@ -0,0 +1,24 @@
1
+ import { dispatch } from '@wordpress/data';
2
+
3
+ export * from './hide-root-block-for-other.js';
4
+ export * from './inner-blocks-cleanup.jsx';
5
+ export * from './root-block.js';
6
+ export * from './block-categories.js';
7
+
8
+ export const setPostTypeFilters = (filters, currentPostType, onSet) => {
9
+ if (!filters || !currentPostType) return;
10
+
11
+ filters?.forEach((filter) => {
12
+ if (filter.isAdded) {
13
+ filter.remove();
14
+ }
15
+
16
+ if (filter.postTypes.includes(currentPostType)) {
17
+ filter.add();
18
+ }
19
+
20
+ dispatch('core/blocks').reapplyBlockTypeFilters();
21
+
22
+ if (onSet) onSet();
23
+ });
24
+ };
@@ -0,0 +1,53 @@
1
+ import { createHigherOrderComponent } from '@wordpress/compose';
2
+ import { dispatch, select, useSelect } from '@wordpress/data';
3
+ import { useEffect } from '@wordpress/element';
4
+ import { addFilter, removeFilter } from '@wordpress/hooks';
5
+
6
+ export class InnerBlocksCleanupFilter {
7
+ name = 'ssm/inner-blocks-cleanup';
8
+ isAdded = false;
9
+
10
+ constructor(blockName) {
11
+ this.blockName = blockName;
12
+ }
13
+
14
+ add() {
15
+ addFilter(
16
+ 'editor.BlockEdit',
17
+ this.name,
18
+ createHigherOrderComponent((BlockEdit) => (props) => {
19
+ const { clientId, attributes, name } = props;
20
+
21
+ const { dataSource } = attributes;
22
+
23
+ if (name !== this.blockName) {
24
+ return <BlockEdit {...props} />;
25
+ }
26
+
27
+ const innerBlocks = useSelect(
28
+ (select) => select('core/block-editor').getBlock(clientId)?.innerBlocks || [],
29
+ []
30
+ );
31
+
32
+ useEffect(() => {
33
+ if (dataSource !== 'none' && innerBlocks.length > 0) {
34
+ dispatch('core/block-editor').updateBlock(clientId, {
35
+ ...select('core/block-editor').getBlock(clientId),
36
+ innerBlocks: [],
37
+ });
38
+ }
39
+ }, [ dataSource, innerBlocks.length ]);
40
+
41
+ return <BlockEdit {...props} />;
42
+ }, this.name),
43
+ 10,
44
+ );
45
+
46
+ this.isAdded = true;
47
+ }
48
+
49
+ remove() {
50
+ removeFilter('editor.BlockEdit', this.name);
51
+ this.isAdded = false;
52
+ }
53
+ }
@@ -0,0 +1,41 @@
1
+ import { addFilter, removeFilter } from '@wordpress/hooks';
2
+
3
+ export class RootBlockFilter {
4
+ name = 'ssm/set-root-block';
5
+ isAdded = false;
6
+
7
+ constructor(
8
+ rootBlockName = 'ssm/section-wrapper',
9
+ postTypes = ['page', 'post', 'ssm_design_system'],
10
+ ) {
11
+ this.rootBlockName = rootBlockName;
12
+ this.postTypes = postTypes;
13
+ }
14
+
15
+ add() {
16
+ addFilter(
17
+ 'blocks.registerBlockType',
18
+ this.name,
19
+ (settings, name) => {
20
+ const isRootBlock = name === this.rootBlockName;
21
+ const isBaseBlock = name === 'core/block';
22
+ const hasAncestor = !!settings?.ancestor;
23
+ const hasParent = !!settings?.parent;
24
+
25
+ if (!isRootBlock && !isBaseBlock && !hasAncestor && !hasParent) {
26
+ settings.ancestor = [this.rootBlockName];
27
+ }
28
+
29
+ return settings;
30
+ },
31
+ 10,
32
+ );
33
+
34
+ this.isAdded = true;
35
+ }
36
+
37
+ remove() {
38
+ removeFilter('blocks.registerBlockType', this.name);
39
+ this.isAdded = false;
40
+ }
41
+ }
@@ -3,8 +3,8 @@ import { store as blockEditorStore } from '@wordpress/block-editor';
3
3
  import { useCallback, useLayoutEffect, useMemo, useState } from '@wordpress/element';
4
4
  import { Button } from '@wordpress/components';
5
5
  import { createBlock } from '@wordpress/blocks';
6
+ import { plus } from '@wordpress/icons';
6
7
 
7
- import { plusIcon } from '../icons/index.jsx';
8
8
  import { useParentBlock } from './useParentBlock.js';
9
9
 
10
10
  export const useTabs = (tabsClientId, tabItemName) => {
@@ -100,7 +100,7 @@ export const useTabs = (tabsClientId, tabItemName) => {
100
100
  const TabAppender = useMemo(() => ({ label = 'Add new tab', ...other }) => (
101
101
  <Button
102
102
  label={label}
103
- icon={plusIcon}
103
+ icon={plus}
104
104
  className="bc-add-new-child-btn"
105
105
  onClick={onTabAppenderClick}
106
106
  {...other}