Closed joetannenbaum closed 1 year ago
Hey @joetannenbaum, thanks for this!
I think this is a nice addition and makes sense to provide a nice way to create tables consistent with the spacing and theme of Prompts.
There was a bug when using colspans because Symfony doesn't strip the invisible formatting characters from the border format before calculating widths. I've tweaked how the border color is applied, and it seems to work well:
Before | After |
---|---|
I've also removed the extra right padding from the last column and changed the border color from "dim" to "gray" to match the border color of the other boxes (these didn't look different with the terminal theme in your screenshot).
I've also fixed some weirdness when no headers were passed:
Before | After |
---|---|
And I've also made it so that when only the first argument is provided, they will become the rows.
Thanks @jessarcher! I feel like I created more work for you than solved something with this one, I'll keep an eye out for more edge cases and alternate scenarios in my next PR.
Love this package, I'll keep thinking of ways to contribute!
All good @joetannenbaum! It was fun :)
I also played around with setting a minimum width on the table to match the other boxes:
The code is gnarly, though - I had to calculate the width of each column and then divide the remaining space evenly between them and assign any remainder to the first column. It gets especially tricky with colspans where the width gets divided amongst the columns it spans. Might stash that for now and come back to it if it stays in my head.
Ooooooh I love the way that looks though! Great idea. Might tinker with it myself and see if I can get anywhere.
Here's the code I wrote if you're interested, as I'm not going to PR it. I'm sure it could be simplified, but I'm still unsure if it's worth the complexity and potential breaking scenarios I haven't considered (like table separates, row spans, etc.)
It also has to do some weird stuff to align with how Symfony determines the col widths when there are col spans. It would be much nicer to handle this in Symfony rather than here, or at least publically expose the methods they're using so we don't need to duplicate it with potential inconsistencies and future breakages that may occur if they change things and don't consider it a breaking change because it's not part of the public API.
diff --git a/src/Themes/Default/TableRenderer.php b/src/Themes/Default/TableRenderer.php
index 185f450..623f9d0 100644
--- a/src/Themes/Default/TableRenderer.php
+++ b/src/Themes/Default/TableRenderer.php
@@ -5,6 +5,7 @@
use Laravel\Prompts\Output\BufferedConsoleOutput;
use Laravel\Prompts\Table;
use Symfony\Component\Console\Helper\Table as SymfonyTable;
+use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Helper\TableStyle;
class TableRenderer extends Renderer
@@ -29,6 +30,7 @@ public function __invoke(Table $table): string
$buffered = new BufferedConsoleOutput();
(new SymfonyTable($buffered))
+ ->setColumnWidths($this->minWidths($table))
->setHeaders($table->headers)
->setRows($table->rows)
->setStyle($tableStyle)
@@ -39,4 +41,45 @@ public function __invoke(Table $table): string
return $this;
}
+
+ protected function minWidths(Table $table): array
+ {
+ $rows = collect([
+ ...(! is_array($table->headers[0]) ? [$table->headers] : $table->headers),
+ ...$table->rows,
+ ]);
+
+ $rowColWidths = $rows->map(fn ($row) => collect($row)->reduce(function (array $carry, $cell) {
+ return [
+ ...$carry,
+ ...match(true) {
+ $cell instanceof TableCell && mb_strwidth($cell) => array_pad(array_map(mb_strwidth(...), mb_str_split($cell, ceil(mb_strwidth($cell) / $cell->getColspan()))), $cell->getColspan(), 0),
+ $cell instanceof TableCell => array_fill(0, $cell->getColspan(), 0),
+ default => [mb_strwidth($cell)],
+ },
+ ];
+ }, []));
+
+ $colWidths = array_map(max(...), ...$rowColWidths);
+
+ $colCount = count($colWidths);
+
+ $overallWidth = array_sum($colWidths) + ($colCount * 3) + 2;
+
+ $minWidth = min(65, $table->terminal()->cols() - 1);
+
+ $spaceRemaining = $minWidth - $overallWidth;
+
+ if ($spaceRemaining < 1) {
+ return $colWidths;
+ }
+
+ $divided = floor($spaceRemaining / $colCount);
+ $remainder = $spaceRemaining % $colCount;
+
+ $minWidths = collect($colWidths)->map(fn ($width) => $width + $divided)->all();
+ $minWidths[0] += $remainder;
+
+ return $minWidths;
+ }
}
Oh and speaking of Symfony, if you're interested in trying a PR there, I think it would make sense if they used their Helper::removeDecoration
method before calculating the column separator width in the Table::getColumnSeparatorWidth
method, as they do in a few other places.
That would allow us to use your original approach for changing the border color which was a lot nicer. We'd need to increase the minimum required Symfony version though, which probably isn't ideal - at least for now.
This PR adds a table helper that renders a table matching the style of the rest of the package components.
I'm not sure if adding more non-interactive output is on the roadmap for the package, but I've had many use cases for this and it's nice that the output is cohesive.
It's a simple wrapper for the existing Symfony console table component:
In context it looks like this (I played with both
cyan
anddim
for the table headers and thoughtdim
looked better in the end):