@secretstache/wordpress-gutenberg 0.6.16 → 0.7.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 (39) hide show
  1. package/build/editor-canvas.css +1 -1
  2. package/build/index.js +3216 -3193
  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 +14 -15
  8. package/src/components/MediaWithFocalPointControl.jsx +10 -11
  9. package/src/components/PatternAppender.jsx +24 -0
  10. package/src/components/PatternsModal.jsx +33 -35
  11. package/src/components/SpacingControl.jsx +5 -6
  12. package/src/components/index.js +18 -16
  13. package/src/filters/block-categories.js +50 -0
  14. package/src/filters/hide-root-block-for-other.js +53 -0
  15. package/src/filters/index.js +24 -0
  16. package/src/filters/inner-blocks-cleanup.jsx +53 -0
  17. package/src/filters/root-block.js +41 -0
  18. package/src/hooks/useTabs.jsx +2 -2
  19. package/src/icons/index.jsx +74 -38
  20. package/src/index.js +2 -0
  21. package/src/plugins/index.js +22 -0
  22. package/src/plugins/root-block-appender.jsx +51 -0
  23. package/src/plugins/root-pattern-appender.jsx +53 -0
  24. package/src/styles/{_empty-block-appender.scss → _empty-block-placeholder.scss} +6 -1
  25. package/src/styles/_root-block-appender.scss +13 -0
  26. package/src/styles/_root-pattern-appender.scss +58 -0
  27. package/src/styles/editor-canvas.scss +9 -3
  28. package/src/utils/helpers.js +53 -0
  29. package/src/utils/index.js +0 -3
  30. package/src/components/EmptyBlockAppender.jsx +0 -38
  31. package/src/utils/attributes.js +0 -252
  32. package/src/utils/filters.jsx +0 -40
  33. package/src/utils/rootBlock/appender.js +0 -62
  34. package/src/utils/rootBlock/hideRootBlockForInlineInserter.js +0 -24
  35. package/src/utils/rootBlock/index.js +0 -6
  36. package/src/utils/rootBlock/rootBlockVisibilityFilter.js +0 -35
  37. package/src/utils/rootBlock/setRootBlockFilter.js +0 -30
  38. package/src/utils/rootBlock/setRootBlockForPostTypes.js +0 -73
  39. 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.16",
3
+ "version": "0.7.1",
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 = ({
@@ -27,7 +27,7 @@ export const ImageRenderer = ({
27
27
  <Button
28
28
  className="bc-remove-btn"
29
29
  onClick={onRemoveClick}
30
- isSecondary
30
+ variant="secondary"
31
31
  isDestructive
32
32
  >
33
33
  {removeButtonLabel}
@@ -61,7 +61,7 @@ export const VideoRenderer = ({
61
61
  <Button
62
62
  className="bc-remove-btn"
63
63
  onClick={onRemoveClick}
64
- isSecondary
64
+ variant="secondary"
65
65
  isDestructive
66
66
  >
67
67
  {removeButtonLabel}
@@ -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
@@ -158,16 +158,15 @@ export const MediaControl = ({
158
158
 
159
159
  {
160
160
  hasFocalPoint && mediaId && mediaUrl && (
161
- <BaseControl label={focalPointLabel}>
162
- <FocalPointPicker
163
- __nextHasNoMarginBottom
164
- url={mediaUrl}
165
- value={focalPoint}
166
- onDragStart={onFocalPointChange}
167
- onDrag={onFocalPointChange}
168
- onChange={onFocalPointChange}
169
- />
170
- </BaseControl>
161
+ <FocalPointPicker
162
+ __nextHasNoMarginBottom
163
+ label={focalPointLabel}
164
+ url={mediaUrl}
165
+ value={focalPoint}
166
+ onDragStart={onFocalPointChange}
167
+ onDrag={onFocalPointChange}
168
+ onChange={onFocalPointChange}
169
+ />
171
170
  )
172
171
  }
173
172
  </>
@@ -36,7 +36,7 @@ export const MediaWithFocalPointControl = memo(({
36
36
 
37
37
  [focalPointAttributeName]: { x: 0.5, y: 0.5 }
38
38
  });
39
- }, [ mediaAttributeName ]);
39
+ }, [ mediaAttributeName, focalPointAttributeName ]);
40
40
 
41
41
  const onMediaRemove = useCallback(() => {
42
42
  setAttributes({
@@ -62,16 +62,15 @@ export const MediaWithFocalPointControl = memo(({
62
62
  </BaseControl>
63
63
 
64
64
  {media?.url && (
65
- <BaseControl label={focalPointLabel}>
66
- <FocalPointPicker
67
- __nextHasNoMarginBottom
68
- url={media.url}
69
- value={focalPoint}
70
- onDragStart={onFocalPointChange}
71
- onDrag={onFocalPointChange}
72
- onChange={onFocalPointChange}
73
- />
74
- </BaseControl>
65
+ <FocalPointPicker
66
+ __nextHasNoMarginBottom
67
+ label={focalPointLabel}
68
+ url={media.url}
69
+ value={focalPoint}
70
+ onDragStart={onFocalPointChange}
71
+ onDrag={onFocalPointChange}
72
+ onChange={onFocalPointChange}
73
+ />
75
74
  )}
76
75
  </>
77
76
  );
@@ -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,18 @@
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 './EmptyBlockPlaceholder.jsx';
17
+ export * from './PatternAppender.jsx';
18
+ 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
+ }