Skip to content

Commit

Permalink
Merge pull request #81 from php-school/inputs-new
Browse files Browse the repository at this point in the history
Inputs
  • Loading branch information
mikeymike authored Apr 30, 2018
2 parents 6270594 + 32b500a commit 4c883fb
Show file tree
Hide file tree
Showing 39 changed files with 1,772 additions and 676 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"require": {
"php" : ">=7.1",
"beberlei/assert": "^2.4",
"php-school/terminal": "dev-catch-all-controls",
"ext-posix": "*"
},
"autoload" : {
Expand Down
32 changes: 32 additions & 0 deletions examples/input-advanced.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

use PhpSchool\CliMenu\CliMenu;
use PhpSchool\CliMenu\CliMenuBuilder;

require_once(__DIR__ . '/../vendor/autoload.php');

$itemCallable = function (CliMenu $menu) {
$username = $menu->askText()
->setPromptText('Enter username')
->setPlaceholderText('alice')
->ask();

$age = $menu->askNumber()
->setPromptText('Enter age')
->setPlaceholderText('28')
->ask();

$password = $menu->askPassword()
->setPromptText('Enter password')
->ask();

var_dump($username->fetch(), $age->fetch(), $password->fetch());
};

$menu = (new CliMenuBuilder)
->setTitle('User Manager')
->addItem('Create New User', $itemCallable)
->addLineBreak('-')
->build();

$menu->open();
24 changes: 24 additions & 0 deletions examples/input-number.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

use PhpSchool\CliMenu\CliMenu;
use PhpSchool\CliMenu\CliMenuBuilder;

require_once(__DIR__ . '/../vendor/autoload.php');

$itemCallable = function (CliMenu $menu) {
$result = $menu->askNumber()
->setPlaceholderText(10)
->ask();

var_dump($result->fetch());
};

$menu = (new CliMenuBuilder)
->setTitle('Basic CLI Menu')
->addItem('Enter number', $itemCallable)
->addItem('Second Item', $itemCallable)
->addItem('Third Item', $itemCallable)
->addLineBreak('-')
->build();

$menu->open();
35 changes: 35 additions & 0 deletions examples/input-password.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

use PhpSchool\CliMenu\CliMenu;
use PhpSchool\CliMenu\CliMenuBuilder;

require_once(__DIR__ . '/../vendor/autoload.php');

$itemCallable = function (CliMenu $menu) {
$result = $menu->askPassword()
->setPlaceholderText('')
->setValidator(function ($password) {
if ($password === 'password') {
$this->setValidationFailedText('Password is too weak');
return false;
} else if (strlen($password) <= 6) {
$this->setValidationFailedText('Password is not long enough');
return false;
}

return true;
})
->ask();

var_dump($result->fetch());
};

$menu = (new CliMenuBuilder)
->setTitle('Basic CLI Menu')
->addItem('Enter password', $itemCallable)
->addItem('Second Item', $itemCallable)
->addItem('Third Item', $itemCallable)
->addLineBreak('-')
->build();

$menu->open();
24 changes: 24 additions & 0 deletions examples/input-text.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

use PhpSchool\CliMenu\CliMenu;
use PhpSchool\CliMenu\CliMenuBuilder;

require_once(__DIR__ . '/../vendor/autoload.php');

$itemCallable = function (CliMenu $menu) {
$result = $menu->askText()
->setPlaceholderText('Enter something here')
->ask();

var_dump($result->fetch());
};

$menu = (new CliMenuBuilder)
->setTitle('Basic CLI Menu')
->addItem('Enter text', $itemCallable)
->addItem('Second Item', $itemCallable)
->addItem('Third Item', $itemCallable)
->addLineBreak('-')
->build();

$menu->open();
126 changes: 91 additions & 35 deletions src/CliMenu.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,34 @@

namespace PhpSchool\CliMenu;

use PhpSchool\CliMenu\Dialogue\NumberInput;
use PhpSchool\CliMenu\Exception\InvalidInstantiationException;
use PhpSchool\CliMenu\Exception\InvalidTerminalException;
use PhpSchool\CliMenu\Exception\MenuNotOpenException;
use PhpSchool\CliMenu\Input\InputIO;
use PhpSchool\CliMenu\Input\Number;
use PhpSchool\CliMenu\Input\Password;
use PhpSchool\CliMenu\Input\Text;
use PhpSchool\CliMenu\MenuItem\LineBreakItem;
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
use PhpSchool\CliMenu\MenuItem\StaticItem;
use PhpSchool\CliMenu\Dialogue\Confirm;
use PhpSchool\CliMenu\Dialogue\Flash;
use PhpSchool\CliMenu\Terminal\TerminalFactory;
use PhpSchool\CliMenu\Terminal\TerminalInterface;
use PhpSchool\CliMenu\Util\StringUtil as s;
use PhpSchool\Terminal\Exception\NotInteractiveTerminal;
use PhpSchool\Terminal\InputCharacter;
use PhpSchool\Terminal\NonCanonicalReader;
use PhpSchool\Terminal\Terminal;
use PhpSchool\Terminal\TerminalReader;

/**
* @author Michael Woodward <mikeymike.mw@gmail.com>
*/
class CliMenu
{
/**
* @var TerminalInterface
* @var Terminal
*/
protected $terminal;

Expand Down Expand Up @@ -62,7 +71,7 @@ class CliMenu
public function __construct(
?string $title,
array $items,
TerminalInterface $terminal = null,
Terminal $terminal = null,
MenuStyle $style = null
) {
$this->title = $title;
Expand All @@ -75,40 +84,33 @@ public function __construct(

/**
* Configure the terminal to work with CliMenu
*
* @throws InvalidTerminalException
*/
protected function configureTerminal() : void
{
$this->assertTerminalIsValidTTY();

$this->terminal->setCanonicalMode();
$this->terminal->disableCanonicalMode();
$this->terminal->disableEchoBack();
$this->terminal->disableCursor();
$this->terminal->clear();
}

/**
* Revert changes made to the terminal
*
* @throws InvalidTerminalException
*/
protected function tearDownTerminal() : void
{
$this->assertTerminalIsValidTTY();

$this->terminal->setCanonicalMode(false);
$this->terminal->enableCursor();
$this->terminal->restoreOriginalConfiguration();
}

private function assertTerminalIsValidTTY() : void
{
if (!$this->terminal->isTTY()) {
throw new InvalidTerminalException(
sprintf('Terminal "%s" is not a valid TTY', $this->terminal->getDetails())
);
if (!$this->terminal->isInteractive()) {
throw new InvalidTerminalException('Terminal is not interactive (TTY)');
}
}


public function setParent(CliMenu $parent) : void
{
$this->parent = $parent;
Expand All @@ -119,7 +121,7 @@ public function getParent() : ?CliMenu
return $this->parent;
}

public function getTerminal() : TerminalInterface
public function getTerminal() : Terminal
{
return $this->terminal;
}
Expand Down Expand Up @@ -161,14 +163,28 @@ private function display() : void
{
$this->draw();

while ($this->isOpen() && $input = $this->terminal->getKeyedInput()) {
switch ($input) {
case 'up':
case 'down':
$this->moveSelection($input);
$reader = new NonCanonicalReader($this->terminal);
$reader->addControlMappings([
'^P' => InputCharacter::UP,
'k' => InputCharacter::UP,
'^K' => InputCharacter::DOWN,
'j' => InputCharacter::DOWN,
"\r" => InputCharacter::ENTER,
' ' => InputCharacter::ENTER,
]);

while ($this->isOpen() && $char = $reader->readCharacter()) {
if (!$char->isHandledControl()) {
continue;
}

switch ($char->getControl()) {
case InputCharacter::UP:
case InputCharacter::DOWN:
$this->moveSelection($char->getControl());
$this->draw();
break;
case 'enter':
case InputCharacter::ENTER:
$this->executeCurrentItem();
break;
}
Expand All @@ -183,12 +199,12 @@ protected function moveSelection(string $direction) : void
do {
$itemKeys = array_keys($this->items);

$direction === 'up'
$direction === 'UP'
? $this->selectedItem--
: $this->selectedItem++;

if (!array_key_exists($this->selectedItem, $this->items)) {
$this->selectedItem = $direction === 'up'
$this->selectedItem = $direction === 'UP'
? end($itemKeys)
: reset($itemKeys);
} elseif ($this->getSelectedItem()->canSelect()) {
Expand Down Expand Up @@ -219,12 +235,16 @@ protected function executeCurrentItem() : void
* Redraw the menu
*/
public function redraw() : void
{
$this->assertOpen();
$this->draw();
}

private function assertOpen() : void
{
if (!$this->isOpen()) {
throw new MenuNotOpenException;
}

$this->draw();
}

/**
Expand Down Expand Up @@ -254,7 +274,7 @@ protected function draw() : void
$frame->newLine(2);

foreach ($frame->getRows() as $row) {
echo $row;
$this->terminal->write($row);
}

$this->currentFrame = $frame;
Expand All @@ -277,7 +297,7 @@ protected function drawMenuItem(MenuItemInterface $item, bool $selected = false)

return array_map(function ($row) use ($setColour, $unsetColour) {
return sprintf(
"%s%s%s%s%s%s%s\n\r",
"%s%s%s%s%s%s%s\n",
str_repeat(' ', $this->style->getMargin()),
$setColour,
str_repeat(' ', $this->style->getPadding()),
Expand Down Expand Up @@ -359,9 +379,7 @@ public function getCurrentFrame() : Frame

public function flash(string $text) : Flash
{
if (strpos($text, "\n") !== false) {
throw new \InvalidArgumentException;
}
$this->guardSingleLine($text);

$style = (new MenuStyle($this->terminal))
->setBg('yellow')
Expand All @@ -372,14 +390,52 @@ public function flash(string $text) : Flash

public function confirm($text) : Confirm
{
if (strpos($text, "\n") !== false) {
throw new \InvalidArgumentException;
}
$this->guardSingleLine($text);

$style = (new MenuStyle($this->terminal))
->setBg('yellow')
->setFg('red');

return new Confirm($this, $style, $this->terminal, $text);
}

public function askNumber() : Number
{
$this->assertOpen();

$style = (new MenuStyle($this->terminal))
->setBg('yellow')
->setFg('red');

return new Number(new InputIO($this, $this->terminal), $style);
}

public function askText() : Text
{
$this->assertOpen();

$style = (new MenuStyle($this->terminal))
->setBg('yellow')
->setFg('red');

return new Text(new InputIO($this, $this->terminal), $style);
}

public function askPassword() : Password
{
$this->assertOpen();

$style = (new MenuStyle($this->terminal))
->setBg('yellow')
->setFg('red');

return new Password(new InputIO($this, $this->terminal), $style);
}

private function guardSingleLine($text)
{
if (strpos($text, "\n") !== false) {
throw new \InvalidArgumentException;
}
}
}
Loading

0 comments on commit 4c883fb

Please sign in to comment.