PluginProbe
Tableberg – Simple Gutenberg Table Block / 1.1.1
Tableberg – Simple Gutenberg Table Block v1.1.1
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.1.1, at renderer/Cell/CellRenderer.php

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