Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a table renderer #68

Merged
merged 9 commits into from
Sep 14, 2023
Merged
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'],
]),
],
]);