Skip to content

Commit

Permalink
Add a table renderer (#68)
Browse files Browse the repository at this point in the history
* add a table helper

* added docblock for table params

* dealing with the collections in the helper function

appeasing static analysis

* Fix colspan and make border color consistent

* Remove extra whitespace from last column

* Fix border when there are no headers

* Allow passing rows as only argument

* Add `table` playground

* Remove unnecessary type

---------

Co-authored-by: Jess Archer <jess@jessarcher.com>
  • Loading branch information
joetannenbaum and jessarcher authored Sep 14, 2023
1 parent 8544d8c commit 85f0a1e
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 0 deletions.
20 changes: 20 additions & 0 deletions playground/table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

use function Laravel\Prompts\table;

require __DIR__.'/../vendor/autoload.php';

table(
['Name', 'Twitter'],
[
['Taylor Otwell', '@taylorotwell'],
['Dries Vints', '@driesvints'],
['James Brooks', '@jbrooksuk'],
['Nuno Maduro', '@enunomaduro'],
['Mior Muhammad Zaki', '@crynobone'],
['Jess Archer', '@jessarchercodes'],
['Guus Leeuw', '@phpguus'],
['Tim MacDonald', '@timacdonald87'],
['Joe Dixon', '@_joedixon'],
],
);
3 changes: 3 additions & 0 deletions src/Concerns/Themes.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Laravel\Prompts\SelectPrompt;
use Laravel\Prompts\Spinner;
use Laravel\Prompts\SuggestPrompt;
use Laravel\Prompts\Table;
use Laravel\Prompts\TextPrompt;
use Laravel\Prompts\Themes\Default\ConfirmPromptRenderer;
use Laravel\Prompts\Themes\Default\MultiSelectPromptRenderer;
Expand All @@ -20,6 +21,7 @@
use Laravel\Prompts\Themes\Default\SelectPromptRenderer;
use Laravel\Prompts\Themes\Default\SpinnerRenderer;
use Laravel\Prompts\Themes\Default\SuggestPromptRenderer;
use Laravel\Prompts\Themes\Default\TableRenderer;
use Laravel\Prompts\Themes\Default\TextPromptRenderer;

trait Themes
Expand All @@ -45,6 +47,7 @@ trait Themes
SuggestPrompt::class => SuggestPromptRenderer::class,
Spinner::class => SpinnerRenderer::class,
Note::class => NoteRenderer::class,
Table::class => TableRenderer::class,
],
];

Expand Down
71 changes: 71 additions & 0 deletions src/Table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

namespace Laravel\Prompts;

use Illuminate\Support\Collection;

class Table extends Prompt
{
/**
* The table headers.
*
* @var array<int, string|array<int, string>>
*/
public array $headers;

/**
* The table rows.
*
* @var array<int, array<int, string>>
*/
public array $rows;

/**
* Create a new Table instance.
*
* @param array<int, string|array<int, string>>|Collection<int, string|array<int, string>> $headers
* @param array<int, array<int, string>>|Collection<int, array<int, string>> $rows
*
* @phpstan-param ($rows is null ? list<list<string>>|Collection<int, list<string>> : list<string|list<string>>|Collection<int, string|list<string>>) $headers
*/
public function __construct(array|Collection $headers = [], array|Collection $rows = null)
{
if ($rows === null) {
$rows = $headers;
$headers = [];
}

$this->headers = $headers instanceof Collection ? $headers->all() : $headers;
$this->rows = $rows instanceof Collection ? $rows->all() : $rows;
}

/**
* Display the table.
*/
public function display(): void
{
$this->prompt();
}

/**
* Display the table.
*/
public function prompt(): bool
{
$this->capturePreviousNewLines();

$this->state = 'submit';

static::output()->write($this->renderTheme());

return true;
}

/**
* Get the value of the prompt.
*/
public function value(): bool
{
return true;
}
}
42 changes: 42 additions & 0 deletions src/Themes/Default/TableRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Laravel\Prompts\Themes\Default;

use Laravel\Prompts\Output\BufferedConsoleOutput;
use Laravel\Prompts\Table;
use Symfony\Component\Console\Helper\Table as SymfonyTable;
use Symfony\Component\Console\Helper\TableStyle;

class TableRenderer extends Renderer
{
/**
* Render the table.
*/
public function __invoke(Table $table): string
{
$tableStyle = (new TableStyle())
->setHorizontalBorderChars('')
->setVerticalBorderChars('', '')
->setCellHeaderFormat($this->dim('<fg=default>%s</>'))
->setCellRowFormat('<fg=default>%s</>');

if (empty($table->headers)) {
$tableStyle->setCrossingChars('', '', '', '', '', '┘</>', '', '', '', '<fg=gray>┌', '', '');
} else {
$tableStyle->setCrossingChars('', '<fg=gray>┌', '', '', '', '┘</>', '', '', '');
}

$buffered = new BufferedConsoleOutput();

(new SymfonyTable($buffered))
->setHeaders($table->headers)
->setRows($table->rows)
->setStyle($tableStyle)
->render();

collect(explode(PHP_EOL, trim($buffered->content(), PHP_EOL)))
->each(fn ($line) => $this->line(' '.$line));

return $this;
}
}
11 changes: 11 additions & 0 deletions src/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,14 @@ function outro(string $message): void
{
(new Note($message, 'outro'))->display();
}

/**
* Display a table.
*
* @param array<int, string|array<int, string>>|Collection<int, string|array<int, string>> $headers
* @param array<int, array<int, string>>|Collection<int, array<int, string>> $rows
*/
function table(array|Collection $headers = [], array|Collection $rows = null): void
{
(new Table($headers, $rows))->display();
}
103 changes: 103 additions & 0 deletions tests/Feature/TableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

use Laravel\Prompts\Prompt;

use function Laravel\Prompts\table;

it('renders a table with headers', function ($headers, $rows) {
Prompt::fake();

table($headers, $rows);

Prompt::assertStrippedOutputContains(<<<'OUTPUT'
┌────────────────────┬──────────────────┐
│ Name │ Twitter │
├────────────────────┼──────────────────┤
│ Taylor Otwell │ @taylorotwell │
│ Dries Vints │ @driesvints │
│ James Brooks │ @jbrooksuk │
│ Nuno Maduro │ @enunomaduro │
│ Mior Muhammad Zaki │ @crynobone │
│ Jess Archer │ @jessarchercodes │
│ Guus Leeuw │ @phpguus │
│ Tim MacDonald │ @timacdonald87 │
│ Joe Dixon │ @_joedixon │
└────────────────────┴──────────────────┘
OUTPUT);
})->with([
'arrays' => [
['Name', 'Twitter'],
[
['Taylor Otwell', '@taylorotwell'],
['Dries Vints', '@driesvints'],
['James Brooks', '@jbrooksuk'],
['Nuno Maduro', '@enunomaduro'],
['Mior Muhammad Zaki', '@crynobone'],
['Jess Archer', '@jessarchercodes'],
['Guus Leeuw', '@phpguus'],
['Tim MacDonald', '@timacdonald87'],
['Joe Dixon', '@_joedixon'],
],
],
'collections' => [
collect(['Name', 'Twitter']),
collect([
['Taylor Otwell', '@taylorotwell'],
['Dries Vints', '@driesvints'],
['James Brooks', '@jbrooksuk'],
['Nuno Maduro', '@enunomaduro'],
['Mior Muhammad Zaki', '@crynobone'],
['Jess Archer', '@jessarchercodes'],
['Guus Leeuw', '@phpguus'],
['Tim MacDonald', '@timacdonald87'],
['Joe Dixon', '@_joedixon'],
]),
],
]);

it('renders a table without headers', function ($rows) {
Prompt::fake();

table($rows);

Prompt::assertStrippedOutputContains(<<<'OUTPUT'
┌────────────────────┬──────────────────┐
│ Taylor Otwell │ @taylorotwell │
│ Dries Vints │ @driesvints │
│ James Brooks │ @jbrooksuk │
│ Nuno Maduro │ @enunomaduro │
│ Mior Muhammad Zaki │ @crynobone │
│ Jess Archer │ @jessarchercodes │
│ Guus Leeuw │ @phpguus │
│ Tim MacDonald │ @timacdonald87 │
│ Joe Dixon │ @_joedixon │
└────────────────────┴──────────────────┘
OUTPUT);
})->with([
'arrays' => [
[
['Taylor Otwell', '@taylorotwell'],
['Dries Vints', '@driesvints'],
['James Brooks', '@jbrooksuk'],
['Nuno Maduro', '@enunomaduro'],
['Mior Muhammad Zaki', '@crynobone'],
['Jess Archer', '@jessarchercodes'],
['Guus Leeuw', '@phpguus'],
['Tim MacDonald', '@timacdonald87'],
['Joe Dixon', '@_joedixon'],
],
],
'collections' => [
collect([
['Taylor Otwell', '@taylorotwell'],
['Dries Vints', '@driesvints'],
['James Brooks', '@jbrooksuk'],
['Nuno Maduro', '@enunomaduro'],
['Mior Muhammad Zaki', '@crynobone'],
['Jess Archer', '@jessarchercodes'],
['Guus Leeuw', '@phpguus'],
['Tim MacDonald', '@timacdonald87'],
['Joe Dixon', '@_joedixon'],
]),
],
]);

0 comments on commit 85f0a1e

Please sign in to comment.