/
use-theme-style-variations-by-property.js
125 lines (113 loc) · 3.42 KB
/
use-theme-style-variations-by-property.js
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
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { useContext, useMemo } from '@wordpress/element';
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import { unlock } from '../../lock-unlock';
import cloneDeep from '../../utils/clone-deep';
import { mergeBaseAndUserConfigs } from './global-styles-provider';
/**
* Returns a new object with only the properties specified in `properties`.
*
* @param {Object} object The object to filter
* @param {Object} property The property to filter by
* @return {Object} The merged object.
*/
export const filterObjectByProperty = ( object, property ) => {
if ( ! object ) {
return {};
}
const newObject = {};
Object.keys( object ).forEach( ( key ) => {
if ( key === property ) {
newObject[ key ] = object[ key ];
} else if ( typeof object[ key ] === 'object' ) {
const newFilter = filterObjectByProperty( object[ key ], property );
if ( Object.keys( newFilter ).length ) {
newObject[ key ] = newFilter;
}
}
} );
return newObject;
};
/**
* Removes all instances of a property from an object.
*
* @param {Object} object
* @param {string} property
* @return {Object} The modified object.
*/
const removePropertyFromObject = ( object, property ) => {
for ( const key in object ) {
if ( key === property ) {
delete object[ key ];
} else if ( typeof object[ key ] === 'object' ) {
removePropertyFromObject( object[ key ], property );
}
}
return object;
};
/**
* Return style variations with all properties removed except for the one specified in `type`.
*
* @param {Object} user The user variation.
* @param {Array} variations The other style variations.
* @param {string} property The property to filter by.
*
* @return {Array} The style variation with only the specified property filtered.
*/
export const getVariationsByProperty = ( user, variations, property ) => {
const userSettingsWithoutProperty = removePropertyFromObject(
cloneDeep( user ),
property
);
const variationsWithOnlyProperty = variations.map( ( variation ) => {
return {
...filterObjectByProperty( variation, property ),
// Add variation title and description to every variation item.
title: variation?.title,
description: variation?.description,
};
} );
return variationsWithOnlyProperty.map( ( variation ) =>
mergeBaseAndUserConfigs( userSettingsWithoutProperty, variation )
);
};
const { GlobalStylesContext } = unlock( blockEditorPrivateApis );
export default function useThemeStyleVariationsByProperty( {
styleProperty,
filter,
} ) {
const variations = useSelect( ( select ) => {
return select(
coreStore
).__experimentalGetCurrentThemeGlobalStylesVariations();
}, [] );
const { user } = useContext( GlobalStylesContext );
return useMemo( () => {
if ( ! styleProperty || ! variations.length ) {
return [];
}
/*
@TODO:
For colors, should also get filter?
Memoize/cache all this better? E.g., should we memoize the variations?
Test with empty theme
Test with 2022 - typography font families bork for some reason
*/
let styleVariations = getVariationsByProperty(
user,
variations,
styleProperty
);
if ( 'function' === typeof filter ) {
styleVariations = styleVariations.filter( filter );
}
return styleVariations;
}, [ styleProperty, variations, filter ] );
}