PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.1
1.7.1 1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 All 51 releases
fluent-cart / app / Services / Theme / Readers / KadenceSettingsReader.php

KadenceSettingsReader.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.7.1, at app/Services/Theme/Readers/KadenceSettingsReader.php

305 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Theme\Readers;
4
5 use FluentCart\App\Services\Theme\ColorMath;
6 use FluentCart\App\Services\Theme\ThemePalette;
7 use FluentCart\Framework\Support\Arr;
8
9 /**
10 * Kadence's Customizer colours, read from its theme mods rather than its slots.
11 *
12 * Kadence keeps each semantic colour in its own setting — Links
13 * (`link_color`), Base Font (`base_font`), Content and Site Background
14 * (`content_background`, `site_background`) and Buttons (`buttons_background`,
15 * `buttons_color`) — each holding a palette slug (`palette3`) or a custom
16 * colour. The button is not tied to the link colour, so each role is read
17 * from its own setting through `\Kadence\kadence()->sub_option()`, the same
18 * call Kadence's own styles component prints from.
19 *
20 * A slug is written the way Kadence writes it (`Kadence_CSS::render_color()`):
21 * `paletteN` becomes `var(--global-paletteN)`, which ThemePalette resolves to
22 * `var(--global-paletteN, #hex)` from `palette_option()` (the active palette
23 * of three; see paletteValues()). A sub-key `sub_option()` answers null for —
24 * cleared, or missing from a saved setting — takes Kadence's own default.
25 *
26 * Kadence has no border setting (its borders use the static
27 * `--global-gray-400`), so no border is stated and FluentCart keeps deriving it.
28 *
29 * Its Buttons → Border Radius (`buttons_border_radius`) is the button radius
30 * (see radii()). Kadence has no form-field radius setting (its stylesheet's
31 * inputs are a fixed 3px) and its product-archive radii are WooCommerce
32 * settings, so neither is read.
33 *
34 * Verified against Kadence 1.5.0.
35 */
36 class KadenceSettingsReader implements ThemeSettingsReader, ThemeRadiusReader
37 {
38 /**
39 * Kadence's defaults per setting and sub-key
40 * (inc/components/options/component.php, defaults()).
41 *
42 * @var array
43 */
44 protected static $defaults = [
45 'link_color' => ['highlight' => 'palette1'],
46 'base_font' => ['color' => 'palette4'],
47 'buttons_background' => ['color' => 'palette1', 'hover' => 'palette2'],
48 'buttons_color' => ['color' => 'palette9', 'hover' => 'palette9'],
49 ];
50
51 /**
52 * Detected by Kadence's own template-tags API, not by theme name, so a
53 * child theme or renamed folder keeps working.
54 *
55 * @return bool
56 */
57 public static function applies(): bool
58 {
59 $kadence = self::api();
60
61 return $kadence !== null
62 && is_callable([$kadence, 'sub_option'])
63 && is_callable([$kadence, 'palette_option']);
64 }
65
66 /**
67 * @return array Role => hex or `var(--global-paletteN, #hex)`.
68 */
69 public static function roles(): array
70 {
71 if (!self::applies()) {
72 return [];
73 }
74
75 $roles = [
76 'accent' => self::read('link_color', 'highlight'),
77 'text' => self::read('base_font', 'color'),
78 'surface' => self::surface(),
79 ];
80
81 // Kadence states both halves of its button, resting and hover. The
82 // hover belongs to the resting button: without one there is no hover.
83 $button = self::buttonPair('color');
84
85 if ($button) {
86 $roles = array_merge($roles, $button, self::buttonPair('hover', 'button_hover_'));
87 }
88
89 return array_filter($roles, function ($value) {
90 return $value !== '';
91 });
92 }
93
94 /**
95 * Each palette slot's property and current colour:
96 * `--global-paletteN` => colour, from the palette Kadence has active — the
97 * same `palette_option()` Kadence prints the properties from.
98 *
99 * @return array Property => colour, as Kadence answers it (palette10 can
100 * be an oklch() expression; ThemePalette drops non-hex).
101 */
102 public static function paletteValues(): array
103 {
104 if (!self::applies()) {
105 return [];
106 }
107
108 $values = [];
109
110 try {
111 $kadence = self::api();
112
113 foreach (range(1, 15) as $index) {
114 $values['--global-palette' . $index] = (string)$kadence->palette_option('palette' . $index);
115 }
116 } catch (\Throwable $e) {
117 // Kadence's API misbehaving means no palette values: references
118 // stay bare and unmeasurable, which resolve() already refuses.
119 return [];
120 }
121
122 return $values;
123 }
124
125 /**
126 * One button half-pair: background from `buttons_background`, text from
127 * `buttons_color`, both at the same state.
128 *
129 * A background that is set but unwritable (a gradient, an rgba()) is not
130 * replaced by a default — Kadence is painting it, just not in a form
131 * FluentCart can write — so the pair is omitted. A text that cannot be
132 * written gets the colour that reads on the background.
133 *
134 * @param string $state 'color' (resting) or 'hover'.
135 * @param string $prefix Role prefix.
136 * @return array
137 */
138 protected static function buttonPair(string $state, string $prefix = 'button_'): array
139 {
140 $background = self::read('buttons_background', $state);
141
142 if ($background === '') {
143 return [];
144 }
145
146 $text = self::read('buttons_color', $state);
147
148 if ($text === '') {
149 $text = ColorMath::readableText(ThemePalette::measurable($background));
150 }
151
152 return [
153 $prefix . 'bg' => $background,
154 $prefix . 'text' => $text,
155 ];
156 }
157
158 /**
159 * The content background, falling back to the site background.
160 *
161 * Kadence paints `.content-bg` (and the unboxed `.site`) with the content
162 * background and the body with the site background; both are saved per
163 * device, and desktop is the unprefixed rule that speaks for all. A
164 * gradient paints no colour, so it states no surface — and the site
165 * background is not under the content either, so it is not used instead.
166 *
167 * @return string
168 */
169 protected static function surface(): string
170 {
171 foreach (['content_background', 'site_background'] as $setting) {
172 $value = self::subOption($setting, 'desktop');
173
174 if (!is_array($value)) {
175 continue;
176 }
177
178 if (Arr::get($value, 'type', 'color') === 'gradient' && Arr::get($value, 'gradient', '') !== '') {
179 return '';
180 }
181
182 $color = Arr::get($value, 'color', '');
183
184 if (is_string($color) && trim($color) !== '') {
185 return self::normalise($color);
186 }
187 }
188
189 return '';
190 }
191
192 /**
193 * One colour setting at one sub-key, over Kadence's default for it.
194 *
195 * @param string $setting
196 * @param string $subKey
197 * @return string
198 */
199 protected static function read(string $setting, string $subKey): string
200 {
201 $raw = self::subOption($setting, $subKey);
202
203 if (!is_string($raw) || trim($raw) === '') {
204 $raw = (string)Arr::get(self::$defaults, $setting . '.' . $subKey, '');
205 }
206
207 return self::normalise($raw);
208 }
209
210 /**
211 * A Kadence colour value as FluentCart writes it.
212 *
213 * `paletteN` is written as Kadence writes it, `var(--global-paletteN)`,
214 * and resolved through ThemePalette::settingValue() like every other
215 * reader's value. A value that cannot be measured — a gradient, rgba(),
216 * or palette10's oklch() complement — is not stated.
217 *
218 * @param string $raw
219 * @return string
220 */
221 protected static function normalise(string $raw): string
222 {
223 $raw = trim($raw);
224
225 if (preg_match('/^palette(\d{1,2})$/', $raw, $matches)) {
226 $raw = 'var(--global-palette' . $matches[1] . ')';
227 }
228
229 $value = ThemePalette::settingValue($raw);
230
231 return ThemePalette::measurable($value) !== '' ? $value : '';
232 }
233
234 /**
235 * `sub_option()`, shielded from Kadence's API throwing.
236 *
237 * @param string $setting
238 * @param string $subKey
239 * @return mixed
240 */
241 protected static function subOption(string $setting, string $subKey)
242 {
243 try {
244 return self::api()->sub_option($setting, $subKey);
245 } catch (\Throwable $e) {
246 return null;
247 }
248 }
249
250 /**
251 * Kadence's template-tags object, or null when Kadence is not loaded.
252 *
253 * @return object|null
254 */
255 protected static function api()
256 {
257 if (!function_exists('Kadence\\kadence')) {
258 return null;
259 }
260
261 try {
262 $kadence = \Kadence\kadence();
263 } catch (\Throwable $e) {
264 return null;
265 }
266
267 return is_object($kadence) ? $kadence : null;
268 }
269
270 /**
271 * The button radius.
272 *
273 * `buttons_border_radius` is a responsive range (`size` / `unit` per
274 * device) Kadence prints through `render_range()` on its button selector
275 * (inc/components/styles/component.php); the desktop rule is the
276 * unprefixed one that speaks for all. A desktop size that is not a number
277 * prints nothing, and Kadence's stylesheet paints its buttons 3px
278 * (assets/css/global.min.css), which is what is stated then.
279 *
280 * @return array Role => length, as Kadence prints it.
281 */
282 public static function radii(): array
283 {
284 if (!self::applies()) {
285 return [];
286 }
287
288 try {
289 $range = self::api()->option('buttons_border_radius');
290 } catch (\Throwable $e) {
291 return [];
292 }
293
294 $size = is_array($range) ? Arr::get($range, 'size.desktop', '') : '';
295
296 if (!is_numeric($size)) {
297 return ['btn' => '3px'];
298 }
299
300 $unit = is_array($range) ? (string)Arr::get($range, 'unit.desktop', '') : '';
301
302 return ['btn' => $size . ($unit !== '' ? $unit : 'px')];
303 }
304 }
305