Skip to content

Commit

Permalink
Add transformation support (#156)
Browse files Browse the repository at this point in the history
* Add transformation support

* Formatting

* Add transformation support to FormBuilder

---------

Co-authored-by: Jess Archer <[email protected]>
  • Loading branch information
emenkens and jessarcher authored Aug 7, 2024
1 parent a86e8d2 commit 72e5035
Show file tree
Hide file tree
Showing 23 changed files with 194 additions and 24 deletions.
1 change: 1 addition & 0 deletions playground/text.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
default => null,
},
hint: 'We will never share your email address with anyone else.',
transform: fn ($value) => strtolower($value),
);

var_dump($email);
Expand Down
4 changes: 2 additions & 2 deletions playground/textarea.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

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

$email = textarea(
$story = textarea(
label: 'Tell me a story',
placeholder: 'Weave me a tale',
);

var_dump($email);
var_dump($story);

echo str_repeat(PHP_EOL, 5);
3 changes: 3 additions & 0 deletions src/ConfirmPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Laravel\Prompts;

use Closure;

class ConfirmPrompt extends Prompt
{
/**
Expand All @@ -20,6 +22,7 @@ public function __construct(
public bool|string $required = false,
public mixed $validate = null,
public string $hint = '',
public ?Closure $transform = null,
) {
$this->confirmed = $default;

Expand Down
18 changes: 9 additions & 9 deletions src/FormBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,23 @@ public function submit(): array
/**
* Prompt the user for text input.
*/
public function text(string $label, string $placeholder = '', string $default = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null): self
public function text(string $label, string $placeholder = '', string $default = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null, ?Closure $transform = null): self
{
return $this->runPrompt(text(...), get_defined_vars());
}

/**
* Prompt the user for multiline text input.
*/
public function textarea(string $label, string $placeholder = '', string $default = '', bool|string $required = false, ?Closure $validate = null, string $hint = '', int $rows = 5, ?string $name = null): self
public function textarea(string $label, string $placeholder = '', string $default = '', bool|string $required = false, ?Closure $validate = null, string $hint = '', int $rows = 5, ?string $name = null, ?Closure $transform = null): self
{
return $this->runPrompt(textarea(...), get_defined_vars());
}

/**
* Prompt the user for input, hiding the value.
*/
public function password(string $label, string $placeholder = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null): self
public function password(string $label, string $placeholder = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null, ?Closure $transform = null): self
{
return $this->runPrompt(password(...), get_defined_vars());
}
Expand All @@ -105,7 +105,7 @@ public function password(string $label, string $placeholder = '', bool|string $r
* @param array<int|string, string>|Collection<int|string, string> $options
* @param true|string $required
*/
public function select(string $label, array|Collection $options, int|string|null $default = null, int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?string $name = null): self
public function select(string $label, array|Collection $options, int|string|null $default = null, int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?string $name = null, ?Closure $transform = null): self
{
return $this->runPrompt(select(...), get_defined_vars());
}
Expand All @@ -116,15 +116,15 @@ public function select(string $label, array|Collection $options, int|string|null
* @param array<int|string, string>|Collection<int|string, string> $options
* @param array<int|string>|Collection<int, int|string> $default
*/
public function multiselect(string $label, array|Collection $options, array|Collection $default = [], int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?string $name = null): self
public function multiselect(string $label, array|Collection $options, array|Collection $default = [], int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?string $name = null, ?Closure $transform = null): self
{
return $this->runPrompt(multiselect(...), get_defined_vars());
}

/**
* Prompt the user to confirm an action.
*/
public function confirm(string $label, bool $default = true, string $yes = 'Yes', string $no = 'No', bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null): self
public function confirm(string $label, bool $default = true, string $yes = 'Yes', string $no = 'No', bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null, ?Closure $transform = null): self
{
return $this->runPrompt(confirm(...), get_defined_vars());
}
Expand All @@ -142,7 +142,7 @@ public function pause(string $message = 'Press enter to continue...', ?string $n
*
* @param array<string>|Collection<int, string>|Closure(string): array<string> $options
*/
public function suggest(string $label, array|Collection|Closure $options, string $placeholder = '', string $default = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null): self
public function suggest(string $label, array|Collection|Closure $options, string $placeholder = '', string $default = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null, ?Closure $transform = null): self
{
return $this->runPrompt(suggest(...), get_defined_vars());
}
Expand All @@ -153,7 +153,7 @@ public function suggest(string $label, array|Collection|Closure $options, string
* @param Closure(string): array<int|string, string> $options
* @param true|string $required
*/
public function search(string $label, Closure $options, string $placeholder = '', int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?string $name = null): self
public function search(string $label, Closure $options, string $placeholder = '', int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?string $name = null, ?Closure $transform = null): self
{
return $this->runPrompt(search(...), get_defined_vars());
}
Expand All @@ -163,7 +163,7 @@ public function search(string $label, Closure $options, string $placeholder = ''
*
* @param Closure(string): array<int|string, string> $options
*/
public function multisearch(string $label, Closure $options, string $placeholder = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?string $name = null): self
public function multisearch(string $label, Closure $options, string $placeholder = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?string $name = null, ?Closure $transform = null): self
{
return $this->runPrompt(multisearch(...), get_defined_vars());
}
Expand Down
1 change: 1 addition & 0 deletions src/MultiSearchPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function __construct(
public bool|string $required = false,
public mixed $validate = null,
public string $hint = '',
public ?Closure $transform = null,
) {
$this->trackTypedValue(submit: false, ignore: fn ($key) => Key::oneOf([Key::SPACE, Key::HOME, Key::END, Key::CTRL_A, Key::CTRL_E], $key) && $this->highlighted !== null);

Expand Down
2 changes: 2 additions & 0 deletions src/MultiSelectPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Laravel\Prompts;

use Closure;
use Illuminate\Support\Collection;

class MultiSelectPrompt extends Prompt
Expand Down Expand Up @@ -43,6 +44,7 @@ public function __construct(
public bool|string $required = false,
public mixed $validate = null,
public string $hint = '',
public ?Closure $transform = null,
) {
$this->options = $options instanceof Collection ? $options->all() : $options;
$this->default = $default instanceof Collection ? $default->all() : $default;
Expand Down
3 changes: 3 additions & 0 deletions src/PasswordPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Laravel\Prompts;

use Closure;

class PasswordPrompt extends Prompt
{
use Concerns\TypedValue;
Expand All @@ -15,6 +17,7 @@ public function __construct(
public bool|string $required = false,
public mixed $validate = null,
public string $hint = '',
public ?Closure $transform = null,
) {
$this->trackTypedValue();
}
Expand Down
31 changes: 28 additions & 3 deletions src/Prompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ abstract class Prompt
*/
public bool|string $required;

/**
* The transformation callback.
*/
public ?Closure $transform = null;

/**
* The validator callback or rules.
*/
Expand Down Expand Up @@ -140,7 +145,7 @@ public function prompt(): mixed
throw new FormRevertedException;
}

return $this->value();
return $this->transformedValue();
}
}
} finally {
Expand Down Expand Up @@ -277,7 +282,7 @@ protected function render(): void
*/
protected function submit(): void
{
$this->validate($this->value());
$this->validate($this->transformedValue());

if ($this->state !== 'error') {
$this->state = 'submit';
Expand Down Expand Up @@ -322,12 +327,32 @@ private function handleKeyPress(string $key): bool
}

if ($this->validated) {
$this->validate($this->value());
$this->validate($this->transformedValue());
}

return true;
}

/**
* Transform the input.
*/
private function transform(mixed $value): mixed
{
if (is_null($this->transform)) {
return $value;
}

return call_user_func($this->transform, $value);
}

/**
* Get the transformed value of the prompt.
*/
protected function transformedValue(): mixed
{
return $this->transform($this->value());
}

/**
* Validate the input.
*/
Expand Down
1 change: 1 addition & 0 deletions src/SearchPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function __construct(
public mixed $validate = null,
public string $hint = '',
public bool|string $required = true,
public ?Closure $transform = null,
) {
if ($this->required === false) {
throw new InvalidArgumentException('Argument [required] must be true or a string.');
Expand Down
2 changes: 2 additions & 0 deletions src/SelectPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Laravel\Prompts;

use Closure;
use Illuminate\Support\Collection;
use InvalidArgumentException;

Expand Down Expand Up @@ -29,6 +30,7 @@ public function __construct(
public mixed $validate = null,
public string $hint = '',
public bool|string $required = true,
public ?Closure $transform = null,
) {
if ($this->required === false) {
throw new InvalidArgumentException('Argument [required] must be true or a string.');
Expand Down
1 change: 1 addition & 0 deletions src/SuggestPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function __construct(
public bool|string $required = false,
public mixed $validate = null,
public string $hint = '',
public ?Closure $transform = null,
) {
$this->options = $options instanceof Collection ? $options->all() : $options;

Expand Down
3 changes: 3 additions & 0 deletions src/TextPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Laravel\Prompts;

use Closure;

class TextPrompt extends Prompt
{
use Concerns\TypedValue;
Expand All @@ -16,6 +18,7 @@ public function __construct(
public bool|string $required = false,
public mixed $validate = null,
public string $hint = '',
public ?Closure $transform = null,
) {
$this->trackTypedValue($default);
}
Expand Down
3 changes: 3 additions & 0 deletions src/TextareaPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Laravel\Prompts;

use Closure;

class TextareaPrompt extends Prompt
{
use Concerns\Scrolling;
Expand All @@ -24,6 +26,7 @@ public function __construct(
public mixed $validate = null,
public string $hint = '',
int $rows = 5,
public ?Closure $transform = null,
) {
$this->scroll = $rows;

Expand Down
20 changes: 10 additions & 10 deletions src/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* Prompt the user for text input.
*/
function text(string $label, string $placeholder = '', string $default = '', bool|string $required = false, mixed $validate = null, string $hint = ''): string
function text(string $label, string $placeholder = '', string $default = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?Closure $transform = null): string
{
return (new TextPrompt(...func_get_args()))->prompt();
}
Expand All @@ -19,17 +19,17 @@ function text(string $label, string $placeholder = '', string $default = '', boo
/**
* Prompt the user for multiline text input.
*/
function textarea(string $label, string $placeholder = '', string $default = '', bool|string $required = false, ?Closure $validate = null, string $hint = '', int $rows = 5): string
function textarea(string $label, string $placeholder = '', string $default = '', bool|string $required = false, ?Closure $validate = null, string $hint = '', int $rows = 5, ?Closure $transform = null): string
{
return (new TextareaPrompt($label, $placeholder, $default, $required, $validate, $hint, $rows))->prompt();
return (new TextareaPrompt(...func_get_args()))->prompt();
}
}

if (! function_exists('\Laravel\Prompts\password')) {
/**
* Prompt the user for input, hiding the value.
*/
function password(string $label, string $placeholder = '', bool|string $required = false, mixed $validate = null, string $hint = ''): string
function password(string $label, string $placeholder = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?Closure $transform = null): string
{
return (new PasswordPrompt(...func_get_args()))->prompt();
}
Expand All @@ -42,7 +42,7 @@ function password(string $label, string $placeholder = '', bool|string $required
* @param array<int|string, string>|Collection<int|string, string> $options
* @param true|string $required
*/
function select(string $label, array|Collection $options, int|string|null $default = null, int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true): int|string
function select(string $label, array|Collection $options, int|string|null $default = null, int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?Closure $transform = null): int|string
{
return (new SelectPrompt(...func_get_args()))->prompt();
}
Expand All @@ -56,7 +56,7 @@ function select(string $label, array|Collection $options, int|string|null $defau
* @param array<int|string>|Collection<int, int|string> $default
* @return array<int|string>
*/
function multiselect(string $label, array|Collection $options, array|Collection $default = [], int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.'): array
function multiselect(string $label, array|Collection $options, array|Collection $default = [], int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?Closure $transform = null): array
{
return (new MultiSelectPrompt(...func_get_args()))->prompt();
}
Expand All @@ -66,7 +66,7 @@ function multiselect(string $label, array|Collection $options, array|Collection
/**
* Prompt the user to confirm an action.
*/
function confirm(string $label, bool $default = true, string $yes = 'Yes', string $no = 'No', bool|string $required = false, mixed $validate = null, string $hint = ''): bool
function confirm(string $label, bool $default = true, string $yes = 'Yes', string $no = 'No', bool|string $required = false, mixed $validate = null, string $hint = '', ?Closure $transform = null): bool
{
return (new ConfirmPrompt(...func_get_args()))->prompt();
}
Expand All @@ -88,7 +88,7 @@ function pause(string $message = 'Press enter to continue...'): bool
*
* @param array<string>|Collection<int, string>|Closure(string): array<string> $options
*/
function suggest(string $label, array|Collection|Closure $options, string $placeholder = '', string $default = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = ''): string
function suggest(string $label, array|Collection|Closure $options, string $placeholder = '', string $default = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = '', ?Closure $transform = null): string
{
return (new SuggestPrompt(...func_get_args()))->prompt();
}
Expand All @@ -101,7 +101,7 @@ function suggest(string $label, array|Collection|Closure $options, string $place
* @param Closure(string): array<int|string, string> $options
* @param true|string $required
*/
function search(string $label, Closure $options, string $placeholder = '', int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true): int|string
function search(string $label, Closure $options, string $placeholder = '', int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?Closure $transform = null): int|string
{
return (new SearchPrompt(...func_get_args()))->prompt();
}
Expand All @@ -114,7 +114,7 @@ function search(string $label, Closure $options, string $placeholder = '', int $
* @param Closure(string): array<int|string, string> $options
* @return array<int|string>
*/
function multisearch(string $label, Closure $options, string $placeholder = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.'): array
function multisearch(string $label, Closure $options, string $placeholder = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?Closure $transform = null): array
{
return (new MultiSearchPrompt(...func_get_args()))->prompt();
}
Expand Down
11 changes: 11 additions & 0 deletions tests/Feature/ConfirmPromptTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@
Prompt::assertOutputContains('No, gracias');
});

it('transforms values', function () {
Prompt::fake([Key::ENTER]);

$result = confirm(
label: 'Are you sure?',
transform: fn ($value) => ! $value,
);

expect($result)->toBeFalse();
});

it('validates', function () {
Prompt::fake([Key::ENTER, 'y', Key::ENTER]);

Expand Down
Loading

0 comments on commit 72e5035

Please sign in to comment.