PluginProbe
The Innovative Form Builder – IvyForms / 1.4
The Innovative Form Builder – IvyForms v1.4
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / src / Services / Form / Style / FieldBaseStylesApplier.php

FieldBaseStylesApplier.php in The Innovative Form Builder – IvyForms 1.4, at backend/src/Services/Form/Style/FieldBaseStylesApplier.php

403 lines 12.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @copyright © Melograno Venture Studio. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace IvyForms\Services\Form\Style;
9
10 // phpcs:disable PSR1.Files.SideEffects
11
12 if (!defined('ABSPATH')) {
13 exit; // Exit if accessed directly
14 }
15
16 /**
17 * Class FieldBaseStylesApplier
18 *
19 * Applies base field styles such as wrapper, padding, labels, placeholders, and height overrides.
20 *
21 * @package IvyForms\Services\Form\StyleHelpers
22 */
23 class FieldBaseStylesApplier
24 {
25 /**
26 * @var string[]
27 */
28 private const ALLOWED_ALIGNMENTS = ['left', 'center', 'right'];
29
30 /**
31 * Apply base field styles
32 *
33 * @param CssBuilder $css
34 * @param string $baseSelector
35 * @param array<string, string> $activeColors
36 * @param array<string, string> $activeStyles
37 * @return void
38 */
39 public function apply(
40 CssBuilder $css,
41 string $baseSelector,
42 array $activeColors,
43 array $activeStyles
44 ): void {
45 $activeCornerRadius = $activeStyles['fieldCornerRadius'] ?? null;
46
47 $this->addFieldWrapperStyles($css, $baseSelector, $activeColors, $activeStyles, $activeCornerRadius);
48 $this->addHeightOverrides($css, $baseSelector);
49 $this->addPaddingStyles($css, $baseSelector, $activeStyles);
50 $this->addLabelStyles($css, $baseSelector, $activeColors, $activeStyles);
51 $this->addPlaceholderStyles($css, $baseSelector, $activeColors, $activeStyles);
52 $this->addDescriptionStyles($css, $baseSelector, $activeColors);
53 }
54
55 /**
56 * Add field wrapper styles
57 *
58 * @param CssBuilder $css
59 * @param string $baseSelector
60 * @param array<string, string> $activeColors
61 * @param array<string, string> $activeStyles
62 * @param string|null $activeCornerRadius
63 * @return void
64 */
65 private function addFieldWrapperStyles(
66 CssBuilder $css,
67 string $baseSelector,
68 array $activeColors,
69 array $activeStyles,
70 ?string $activeCornerRadius
71 ): void {
72 $selectors = $this->getFieldWrapperSelectors($baseSelector);
73 $styles = [];
74
75 $borderWidth = $activeStyles['fieldBorderWidth'] ?? '1px';
76 $borderStyle = $activeStyles['fieldBorderStyle'] ?? 'solid';
77 $styles[] = ' border-width: ' . esc_attr($borderWidth) . ' !important;';
78 $styles[] = ' border-style: ' . esc_attr($borderStyle) . ' !important;';
79
80 if (!empty($activeCornerRadius)) {
81 $styles[] = ' border-radius: ' . esc_attr($activeCornerRadius) . ' !important;';
82 }
83
84 if (!empty($activeColors['fieldBorderColor'])) {
85 $styles[] = ' border-color: ' . esc_attr($activeColors['fieldBorderColor']) . ' !important;';
86 }
87
88 if (!empty($activeColors['fieldText'])) {
89 $styles[] = ' color: ' . esc_attr($activeColors['fieldText']) . ' !important;';
90 }
91
92 if (!empty($activeColors['fieldBackground'])) {
93 $styles[] = ' background-color: ' . esc_attr($activeColors['fieldBackground']) . ' !important;';
94 }
95
96 $css->addRule($selectors, $styles);
97
98 // Separate rule for phone field inner input text color (to avoid being overridden by default styles)
99 if (!empty($activeColors['fieldText'])) {
100 $phoneTextSelectors = [$baseSelector . ' .vti__input'];
101 $css->addRule($phoneTextSelectors, [
102 ' color: ' . esc_attr($activeColors['fieldText']) . ' !important;',
103 ]);
104 }
105 }
106
107 /**
108 * Add height override styles
109 *
110 * @param CssBuilder $css
111 * @param string $baseSelector
112 * @return void
113 */
114 private function addHeightOverrides(CssBuilder $css, string $baseSelector): void
115 {
116 $heightSelectors = [
117 $baseSelector . ' .ivyforms-input.el-input',
118 $baseSelector . ' .ivyforms-phone-input',
119 $baseSelector . ' .ivyforms-field__phone',
120 $baseSelector . ' .ivyforms-field__select',
121 $baseSelector . ' .ivyforms-field__address',
122 $baseSelector . ' .ivyforms-field__date',
123 $baseSelector . ' .ivyforms-field__time',
124 $baseSelector . ' .ivyforms-form-item-select .el-select__wrapper',
125 $baseSelector . ' .ivyforms-phone-input .vue-tel-input',
126 ];
127
128 $css->addRule($heightSelectors, [
129 ' height: auto !important;',
130 ' min-height: 40px !important;',
131 ]);
132 }
133
134 /**
135 * Add padding styles
136 *
137 * @param CssBuilder $css
138 * @param string $baseSelector
139 * @param array<string, string> $activeStyles
140 * @return void
141 */
142 private function addPaddingStyles(
143 CssBuilder $css,
144 string $baseSelector,
145 array $activeStyles
146 ): void {
147 $paddingY = $activeStyles['fieldPaddingTopBottom'] ?? '0px';
148 $paddingX = $activeStyles['fieldPaddingLeftRight'] ?? '12px';
149
150 $directInputSelectors = [
151 $baseSelector . ' input[type="email"]',
152 $baseSelector . ' input[type="tel"]',
153 $baseSelector . ' input[type="number"]',
154 $baseSelector . ' input[type="url"]',
155 $baseSelector . ' input[type="date"]',
156 $baseSelector . ' input[type="time"]',
157 $baseSelector . ' textarea',
158 ];
159
160 $css->addRule($directInputSelectors, [
161 ' padding: ' . esc_attr($paddingY) . ' ' . esc_attr($paddingX) . ' !important;',
162 ]);
163 }
164
165 /**
166 * Add label styles
167 *
168 * @param CssBuilder $css
169 * @param string $baseSelector
170 * @param array<string, string> $activeColors
171 * @param array<string, string> $activeStyles
172 * @return void
173 */
174 private function addLabelStyles(
175 CssBuilder $css,
176 string $baseSelector,
177 array $activeColors,
178 array $activeStyles
179 ): void {
180 if (!$this->hasAnyLabelStyle($activeColors, $activeStyles)) {
181 return;
182 }
183
184 $styles = $this->buildLabelStyles($activeColors, $activeStyles);
185 $css->addRule($this->getLabelSelectors($baseSelector), $styles);
186 }
187
188 /**
189 * Check whether any label style value is present.
190 *
191 * @param array<string, string> $activeColors
192 * @param array<string, string> $activeStyles
193 * @return bool
194 */
195 private function hasAnyLabelStyle(array $activeColors, array $activeStyles): bool
196 {
197 return !empty($activeColors['fieldLabels'])
198 || !empty($activeStyles['fieldLabelPaddingLeftRight'])
199 || !empty($activeStyles['fieldLabelPaddingTopBottom'])
200 || !empty($activeStyles['fieldLabelAlignment']);
201 }
202
203 /**
204 * Build label styles list.
205 *
206 * @param array<string, string> $activeColors
207 * @param array<string, string> $activeStyles
208 * @return string[]
209 */
210 private function buildLabelStyles(array $activeColors, array $activeStyles): array
211 {
212 $styles = [];
213
214 if (!empty($activeColors['fieldLabels'])) {
215 $styles[] = ' color: ' . esc_attr($activeColors['fieldLabels']) . ' !important;';
216 }
217
218 if (!empty($activeStyles['fieldLabelPaddingLeftRight'])) {
219 $styles[] = ' padding-left: ' . esc_attr($activeStyles['fieldLabelPaddingLeftRight']) . ' !important;';
220 $styles[] = ' padding-right: ' . esc_attr($activeStyles['fieldLabelPaddingLeftRight']) . ' !important;';
221 }
222
223 if (!empty($activeStyles['fieldLabelPaddingTopBottom'])) {
224 $styles[] = ' padding-top: ' . esc_attr($activeStyles['fieldLabelPaddingTopBottom']) . ' !important;';
225 $styles[] = ' padding-bottom: ' . esc_attr($activeStyles['fieldLabelPaddingTopBottom']) . ' !important;';
226 }
227
228 $this->appendLabelAlignmentStyles($styles, $activeStyles['fieldLabelAlignment'] ?? null);
229
230 return $styles;
231 }
232
233 /**
234 * Add label alignment styles when alignment is valid.
235 *
236 * @param string[] $styles
237 * @param mixed $labelAlignmentRaw
238 * @return void
239 */
240 private function appendLabelAlignmentStyles(array &$styles, $labelAlignmentRaw): void
241 {
242 $labelAlignment = $this->sanitizeAlignment($labelAlignmentRaw);
243
244 if (null === $labelAlignment) {
245 return;
246 }
247
248 $justifyContent = $this->mapAlignmentToJustifyContent($labelAlignment);
249
250 if ('left' === $labelAlignment) {
251 return;
252 }
253
254 $styles[] = ' text-align: ' . esc_attr($labelAlignment) . ' !important;';
255 $styles[] = ' justify-content: ' . esc_attr($justifyContent) . ' !important;';
256 $styles[] = ' display: flex !important;';
257 $styles[] = ' width: 100% !important;';
258 }
259
260 /**
261 * Get label selectors.
262 *
263 * @param string $baseSelector
264 * @return string[]
265 */
266 private function getLabelSelectors(string $baseSelector): array
267 {
268 return [
269 $baseSelector . ' .ivyforms-form-item .el-form-item__label',
270 $baseSelector . ' .ivyforms-form-item__left-label',
271 $baseSelector . ' .ivyforms-label',
272 $baseSelector . ' .el-form-item__label',
273 $baseSelector . ' label',
274 ];
275 }
276
277 /**
278 * Add placeholder styles
279 *
280 * @param CssBuilder $css
281 * @param string $baseSelector
282 * @param array<string, string> $activeColors
283 * @param array<string, string> $activeStyles
284 * @return void
285 */
286 private function addPlaceholderStyles(
287 CssBuilder $css,
288 string $baseSelector,
289 array $activeColors,
290 array $activeStyles
291 ): void {
292 if (
293 empty($activeColors['fieldPlaceholder']) &&
294 empty($activeStyles['fieldPlaceholderPaddingLeftRight']) &&
295 empty($activeStyles['fieldPlaceholderAlignment'])
296 ) {
297 return;
298 }
299
300 $placeholderSelectors = [
301 $baseSelector . ' input::placeholder',
302 $baseSelector . ' textarea::placeholder',
303 $baseSelector . ' .el-input__inner::placeholder',
304 $baseSelector . ' .el-textarea__inner::placeholder',
305 ];
306
307 $styles = [];
308
309 if (!empty($activeColors['fieldPlaceholder'])) {
310 $styles[] = ' color: ' . esc_attr($activeColors['fieldPlaceholder']) . ' !important;';
311 }
312
313 if (!empty($activeStyles['fieldPlaceholderPaddingLeftRight'])) {
314 $styles[] = ' padding-left: ' .
315 esc_attr($activeStyles['fieldPlaceholderPaddingLeftRight']) . ' !important;';
316 $styles[] = ' padding-right: ' .
317 esc_attr($activeStyles['fieldPlaceholderPaddingLeftRight']) . ' !important;';
318 }
319
320 $placeholderAlignment = $this->sanitizeAlignment($activeStyles['fieldPlaceholderAlignment'] ?? null);
321 if (null !== $placeholderAlignment) {
322 $styles[] = ' text-align: ' . esc_attr($placeholderAlignment) . ' !important;';
323 }
324
325 if (!empty($styles)) {
326 $css->addRule($placeholderSelectors, $styles);
327 }
328 }
329
330 /**
331 * Add default field description styles.
332 *
333 * @param CssBuilder $css
334 * @param string $baseSelector
335 * @param array<string, string> $activeColors
336 * @return void
337 */
338 private function addDescriptionStyles(
339 CssBuilder $css,
340 string $baseSelector,
341 array $activeColors
342 ): void {
343 if (empty($activeColors['fieldDescriptionColor'])) {
344 return;
345 }
346
347 $css->addRule([
348 $baseSelector . ' .ivyforms-form-item__info-text',
349 ], [
350 ' color: ' . esc_attr($activeColors['fieldDescriptionColor']) . ' !important;',
351 ' --ivyforms-info-color: ' . esc_attr($activeColors['fieldDescriptionColor']) . ' !important;',
352 ]);
353 }
354
355 /**
356 * Get field wrapper selectors
357 *
358 * @param string $baseSelector
359 * @return string[]
360 */
361 private function getFieldWrapperSelectors(string $baseSelector): array
362 {
363 return [
364 $baseSelector . ' .ivyforms-input.el-input .el-input__wrapper',
365 $baseSelector . ' .ivyforms-field .el-input__wrapper',
366 $baseSelector . ' .ivyforms-field .el-textarea__inner',
367 $baseSelector . ' .el-select .el-input__wrapper',
368 $baseSelector . ' .ivyforms-phone-input .el-input__wrapper',
369 $baseSelector . ' .ivyforms-field__select .el-input__wrapper',
370 ];
371 }
372
373 /**
374 * Sanitize alignment value.
375 *
376 * @param mixed $alignment
377 * @return string|null
378 */
379 private function sanitizeAlignment($alignment): ?string
380 {
381 if (!is_string($alignment) || '' === $alignment) {
382 return null;
383 }
384
385 return in_array($alignment, self::ALLOWED_ALIGNMENTS, true) ? $alignment : null;
386 }
387
388 /**
389 * Map alignment value to justify-content equivalent.
390 *
391 * @param string $alignment
392 * @return string
393 */
394 private function mapAlignmentToJustifyContent(string $alignment): string
395 {
396 if ('left' === $alignment) {
397 return 'flex-start';
398 }
399
400 return 'center' === $alignment ? 'center' : 'flex-end';
401 }
402 }
403