PluginProbe
Tableberg – Simple Gutenberg Table Block / 1.1.5
Tableberg – Simple Gutenberg Table Block v1.1.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
← All changes | renderer/Table/TableRenderer.php +109 -25 1.1.21.1.5 View file →
@@ -69,8 +69,19 @@
69 69 );
70 70 $stickyHeader = !empty($stickySettings['stickyHeader']);
71 71 $stickyFirstCol = !empty($stickySettings['stickyFirstCol']);
72 72
73 + // Row-only/column-only is Pro-owned. Free's empty default preserves
74 + // the attribute without enabling its editor or frontend behavior.
75 + $innerBorderType = apply_filters(
76 + 'tableberg/inner_border_type',
77 + '',
78 + $attrs->attrs
79 + );
80 + if (!in_array($innerBorderType, ['row', 'col'], true)) {
81 + $innerBorderType = '';
82 + }
83 +
73 84 $caption = $attrs->table->caption;
74 85 $tableWidth = trim($attrs->table->tableWidth->asAttr());
75 86 $tableAlignment = $attrs->table->tableAlignment->asAttr();
76 87 $cellSpacingHorizontal = trim($attrs->table->cellSpacing->horizontal->asAttr());
@@ -79,8 +90,24 @@
79 90 $tableBorderRight = trim($attrs->table->tableBorder->right->asAttr());
80 91 $tableBorderBottom = trim($attrs->table->tableBorder->bottom->asAttr());
81 92 $tableBorderLeft = trim($attrs->table->tableBorder->left->asAttr());
82 93 $fixedColumnWidths = $attrs->table->fixedColumnWidths->value();
94 + $tableRadius = [
95 + 'topLeft' => $attrs->cellDefaults->styles->borderRadius->topLeft->asAttr(),
96 + 'topRight' => $attrs->cellDefaults->styles->borderRadius->topRight->asAttr(),
97 + 'bottomRight' => $attrs->cellDefaults->styles->borderRadius->bottomRight->asAttr(),
98 + 'bottomLeft' => $attrs->cellDefaults->styles->borderRadius->bottomLeft->asAttr(),
99 + ];
100 + $isZeroRadius = function ($value) {
101 + $trimmed = trim((string) $value);
102 + return $trimmed === '' || preg_match('/^0(?:\.0+)?[a-z%]*$/i', $trimmed) === 1;
103 + };
104 + $hasRoundedCorners = count(array_filter(
105 + $tableRadius,
106 + function ($value) use ($isZeroRadius) {
107 + return !$isZeroRadius($value);
108 + }
109 + )) > 0;
83 110
84 111 $isHorizontalSpacingZero = $cellSpacingHorizontal === '0';
85 112 $isVerticalSpacingZero = $cellSpacingVertical === '0';
86 113 $hasCellSpacing = !$isHorizontalSpacingZero || !$isVerticalSpacingZero;
@@ -106,8 +133,11 @@
106 133 );
107 134
108 135 $tableStyles = [
109 136 'border-collapse: ' . ($hasCellSpacing ? 'separate' : 'collapse'),
137 + // Neutralize theme-level table borders. Tableberg owns its
138 + // borders through the table/cell controls and rounded wrapper.
139 + 'border: 0',
110 140 ];
111 141
112 142 if ($hasCellSpacing) {
113 143 $tableStyles[] = "border-spacing: {$cellSpacingHorizontal} {$cellSpacingVertical}";
@@ -119,21 +149,21 @@
119 149 } else {
120 150 $tableStyles[] = 'width: 100%';
121 151 }
122 152
123 - if ($tableBorderTop !== '') {
153 + if (!$hasRoundedCorners && $tableBorderTop !== '') {
124 154 $tableStyles[] = "border-top: {$tableBorderTop}";
125 155 }
126 156
127 - if ($tableBorderRight !== '') {
157 + if (!$hasRoundedCorners && $tableBorderRight !== '') {
128 158 $tableStyles[] = "border-right: {$tableBorderRight}";
129 159 }
130 160
131 - if ($tableBorderBottom !== '') {
161 + if (!$hasRoundedCorners && $tableBorderBottom !== '') {
132 162 $tableStyles[] = "border-bottom: {$tableBorderBottom}";
133 163 }
134 164
135 - if ($tableBorderLeft !== '') {
165 + if (!$hasRoundedCorners && $tableBorderLeft !== '') {
136 166 $tableStyles[] = "border-left: {$tableBorderLeft}";
137 167 }
138 168
139 169 $tableStyleAttr = "style='" . implode('; ', $tableStyles) . ";'";
@@ -244,13 +274,18 @@
244 274 'bottomLeft' => $attrs->cellDefaults->styles->borderRadius->bottomLeft->asAttr(),
245 275 ],
246 276 ];
247 277
248 - // The table border radius rounds the whole table's outer corners.
249 - // border-radius on collapsed-border tables/cells is ignored by
250 - // browsers, so it is rendered on the wrapper (with overflow:hidden)
251 - // and removed from the individual cells here.
252 - $tableRadius = $globalCellStyles['borderRadius'];
278 + // A collapsed table cannot reliably round its own explicit border.
279 + // The wrapper owns only the table border and clips the cell grid to
280 + // it. Cell borders stay on cells; copying them to the wrapper would
281 + // add an outline that the user did not configure as a table border.
282 + $tableOuterBorder = [
283 + 'top' => $tableBorderTop,
284 + 'right' => $tableBorderRight,
285 + 'bottom' => $tableBorderBottom,
286 + 'left' => $tableBorderLeft,
287 + ];
253 288 $globalCellStyles['borderRadius'] = [
254 289 'topLeft' => '',
255 290 'topRight' => '',
256 291 'bottomRight' => '',
@@ -269,8 +304,32 @@
269 304 $rowBackgroundColor = isset($rowStyles['backgroundColor']) && is_string($rowStyles['backgroundColor'])
270 305 ? $rowStyles['backgroundColor']
271 306 : '';
272 307
308 + $cellStylesForRow = $globalCellStyles;
309 +
310 + // Header/footer/even/odd are table-wide defaults, resolved per
311 + // row here the same way `cell/index.tsx`'s
312 + // `useRowBackgroundStyleKey` does in the editor — header/footer
313 + // don't consume a slot in the even/odd count.
314 + $isHeaderRow = $headerEnabled && $row === 0;
315 + $isFooterRow = $footerEnabled && $rows > 0 && $row === $rows - 1;
316 +
317 + if ($isHeaderRow) {
318 + $rowPositionBackground = $attrs->cellDefaults->styles->headerBackgroundColor->asAttr();
319 + } elseif ($isFooterRow) {
320 + $rowPositionBackground = $attrs->cellDefaults->styles->footerBackgroundColor->asAttr();
321 + } else {
322 + $dataRowPosition = $headerEnabled ? $row - 1 : $row;
323 + $rowPositionBackground = ($dataRowPosition % 2 === 0)
324 + ? $attrs->cellDefaults->styles->oddRowBackgroundColor->asAttr()
325 + : $attrs->cellDefaults->styles->evenRowBackgroundColor->asAttr();
326 + }
327 +
328 + if ($rowPositionBackground !== '') {
329 + $cellStylesForRow['backgroundColor'] = $rowPositionBackground;
330 + }
331 +
273 332 // A row with its own background would otherwise never show
274 333 // through: every cell paints its own background over the
275 334 // `<tr>`'s, and cells fall back to the table-wide default
276 335 // whenever they carry no colour of their own. So for this row's
@@ -275,9 +334,8 @@
275 334 // `<tr>`'s, and cells fall back to the table-wide default
276 335 // whenever they carry no colour of their own. So for this row's
277 336 // cells, that fallback is cleared — a cell/column colour on top
278 337 // of it (applied below by the pro filter) still wins either way.
279 - $cellStylesForRow = $globalCellStyles;
280 338 if ($rowBackgroundColor !== '') {
281 339 $cellStylesForRow['backgroundColor'] = '';
282 340 }
283 341
@@ -345,9 +403,12 @@
345 403 $stickyHeader,
346 404 $stickyFirstCol,
347 405 $this->getCellClassName($cell),
348 406 $cell instanceof CellData ? $cell->attrs : [],
349 - $this->isCellEmpty($cell)
407 + $this->isCellEmpty($cell),
408 + $innerBorderType,
409 + $rows,
410 + $cols
350 411 )
351 412 );
352 413 }
353 414
@@ -385,27 +446,29 @@
385 446 'sortable' => $sortType,
386 447 ];
387 448 }
388 449
389 - $columnsJson = json_encode($columnsData);
450 + $columnsJson = wp_json_encode($columnsData);
390 451 if (!is_string($columnsJson)) {
391 452 $columnsJson = '{}';
392 453 }
454 + $columnsJson = esc_attr($columnsJson);
393 455
394 - $paginationJson = json_encode($paginationConfig);
456 + $paginationJson = wp_json_encode($paginationConfig);
395 457 if (!is_string($paginationJson)) {
396 458 $paginationJson = '{}';
397 459 }
460 + $paginationJson = esc_attr($paginationJson);
398 461
462 + $searchPlaceholderAttr = esc_attr($searchPlaceholder);
463 + $searchPositionAttr = esc_attr($searchPosition);
464 + $searchHighlightColorAttr = esc_attr($searchHighlightColor);
465 +
399 466 $wrapperClass = trim("tableberg-table-wrapper {$wrapperAlignmentClass}");
400 467
401 - // Round the whole table's outer corners by clipping the wrapper.
468 + // Round the whole table's outer corners on the wrapper.
402 469 // Zero radii are skipped so a table without any actual rounding does
403 470 // not carry pointless border-radius declarations.
404 - $isZeroRadius = function ($value) {
405 - $trimmed = trim((string) $value);
406 - return $trimmed === '' || preg_match('/^0(?:\.0+)?[a-z%]*$/i', $trimmed) === 1;
407 - };
408 471 $wrapperStyles = [];
409 472 if (!$isZeroRadius($tableRadius['topLeft'])) {
410 473 $wrapperStyles[] = 'border-top-left-radius:' . $tableRadius['topLeft'];
411 474 }
@@ -418,8 +481,24 @@
418 481 if (!$isZeroRadius($tableRadius['bottomLeft'])) {
419 482 $wrapperStyles[] = 'border-bottom-left-radius:' . $tableRadius['bottomLeft'];
420 483 }
421 484
485 + if (!empty($wrapperStyles)) {
486 + if ($tableOuterBorder['top'] !== '') {
487 + $wrapperStyles[] = 'border-top:' . $tableOuterBorder['top'];
488 + }
489 + if ($tableOuterBorder['right'] !== '') {
490 + $wrapperStyles[] = 'border-right:' . $tableOuterBorder['right'];
491 + }
492 + if ($tableOuterBorder['bottom'] !== '') {
493 + $wrapperStyles[] = 'border-bottom:' . $tableOuterBorder['bottom'];
494 + }
495 + if ($tableOuterBorder['left'] !== '') {
496 + $wrapperStyles[] = 'border-left:' . $tableOuterBorder['left'];
497 + }
498 + $wrapperStyles[] = 'box-sizing:border-box';
499 + }
500 +
422 501 // A table wider than its wrapper must scroll inside the wrapper
423 502 // instead of pushing the whole page sideways, so the wrapper is a
424 503 // scroll container by default.
425 504 //
@@ -431,10 +510,8 @@
431 510 // scrolling wins and the header sticks within the wrapper.
432 511 //
433 512 // A rounded wrapper always needs the clip, so it opts in regardless.
434 513 $pageStickyHeader = $stickyHeader && !$stickyFirstCol;
435 - $hasRoundedCorners = !empty($wrapperStyles);
436 -
437 514 if ($hasRoundedCorners || !$pageStickyHeader) {
438 515 // `auto`, not `hidden`: both clip to the rounded corners, but
439 516 // `hidden` would also swallow a table wider than the wrapper.
440 517 // Inline, so it also wins over the `.tableberg-scroll-x` rule.
@@ -494,11 +571,11 @@
494 571 data-tableberg-sortable='$sortingBoolAsStr'
495 572 data-tableberg-columns='$columnsJson'
496 573 data-tableberg-pagination='$paginationJson'
497 574 data-tableberg-search-enabled='$searchEnabledAsStr'
498 - data-tableberg-search-placeholder='$searchPlaceholder'
499 - data-tableberg-search-position='$searchPosition'
500 - data-tableberg-search-highlight-color='$searchHighlightColor'
575 + data-tableberg-search-placeholder='$searchPlaceholderAttr'
576 + data-tableberg-search-position='$searchPositionAttr'
577 + data-tableberg-search-highlight-color='$searchHighlightColorAttr'
501 578 data-tableberg-header='$headerBoolAsStr'
502 579 data-tableberg-footer='$footerBoolAsStr'
503 580 {$responsiveDataAttrs}
504 581 >
@@ -570,8 +647,15 @@
570 647 $mobileMaxWidth = max(1, (int) $mobile->maxWidth->value());
571 648 $tabletStackCount = max(1, (int) $tablet->stackCount->value());
572 649 $mobileStackCount = max(1, (int) $mobile->stackCount->value());
573 650
651 + // Repeating the first column in every stack row is a pro option. Only
652 + // the licensed pro plugin turns this filter on, so the saved value is
653 + // ignored without a valid licence.
654 + $proActive = (bool) apply_filters('tableberg/is_pro_runtime_active', false);
655 + $tabletRepeatFirstCol = $proActive ? $tablet->repeatFirstCol->asAttr() : '';
656 + $mobileRepeatFirstCol = $proActive ? $mobile->repeatFirstCol->asAttr() : '';
657 +
574 658 return "
575 659 data-tableberg-responsive='true'
576 660 data-tableberg-rows='{$rows}'
577 661 data-tableberg-cols='{$cols}'
@@ -579,15 +663,15 @@
579 663 data-tableberg-tablet-width='{$tabletMaxWidth}'
580 664 data-tableberg-tablet-mode='{$tablet->mode->asAttr()}'
581 665 data-tableberg-tablet-transpose='{$tablet->transpose->asAttr()}'
582 666 data-tableberg-tablet-count='{$tabletStackCount}'
583 - data-tableberg-tablet-repeat-first-col='{$tablet->repeatFirstCol->asAttr()}'
667 + data-tableberg-tablet-repeat-first-col='{$tabletRepeatFirstCol}'
584 668 data-tableberg-mobile-enabled='{$mobile->enabled->asAttr()}'
585 669 data-tableberg-mobile-width='{$mobileMaxWidth}'
586 670 data-tableberg-mobile-mode='{$mobile->mode->asAttr()}'
587 671 data-tableberg-mobile-transpose='{$mobile->transpose->asAttr()}'
588 672 data-tableberg-mobile-count='{$mobileStackCount}'
589 - data-tableberg-mobile-repeat-first-col='{$mobile->repeatFirstCol->asAttr()}'
673 + data-tableberg-mobile-repeat-first-col='{$mobileRepeatFirstCol}'
590 674 ";
591 675 }
592 676
593 677 private function getCell($row, $col, $cells) {