PluginProbe
Extendify / trunk
Extendify vtrunk
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
← All changes | src/Shared/lib/vibe-globals.js +21 -89 3.1.6 → trunk View file →
@@ -1,5 +1,8 @@
1 -import { isObject } from '@shared/lib/utils';
1 +import {
2 + applyOwnedSection,
3 + collectOwnedPaths,
4 +} from '@shared/lib/owned-globals';
2 5
3 6 const ownedSettings = ['layout', 'typography', 'custom'];
4 7 // Per-leaf, so clearing a heading size leaves fontFamily to the fonts layer.
5 8 // WordPress reads duotone per block name, never per variation, so blocks is owned here.
@@ -4,20 +7,8 @@
4 7 // Per-leaf, so clearing a heading size leaves fontFamily to the fonts layer.
5 8 // WordPress reads duotone per block name, never per variation, so blocks is owned here.
6 9 const ownedStyles = ['typography', 'elements', 'blocks'];
7 10
8 -const leafPaths = (value, prefix) => {
9 - // PHP serializes an empty object as []; as a leaf it would purge the branch.
10 - if (Array.isArray(value) && value.length === 0) return [];
11 - if (!isObject(value)) return [prefix];
12 -
13 - const entries = Object.entries(value);
14 - // Vibes ship `elements: {}` to mean none; as a leaf it would purge fonts too.
15 - if (entries.length === 0) return [];
16 -
17 - return entries.flatMap(([key, child]) => leafPaths(child, [...prefix, key]));
18 -};
19 -
20 11 // The fonts and palette layers own these; a declaring payload is ignored.
21 12 const isExcludedPath = (section, path) => {
22 13 if (path[path.length - 1] === 'fontFamily') return true;
23 14 if (section !== 'settings') return false;
@@ -24,89 +15,29 @@
24 15 if (path[0] === 'color') return true;
25 16 return path[0] === 'typography' && path[1] === 'fontFamilies';
26 17 };
27 18
28 -const collectOwnedPaths = (vibes, section, roots) => {
29 - const paths = new Map();
30 -
31 - for (const vibe of Object.values(vibes)) {
32 - const source = vibe?.[section];
33 - if (!isObject(source)) continue;
34 -
35 - for (const key of roots) {
36 - if (!(key in source)) continue;
37 -
38 - for (const path of leafPaths(source[key], [key])) {
39 - if (isExcludedPath(section, path)) continue;
40 - paths.set(path.join('.'), path);
41 - }
42 - }
43 - }
44 -
45 - return [...paths.values()];
46 -};
47 -
48 -const valueAtPath = (source, path) =>
49 - path.reduce(
50 - (value, key) => (isObject(value) ? value[key] : undefined),
51 - source,
52 - );
53 -
54 -const withoutPath = (source, [key, ...rest]) => {
55 - if (!isObject(source) || !(key in source)) return source;
56 -
57 - const updated = { ...source };
58 -
59 - if (rest.length === 0) {
60 - delete updated[key];
61 - return updated;
62 - }
63 -
64 - const child = withoutPath(updated[key], rest);
65 -
66 - if (isObject(child) && Object.keys(child).length === 0) {
67 - delete updated[key];
68 - } else {
69 - updated[key] = child;
70 - }
71 -
72 - return updated;
73 -};
74 -
75 -const withPath = (source, [key, ...rest], value) => {
76 - const updated = isObject(source) ? { ...source } : {};
77 - updated[key] =
78 - rest.length === 0 ? value : withPath(updated[key], rest, value);
79 - return updated;
80 -};
81 -
82 -// Every vibe's leaves are purged first, so a switch leaves none of the last one behind.
83 -// A vibe that declares nothing lets the theme's own theme.json surface again.
84 -const applySection = (current, incoming, ownedPaths) => {
85 - const purged = ownedPaths.reduce(
86 - (section, path) => withoutPath(section, path),
87 - current,
88 - );
89 -
90 - if (!isObject(incoming)) return purged;
91 -
92 - return ownedPaths.reduce((section, path) => {
93 - const value = valueAtPath(incoming, path);
94 - return value === undefined ? section : withPath(section, path, value);
95 - }, purged);
96 -};
97 -
98 19 export const ownedSettingPaths = (vibes) =>
99 - collectOwnedPaths(vibes, 'settings', ownedSettings);
20 + collectOwnedPaths({
21 + payloads: vibes,
22 + section: 'settings',
23 + roots: ownedSettings,
24 + exclude: isExcludedPath,
25 + });
100 26
101 27 export const ownedStylePaths = (vibes) =>
102 - collectOwnedPaths(vibes, 'styles', ownedStyles);
28 + collectOwnedPaths({
29 + payloads: vibes,
30 + section: 'styles',
31 + roots: ownedStyles,
32 + exclude: isExcludedPath,
33 + });
103 34
104 35 export const applyVibeGlobals = ({ currentSettings, vibeSettings, vibes }) =>
105 - applySection(currentSettings, vibeSettings, ownedSettingPaths(vibes));
36 + applyOwnedSection(currentSettings, vibeSettings, ownedSettingPaths(vibes));
106 37
107 38 export const applyVibeStyles = ({ currentStyles, vibeStyles, vibes }) =>
108 - applySection(currentStyles, vibeStyles, ownedStylePaths(vibes));
39 + applyOwnedSection(currentStyles, vibeStyles, ownedStylePaths(vibes));
109 40
110 41 // A fonts or colors write may not move vibe-owned leaves.
111 42 export const preserveVibeSettings = ({
112 43 mergedSettings,
@@ -111,12 +42,13 @@
111 42 export const preserveVibeSettings = ({
112 43 mergedSettings,
113 44 currentSettings,
114 45 vibes,
115 -}) => applySection(mergedSettings, currentSettings, ownedSettingPaths(vibes));
46 +}) =>
47 + applyOwnedSection(mergedSettings, currentSettings, ownedSettingPaths(vibes));
116 48
117 49 export const preserveVibeStyles = ({ mergedStyles, currentStyles, vibes }) =>
118 - applySection(mergedStyles, currentStyles, ownedStylePaths(vibes));
50 + applyOwnedSection(mergedStyles, currentStyles, ownedStylePaths(vibes));
119 51
120 52 // natural-1 is what the theme ships, so writing it would only restate the theme.
121 53 export const vibeGlobalsEntry = (vibes, selectedVibe) =>
122 54 selectedVibe === 'natural-1' ? undefined : vibes?.[selectedVibe];