PluginProbe
Tableberg – Simple Gutenberg Table Block / 1.0.5
Tableberg – Simple Gutenberg Table Block v1.0.5
1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 trunk 0.0.2 0.2.1 0.3.2 0.3.3 0.4.1 0.5.0 0.5.1 0.5.2 0.5.3 0.5.4 0.5.5 0.5.6 0.5.7 All 42 releases
tableberg / renderer / Cell / CellRenderer.php

CellRenderer.php in Tableberg – Simple Gutenberg Table Block 1.0.5, at renderer/Cell/CellRenderer.php

463 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Tableberg\Renderer\Cell;
4
5 use Tableberg\Renderer\Attrs\StringAttr;
6 use Tableberg\Renderer\Button\ButtonRenderer;
7 use Tableberg\Renderer\Image\ImageRenderer;
8 use Tableberg\Renderer\ListEl\ListRenderer;
9 use Tableberg\Renderer\Text\TextRenderer;
10
11 use function apply_filters;
12 use function Tableberg\Renderer\getArrayOrNull;
13 use function Tableberg\Renderer\getStringOrNull;
14
15 class CellRenderer {
16 /**
17 * @param CellRenderContext $context
18 * @return string
19 */
20 public function render(CellRenderContext $context) {
21 if (!$context->hasCellCoords) {
22 return '';
23 }
24
25 $styleValues = [
26 'paddingTop' => $context->paddingTop,
27 'paddingRight' => $context->paddingRight,
28 'paddingBottom' => $context->paddingBottom,
29 'paddingLeft' => $context->paddingLeft,
30 'orientation' => $context->orientation,
31 'elementGap' => $context->elementGap,
32 'wrap' => $context->wrap,
33 'verticalAlign' => $context->verticalAlign,
34 'backgroundColor' => $context->backgroundColor,
35 'borderTop' => $context->borderTop,
36 'borderRight' => $context->borderRight,
37 'borderBottom' => $context->borderBottom,
38 'borderLeft' => $context->borderLeft,
39 'borderTopLeftRadius' => $context->borderTopLeftRadius,
40 'borderTopRightRadius' => $context->borderTopRightRadius,
41 'borderBottomRightRadius' => $context->borderBottomRightRadius,
42 'borderBottomLeftRadius' => $context->borderBottomLeftRadius,
43 ];
44
45 $styleValues = $this->overrideStyles(
46 $context->cellStyleOverride,
47 $styleValues
48 );
49
50 $isStickyHeaderCell =
51 $context->stickyHeader &&
52 $context->row === 0 &&
53 $context->tag === 'th';
54
55 $styleParts = ['position:relative'];
56 if ($styleValues['paddingTop'] !== '') {
57 $styleParts[] = 'padding-top:' . $styleValues['paddingTop'];
58 }
59 if ($styleValues['paddingRight'] !== '') {
60 $styleParts[] = 'padding-right:' . $styleValues['paddingRight'];
61 }
62 if ($styleValues['paddingBottom'] !== '') {
63 $styleParts[] = 'padding-bottom:' . $styleValues['paddingBottom'];
64 }
65 if ($styleValues['paddingLeft'] !== '') {
66 $styleParts[] = 'padding-left:' . $styleValues['paddingLeft'];
67 }
68 if ($styleValues['verticalAlign'] !== '') {
69 $styleParts[] = 'vertical-align:' . $styleValues['verticalAlign'];
70 }
71 if ($styleValues['backgroundColor'] !== '') {
72 $styleParts[] = 'background-color:' . $styleValues['backgroundColor'];
73 }
74 if ($styleValues['borderTop'] !== '') {
75 $styleParts[] = 'border-top:' . $styleValues['borderTop'];
76 }
77 if ($styleValues['borderRight'] !== '') {
78 $styleParts[] = 'border-right:' . $styleValues['borderRight'];
79 }
80 if ($styleValues['borderBottom'] !== '') {
81 $styleParts[] = 'border-bottom:' . $styleValues['borderBottom'];
82 }
83 if ($styleValues['borderLeft'] !== '') {
84 $styleParts[] = 'border-left:' . $styleValues['borderLeft'];
85 }
86 if ($styleValues['borderTopLeftRadius'] !== '') {
87 $styleParts[] = 'border-top-left-radius:' . $styleValues['borderTopLeftRadius'];
88 }
89 if ($styleValues['borderTopRightRadius'] !== '') {
90 $styleParts[] = 'border-top-right-radius:' . $styleValues['borderTopRightRadius'];
91 }
92 if ($styleValues['borderBottomRightRadius'] !== '') {
93 $styleParts[] = 'border-bottom-right-radius:' . $styleValues['borderBottomRightRadius'];
94 }
95 if ($styleValues['borderBottomLeftRadius'] !== '') {
96 $styleParts[] = 'border-bottom-left-radius:' . $styleValues['borderBottomLeftRadius'];
97 }
98 if ($context->colSpan === 1 && $context->width !== null && $context->width !== '') {
99 $styleParts[] = 'width:' . $context->width;
100 $styleParts[] = 'min-width:' . $context->width;
101 }
102 if ($context->rowSpan === 1 && $context->height !== null && $context->height !== '') {
103 $styleParts[] = 'height:' . $context->height;
104 $styleParts[] = 'min-height:' . $context->height;
105 }
106
107 if ($isStickyHeaderCell) {
108 $styleParts[] = 'position:sticky';
109 $styleParts[] = 'top:0';
110 $styleParts[] = 'z-index:2';
111
112 if ($styleValues['backgroundColor'] === '') {
113 $styleParts[] = 'background-color:#fff';
114 }
115 }
116
117 $tag = $context->tag === 'th' ? 'th' : 'td';
118 $sortableType = getStringOrNull($context->sortableType);
119
120 $buttonRenderer = new ButtonRenderer();
121 $imageRenderer = new ImageRenderer();
122 $listRenderer = new ListRenderer();
123 $textRenderer = new TextRenderer();
124
125 $elementsHtml = '';
126
127 $isHorizontal = $styleValues['orientation'] === 'horizontal';
128 $implicitAlignment = $this->getUniformElementsAlignment($context->elements);
129
130 foreach ($context->elements as $element) {
131 if (!is_object($element)) {
132 continue;
133 }
134
135 $name = getStringOrNull($element->name->asText()) ?? 'unknown';
136 $attributes = is_array($element->attributes)
137 ? $element->attributes
138 : [];
139
140 $elementHtml = '';
141
142 if ($name === 'text') {
143 $elementHtml = $textRenderer->render($attributes);
144 }
145 if ($name === 'button') {
146 $elementHtml = $buttonRenderer->render($attributes);
147 }
148 if ($name === 'image') {
149 $elementHtml = $imageRenderer->render($attributes);
150 }
151 if ($name === 'list') {
152 $elementHtml = $listRenderer->render($attributes);
153 }
154
155 if ($elementHtml === '') {
156 $elementHtml = apply_filters(
157 'tableberg/render_cell_element_html',
158 '',
159 $name,
160 $attributes,
161 $context
162 );
163
164 if (!is_string($elementHtml)) {
165 $elementHtml = '';
166 }
167 }
168
169 if ($elementHtml === '') {
170 $elementHtml =
171 "<div
172 class='tableberg-cell-element-placeholder'
173 data-element-name='$name'
174 >
175 $name
176 </div>";
177 }
178
179 $elementAlign = $this->getElementAlignment(
180 $name,
181 $attributes
182 );
183 $elementWrapperStyles = [
184 'display:flex',
185 'justify-content:' . $this->alignmentToJustifyContent($elementAlign),
186 ];
187
188 if (!$isHorizontal) {
189 $elementWrapperStyles[] = 'width:100%';
190 }
191
192 $elementsHtml .=
193 "<div
194 class='tableberg-cell-element'
195 style='" . implode(';', $elementWrapperStyles) . "'
196 >
197 {$elementHtml}
198 </div>";
199 }
200
201 $elementsWrapperClasses = ['tableberg-cell-elements'];
202 $elementsWrapperStyleParts = ['display:flex'];
203
204 if ($isHorizontal) {
205 $elementsWrapperClasses[] = 'tableberg-cell-elements-horizontal';
206 }
207
208 $elementsWrapperStyleParts[] = 'flex-direction:' . ($isHorizontal ? 'row' : 'column');
209
210 $verticalAlignFlex = 'center';
211 if ($styleValues['verticalAlign'] === 'top') {
212 $verticalAlignFlex = 'flex-start';
213 } elseif ($styleValues['verticalAlign'] === 'bottom') {
214 $verticalAlignFlex = 'flex-end';
215 }
216
217 if ($isHorizontal) {
218 $elementsWrapperStyleParts[] =
219 'justify-content:' .
220 $this->alignmentToJustifyContent($implicitAlignment);
221 $elementsWrapperStyleParts[] = 'align-items:' . $verticalAlignFlex;
222 } else {
223 $elementsWrapperStyleParts[] = 'justify-content:' . $verticalAlignFlex;
224 $elementsWrapperStyleParts[] = 'align-items:stretch';
225 }
226
227 $elementsWrapperStyleParts[] =
228 'flex-wrap:' . ($styleValues['wrap'] === 'nowrap' ? 'nowrap' : 'wrap');
229
230 if ($styleValues['elementGap'] !== '') {
231 $elementsWrapperStyleParts[] = 'gap:' . $styleValues['elementGap'];
232 }
233
234 $elementsWrapperClassAttr = implode(' ', $elementsWrapperClasses);
235 $elementsWrapperStyleAttr = '';
236 $elementsWrapperStyleAttr = "style='" . implode(';', $elementsWrapperStyleParts) . "'";
237
238 $elementsWrapperHtml =
239 "<div
240 class='{$elementsWrapperClassAttr}'
241 {$elementsWrapperStyleAttr}
242 >
243 {$elementsHtml}
244 </div>";
245
246 $ribbonHtml = apply_filters(
247 'tableberg/render_cell_ribbon_html',
248 '',
249 $context->ribbon,
250 $context
251 );
252
253 if (!is_string($ribbonHtml)) {
254 $ribbonHtml = '';
255 }
256
257 $cellIsSortableHeader = $tag === 'th' && $sortableType !== null;
258 $sortIndicatorHtml = $cellIsSortableHeader ?
259 "<span
260 class='tableberg-sort-indicator'
261 aria-hidden='true'
262 >
263 &#9650;&#9660;
264 </span>" : '';
265
266 $stylesStr = implode(';', $styleParts);
267
268 $attrsStr = ($tag === 'th' && $sortableType !== null) ?
269 "data-sortable='{$sortableType}'
270 role='button'
271 tabindex='0'
272 aria-sort='none'" : '';
273
274 $classAttr = $context->className !== ''
275 ? " class='" . \esc_attr($context->className) . "'"
276 : '';
277
278 $html =
279 "<$tag
280 rowspan='{$context->rowSpan}'
281 colspan='{$context->colSpan}'
282 {$classAttr}
283 style='$stylesStr'
284 data-cell-row='{$context->row}'
285 data-cell-col='{$context->col}'
286 {$attrsStr}
287 >
288 $elementsWrapperHtml
289 $sortIndicatorHtml
290 $ribbonHtml
291 </$tag>";
292
293 return $html;
294 }
295
296 /**
297 * @param string $elementName
298 * @param array<string, mixed> $attributes
299 * @return string
300 */
301 private function getElementAlignment($elementName, $attributes) {
302 if ($elementName === 'icon') {
303 $styles = getArrayOrNull($attributes['styles']) ?? [];
304 $styleAlign = getStringOrNull($styles['align']);
305 if (in_array($styleAlign, ['left', 'center', 'right'], true)) {
306 return $styleAlign;
307 }
308
309 return 'left';
310 }
311
312 $topLevelAlign = getStringOrNull($attributes['align']);
313 if (in_array($topLevelAlign, ['left', 'center', 'right'], true)) {
314 return $topLevelAlign;
315 }
316
317 return 'left';
318 }
319
320 /**
321 * @param string $alignment
322 * @return string
323 */
324 private function alignmentToJustifyContent($alignment) {
325 if ($alignment === 'center') {
326 return 'center';
327 }
328
329 if ($alignment === 'right') {
330 return 'flex-end';
331 }
332
333 return 'flex-start';
334 }
335
336 /**
337 * @param array<int, mixed> $elements
338 * @return string
339 */
340 private function getUniformElementsAlignment($elements) {
341 $resolvedAlignment = null;
342 $hasElements = false;
343
344 foreach ($elements as $element) {
345 if (!is_object($element)) {
346 continue;
347 }
348
349 $name = getStringOrNull($element->name->asText()) ?? 'unknown';
350 $attributes = is_array($element->attributes)
351 ? $element->attributes
352 : [];
353
354 $currentAlignment = $this->getElementAlignment($name, $attributes);
355
356 if (!$hasElements) {
357 $resolvedAlignment = $currentAlignment;
358 $hasElements = true;
359 continue;
360 }
361
362 if ($resolvedAlignment !== $currentAlignment) {
363 return 'left';
364 }
365 }
366
367 return $hasElements ? $resolvedAlignment : 'left';
368 }
369
370 /**
371 * @param array<string, StringAttr|array>|null $cellStyleOverride
372 * @param array<string, string> $styleValues
373 * @return array<string, string>
374 */
375 private function overrideStyles($cellStyleOverride, $styleValues) {
376 if (!is_array($cellStyleOverride)) {
377 return $styleValues;
378 }
379
380 $overridePadding = getArrayOrNull($cellStyleOverride['padding']);
381 if (is_array($overridePadding)) {
382 if (isset($overridePadding['top']) && $overridePadding['top'] instanceof StringAttr) {
383 $styleValues['paddingTop'] = $overridePadding['top']->asAttr();
384 }
385 if (isset($overridePadding['right']) && $overridePadding['right'] instanceof StringAttr) {
386 $styleValues['paddingRight'] = $overridePadding['right']->asAttr();
387 }
388 if (isset($overridePadding['bottom']) && $overridePadding['bottom'] instanceof StringAttr) {
389 $styleValues['paddingBottom'] = $overridePadding['bottom']->asAttr();
390 }
391 if (isset($overridePadding['left']) && $overridePadding['left'] instanceof StringAttr) {
392 $styleValues['paddingLeft'] = $overridePadding['left']->asAttr();
393 }
394 }
395
396 if (isset($cellStyleOverride['backgroundColor'])
397 && $cellStyleOverride['backgroundColor'] instanceof StringAttr) {
398 $styleValues['backgroundColor'] = $cellStyleOverride['backgroundColor']->asAttr();
399 }
400
401 if (isset($cellStyleOverride['orientation'])
402 && $cellStyleOverride['orientation'] instanceof StringAttr) {
403 $orientation = $cellStyleOverride['orientation']->asAttr();
404 $styleValues['orientation'] = $orientation;
405 }
406
407 if (isset($cellStyleOverride['wrap'])
408 && $cellStyleOverride['wrap'] instanceof StringAttr) {
409 $wrap = $cellStyleOverride['wrap']->asAttr();
410 $styleValues['wrap'] = $wrap;
411 }
412
413 if (isset($cellStyleOverride['elementGap'])
414 && $cellStyleOverride['elementGap'] instanceof StringAttr) {
415 $styleValues['elementGap'] = $cellStyleOverride['elementGap']->asAttr();
416 }
417
418 if (isset($cellStyleOverride['verticalAlign'])
419 && $cellStyleOverride['verticalAlign'] instanceof StringAttr) {
420 $verticalAlign = $cellStyleOverride['verticalAlign']->asAttr();
421 $styleValues['verticalAlign'] = $verticalAlign;
422 }
423
424 $overrideBorder = getArrayOrNull($cellStyleOverride['border']);
425 if (is_array($overrideBorder)) {
426 if (isset($overrideBorder['top']) && $overrideBorder['top'] instanceof StringAttr) {
427 $styleValues['borderTop'] = $overrideBorder['top']->asAttr();
428 }
429 if (isset($overrideBorder['right']) && $overrideBorder['right'] instanceof StringAttr) {
430 $styleValues['borderRight'] = $overrideBorder['right']->asAttr();
431 }
432 if (isset($overrideBorder['bottom']) && $overrideBorder['bottom'] instanceof StringAttr) {
433 $styleValues['borderBottom'] = $overrideBorder['bottom']->asAttr();
434 }
435 if (isset($overrideBorder['left']) && $overrideBorder['left'] instanceof StringAttr) {
436 $styleValues['borderLeft'] = $overrideBorder['left']->asAttr();
437 }
438 }
439
440 $overrideBorderRadius = getArrayOrNull($cellStyleOverride['borderRadius']);
441 if (is_array($overrideBorderRadius)) {
442 if (isset($overrideBorderRadius['topLeft'])
443 && $overrideBorderRadius['topLeft'] instanceof StringAttr) {
444 $styleValues['borderTopLeftRadius'] = $overrideBorderRadius['topLeft']->asAttr();
445 }
446 if (isset($overrideBorderRadius['topRight'])
447 && $overrideBorderRadius['topRight'] instanceof StringAttr) {
448 $styleValues['borderTopRightRadius'] = $overrideBorderRadius['topRight']->asAttr();
449 }
450 if (isset($overrideBorderRadius['bottomRight'])
451 && $overrideBorderRadius['bottomRight'] instanceof StringAttr) {
452 $styleValues['borderBottomRightRadius'] = $overrideBorderRadius['bottomRight']->asAttr();
453 }
454 if (isset($overrideBorderRadius['bottomLeft'])
455 && $overrideBorderRadius['bottomLeft'] instanceof StringAttr) {
456 $styleValues['borderBottomLeftRadius'] = $overrideBorderRadius['bottomLeft']->asAttr();
457 }
458 }
459
460 return $styleValues;
461 }
462 }
463