-
Notifications
You must be signed in to change notification settings - Fork 3
/
usePatterns.ts
126 lines (111 loc) · 4.22 KB
/
usePatterns.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import {
BlockEditorStoreActions,
BlockEditorStoreSelectors,
BlockPattern,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { BlockInstance, cloneBlock, createBlock } from '@wordpress/blocks';
import { useDispatch, useSelect } from '@wordpress/data';
import { useState } from '@wordpress/element';
import {
getBoundAttributeEntries,
getMismatchedAttributes,
hasBlockBinding,
isSyncedPattern,
} from '@/utils/block-binding';
import { getBlockConfig } from '@/utils/localized-block-data';
export function cloneBlockWithAttributes(
block: BlockInstance,
attributes: Record< string, string >,
remoteDataBlockName: string
): BlockInstance {
const mismatchedAttributes = getMismatchedAttributes(
block.attributes,
[ attributes ],
remoteDataBlockName
);
const newInnerBlocks = block.innerBlocks?.map( innerBlock =>
cloneBlockWithAttributes( innerBlock, attributes, remoteDataBlockName )
);
return cloneBlock( block, mismatchedAttributes, newInnerBlocks );
}
export function usePatterns( remoteDataBlockName: string, rootClientId: string = '' ) {
const { patterns } = getBlockConfig( remoteDataBlockName ) ?? {};
const { replaceInnerBlocks } = useDispatch< BlockEditorStoreActions >( blockEditorStore );
const { getBlocks, getPatternsByBlockTypes, __experimentalGetAllowedPatterns } =
useSelect< BlockEditorStoreSelectors >( blockEditorStore, [
remoteDataBlockName,
[ remoteDataBlockName, rootClientId ],
] );
const [ showPatternSelection, setShowPatternSelection ] = useState< boolean >( false );
// Extract patterns with defined roles.
const patternsByBlockTypes = getPatternsByBlockTypes( remoteDataBlockName );
const defaultPattern = patternsByBlockTypes.find( ( { name } ) => name === patterns?.default );
const innerBlocksPattern = patternsByBlockTypes.find(
( { name } ) => name === patterns?.inner_blocks
);
const returnValue = {
defaultPattern,
getInnerBlocks: (
result: Record< string, string >
): BlockInstance< RemoteDataInnerBlockAttributes >[] => {
return getBlocks< RemoteDataInnerBlockAttributes >( rootClientId ).map( block =>
cloneBlockWithAttributes( block, result, remoteDataBlockName )
);
},
getSupportedPatterns: ( result?: Record< string, string > ): BlockPattern[] => {
const supportedPatterns = __experimentalGetAllowedPatterns( rootClientId ).filter(
pattern =>
pattern?.blockTypes?.includes( remoteDataBlockName ) ||
pattern.blocks.some( block => hasBlockBinding( block, remoteDataBlockName ) )
);
// If no result is provided, return the supported patterns as is.
if ( ! result ) {
return supportedPatterns;
}
// Clone the pattern blocks and inject the provided result data so that
// it can be previewed.
return supportedPatterns.map( pattern => ( {
...pattern,
blocks: pattern.blocks.map( block =>
cloneBlockWithAttributes( block, result, remoteDataBlockName )
),
} ) );
},
insertPatternBlocks: ( pattern: BlockPattern ): void => {
setShowPatternSelection( false );
// If the pattern is a synced pattern, insert it directly.
if ( isSyncedPattern( pattern ) ) {
const syncedPattern = createBlock( 'core/block', { ref: pattern.id } );
replaceInnerBlocks( rootClientId, [ syncedPattern ] ).catch( () => {} );
return;
}
// Clone the pattern blocks with bindings to allow the user to make changes.
// We always insert a single representation of the pattern, even if it is a
// collection. The InnerBlocksLoop component will handle rendering the rest
// of the collection.
const patternBlocks =
pattern.blocks.map( block => {
const boundAttributes = getBoundAttributeEntries( block.attributes, remoteDataBlockName );
if ( ! boundAttributes.length ) {
return block;
}
return cloneBlock( block );
} ) ?? [];
replaceInnerBlocks( rootClientId, patternBlocks ).catch( () => {} );
},
markReadyForInsertion: (): void => {
if ( innerBlocksPattern ) {
returnValue.insertPatternBlocks( innerBlocksPattern );
return;
}
setShowPatternSelection( true );
},
resetReadyForInsertion: (): void => {
replaceInnerBlocks( rootClientId, [] ).catch( () => {} );
setShowPatternSelection( false );
},
showPatternSelection,
};
return returnValue;
}