Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
lukeraymonddowning committed Mar 25, 2024
1 parent e5a7abb commit f586204
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 41 deletions.
60 changes: 37 additions & 23 deletions playground/form.php
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
<?php

use Laravel\Prompts\FormBuilder;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\form;
use function Laravel\Prompts\info;
use function Laravel\Prompts\intro;
use function Laravel\Prompts\multiselect;
use function Laravel\Prompts\note;
use function Laravel\Prompts\password;
use function Laravel\Prompts\pause;
use function Laravel\Prompts\select;
use function Laravel\Prompts\spin;
use function Laravel\Prompts\suggest;
use function Laravel\Prompts\text;

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

$responses = form()
->add(fn () => intro('Welcome to Laravel'))
->add(fn () => suggest(
->intro('Welcome to Laravel')
->suggest(
label: 'What is your name?',
placeholder: 'E.g. Taylor Otwell',
options: [
Expand All @@ -29,37 +32,47 @@
'Taylor Otwell',
'Tim MacDonald',
],
validate: fn ($value) => match (true) {
! $value => 'Please enter your name.',
validate: fn($value) => match (true) {
!$value => 'Please enter your name.',
default => null,
},
))
->add(fn () => text(
)
->text(
label: 'Where should we create your project?',
placeholder: 'E.g. ./laravel',
validate: fn ($value) => match (true) {
! $value => 'Please enter a path',
validate: fn($value) => match (true) {
!$value => 'Please enter a path',
$value[0] !== '.' => 'Please enter a relative path',
default => null,
},
), name: 'path')
->add(fn () => password(
name: 'path'
)
->when(
fn(array $responses) => $responses['path'] === './laravel',
fn(array $responses) => info("This is a Laravel project!"),
ignoreWhenReverting: true,
)
->pause()
->submit();

$moreResponses = form()
->password(
label: 'Provide a password',
validate: fn ($value) => match (true) {
! $value => 'Please enter a password.',
validate: fn($value) => match (true) {
!$value => 'Please enter a password.',
strlen($value) < 5 => 'Password should have at least 5 characters.',
default => null,
},
))
->add(fn () => select(
)
->select(
label: 'Pick a project type',
default: 'ts',
options: [
'ts' => 'TypeScript',
'js' => 'JavaScript',
],
))
->add(fn () => multiselect(
)
->multiselect(
label: 'Select additional tools.',
default: ['pint', 'eslint'],
options: [
Expand All @@ -72,27 +85,28 @@
return 'Please select at least one tool.';
}
}
))
)
->add(function () {
$install = confirm(
label: 'Install dependencies?',
);

if ($install) {
spin(fn () => sleep(3), 'Installing dependencies...');
spin(fn() => sleep(3), 'Installing dependencies...');
}

return $install;
}, name: 'install')
->add(fn () => confirm('Finish installation?'))
->add(fn ($responses) => note(<<<EOT
->confirm('Finish installation?')
->add(fn($responses) => note(<<<EOT
Installation complete!
To get started, run:
cd {$responses['path']}
php artisan serve
EOT))
EOT
))
->submit();

var_dump($responses);
var_dump($responses, $moreResponses);
56 changes: 40 additions & 16 deletions src/FormBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,19 @@ class FormBuilder
/**
* Add a new step.
*/
public function add(Closure $step, ?string $name = null): self
public function add(Closure $step, ?string $name = null, bool $ignoreWhenReverting = false): self
{
$this->steps[] = new FormStep($step, $name);
return $this->when(true, $step, $name, $ignoreWhenReverting);
}

/**
* Add a step if the given condition evaluates to true.
*
* @param bool|(Closure(array<mixed>): bool) $condition
*/
public function when(bool|Closure $condition, Closure $step, ?string $name = null, bool $ignoreWhenReverting = false): self
{
$this->steps[] = new FormStep($step, $condition, $name, $ignoreWhenReverting);

return $this;
}
Expand All @@ -40,10 +50,16 @@ public function add(Closure $step, ?string $name = null): self
public function submit(): array
{
$index = 0;
$wasReverted = false;

while ($index < count($this->steps)) {
$step = $this->steps[$index];

if ($wasReverted && $index > 0 && $step->shouldIgnoreWhenReverting($this->responses)) {
$index--;
continue;
}

$wasReverted = false;

$index > 0
Expand All @@ -52,7 +68,10 @@ public function submit(): array
})
: Prompt::preventReverting();

$this->responses[$step->name ?? $index] = $step->run($this->responses);
$this->responses[$step->name ?? $index] = $step->run(
$this->responses,
$this->responses[$step->name ?? $index] ?? null,
);

$wasReverted ? $index-- : $index++;
}
Expand Down Expand Up @@ -154,63 +173,63 @@ public function multisearch(string $label, Closure $options, string $placeholder
*/
public function spin(Closure $callback, string $message = '', ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\spin', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\spin', get_defined_vars(), true);
}

/**
* Display a note.
*/
public function note(string $message, ?string $type = null, ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\note', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\note', get_defined_vars(), true);
}

/**
* Display an error.
*/
public function error(string $message, ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\error', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\error', get_defined_vars(), true);
}

/**
* Display a warning.
*/
public function warning(string $message, ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\warning', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\warning', get_defined_vars(), true);
}

/**
* Display an alert.
*/
public function alert(string $message, ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\alert', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\alert', get_defined_vars(), true);
}

/**
* Display an informational message.
*/
public function info(string $message, ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\info', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\info', get_defined_vars(), true);
}

/**
* Display an introduction.
*/
public function intro(string $message, ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\intro', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\intro', get_defined_vars(), true);
}

/**
* Display a closing message.
*/
public function outro(string $message, ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\outro', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\outro', get_defined_vars(), true);
}

/**
Expand All @@ -221,7 +240,7 @@ public function outro(string $message, ?string $name = null): self
*/
public function table(array|Collection $headers = [], array|Collection|null $rows = null, ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\table', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\table', get_defined_vars(), true);
}

/**
Expand All @@ -235,7 +254,7 @@ public function table(array|Collection $headers = [], array|Collection|null $row
*/
public function progress(string $label, iterable|int $steps, ?Closure $callback = null, string $hint = '', ?string $name = null): self
{
return $this->runPrompt('\\Laravel\\Prompts\\progress', get_defined_vars());
return $this->runPrompt('\\Laravel\\Prompts\\progress', get_defined_vars(), true);
}

/**
Expand All @@ -244,15 +263,20 @@ public function progress(string $label, iterable|int $steps, ?Closure $callback
* @param callable-string $prompt
* @param array<mixed> $arguments
*/
protected function runPrompt(string $prompt, array $arguments): self
protected function runPrompt(string $prompt, array $arguments, bool $ignoreWhenReverting = false): self
{
if (! function_exists($prompt)) {
throw new BadFunctionCallException("The given prompt {$prompt} does not exist.");
}

return $this->add(function (array $responses) use ($prompt, $arguments) {
return $this->add(function (array $responses, mixed $previousResponse) use ($prompt, $arguments) {
unset($arguments['name']);

if (array_key_exists('default', $arguments) && $previousResponse !== null) {
$arguments['default'] = $previousResponse;
}

return $prompt(...$arguments);
}, name: $arguments['name']);
}, name: $arguments['name'], ignoreWhenReverting: $ignoreWhenReverting);
}
}
39 changes: 37 additions & 2 deletions src/FormStep.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,54 @@

class FormStep
{
protected readonly Closure $condition;

public function __construct(
protected readonly Closure $step,
bool|Closure $condition,
public readonly ?string $name,
protected readonly bool $ignoreWhenReverting,
) {
$this->condition = is_bool($condition)
? fn () => $condition
: $condition;
}

/**
* Execute this step.
*
* @param array<mixed> $responses
*/
public function run(array $responses): mixed
public function run(array $responses, mixed $previousResponse): mixed
{
if (! $this->shouldRun($responses)) {
return null;
}

return ($this->step)($responses, $previousResponse);
}

/**
* Whether the step should run based on the given condition.
*
* @param array<mixed> $responses
*/
protected function shouldRun(array $responses): bool
{
return ($this->step)($responses);
return ($this->condition)($responses);
}

/**
* Whether this step should be skipped over when a subsequent step is reverted.
*
* @param array<mixed> $responses
*/
public function shouldIgnoreWhenReverting(array $responses): bool
{
if (! $this->shouldRun($responses)) {
return true;
}

return $this->ignoreWhenReverting;
}
}
Loading

0 comments on commit f586204

Please sign in to comment.