Added added third-party library 'php-school/cli-menu'
This commit is contained in:
parent
91eb808664
commit
e9a36ab9a7
52 changed files with 8109 additions and 0 deletions
BIN
cache.db
Normal file
BIN
cache.db
Normal file
Binary file not shown.
17
src/ncc/ThirdParty/php-school/cli-menu/Action/ExitAction.php
vendored
Normal file
17
src/ncc/ThirdParty/php-school/cli-menu/Action/ExitAction.php
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Action;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class ExitAction
|
||||||
|
{
|
||||||
|
public function __invoke(CliMenu $menu) : void
|
||||||
|
{
|
||||||
|
$menu->close();
|
||||||
|
}
|
||||||
|
}
|
20
src/ncc/ThirdParty/php-school/cli-menu/Action/GoBackAction.php
vendored
Normal file
20
src/ncc/ThirdParty/php-school/cli-menu/Action/GoBackAction.php
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Action;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class GoBackAction
|
||||||
|
{
|
||||||
|
public function __invoke(CliMenu $menu) : void
|
||||||
|
{
|
||||||
|
if ($parent = $menu->getParent()) {
|
||||||
|
$menu->closeThis();
|
||||||
|
$parent->open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
659
src/ncc/ThirdParty/php-school/cli-menu/Builder/CliMenuBuilder.php
vendored
Normal file
659
src/ncc/ThirdParty/php-school/cli-menu/Builder/CliMenuBuilder.php
vendored
Normal file
|
@ -0,0 +1,659 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Builder;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\Action\ExitAction;
|
||||||
|
use PhpSchool\CliMenu\Action\GoBackAction;
|
||||||
|
use PhpSchool\CliMenu\Exception\InvalidShortcutException;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\AsciiArtItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\CheckboxItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\LineBreakItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuMenuItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\RadioItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\SelectableItem;
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\SplitItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\StaticItem;
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\CheckboxStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\DefaultStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\RadioStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\SelectableStyle;
|
||||||
|
use PhpSchool\CliMenu\Terminal\TerminalFactory;
|
||||||
|
use PhpSchool\Terminal\Terminal;
|
||||||
|
use function PhpSchool\CliMenu\Util\each;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.com>
|
||||||
|
*/
|
||||||
|
class CliMenuBuilder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CliMenu
|
||||||
|
*/
|
||||||
|
private $menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $goBackButtonText = 'Go Back';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $exitButtonText = 'Exit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Terminal
|
||||||
|
*/
|
||||||
|
private $terminal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $disableDefaultItems = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $disabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to auto create keyboard shortcuts for items
|
||||||
|
* when they contain square brackets. Eg: [M]y item
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $autoShortcuts = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regex to auto match for shortcuts defaults to looking
|
||||||
|
* for a single character encased in square brackets
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $autoShortcutsRegex = '/\[(.)\]/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $extraItemStyles = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $subMenu = false;
|
||||||
|
|
||||||
|
public function __construct(Terminal $terminal = null)
|
||||||
|
{
|
||||||
|
$this->terminal = $terminal ?? TerminalFactory::fromSystem();
|
||||||
|
$this->style = new MenuStyle($this->terminal);
|
||||||
|
$this->menu = new CliMenu(null, [], $this->terminal, $this->style);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function newSubMenu(Terminal $terminal) : self
|
||||||
|
{
|
||||||
|
$instance = new self($terminal);
|
||||||
|
$instance->subMenu = true;
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTitle(string $title) : self
|
||||||
|
{
|
||||||
|
$this->menu->setTitle($title);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addMenuItem(MenuItemInterface $item) : self
|
||||||
|
{
|
||||||
|
$this->menu->addItem($item);
|
||||||
|
|
||||||
|
$this->processItemShortcut($item);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addItem(
|
||||||
|
string $text,
|
||||||
|
callable $itemCallable,
|
||||||
|
bool $showItemExtra = false,
|
||||||
|
bool $disabled = false
|
||||||
|
) : self {
|
||||||
|
$this->addMenuItem(new SelectableItem($text, $itemCallable, $showItemExtra, $disabled));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addItems(array $items) : self
|
||||||
|
{
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$this->addItem(...$item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addCheckboxItem(
|
||||||
|
string $text,
|
||||||
|
callable $itemCallable,
|
||||||
|
bool $showItemExtra = false,
|
||||||
|
bool $disabled = false
|
||||||
|
) : self {
|
||||||
|
$this->addMenuItem(new CheckboxItem($text, $itemCallable, $showItemExtra, $disabled));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addCheckboxItems(array $items): self
|
||||||
|
{
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$this->addCheckboxItem(...$item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRadioItem(
|
||||||
|
string $text,
|
||||||
|
callable $itemCallable,
|
||||||
|
bool $showItemExtra = false,
|
||||||
|
bool $disabled = false
|
||||||
|
) : self {
|
||||||
|
$this->addMenuItem(new RadioItem($text, $itemCallable, $showItemExtra, $disabled));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRadioItems(array $items): self
|
||||||
|
{
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$this->addRadioItem(...$item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addStaticItem(string $text) : self
|
||||||
|
{
|
||||||
|
$this->addMenuItem(new StaticItem($text));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addLineBreak(string $breakChar = ' ', int $lines = 1) : self
|
||||||
|
{
|
||||||
|
$this->addMenuItem(new LineBreakItem($breakChar, $lines));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addAsciiArt(string $art, string $position = AsciiArtItem::POSITION_CENTER, string $alt = '') : self
|
||||||
|
{
|
||||||
|
$this->addMenuItem(new AsciiArtItem($art, $position, $alt));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSubMenu(string $text, \Closure $callback) : self
|
||||||
|
{
|
||||||
|
$builder = self::newSubMenu($this->terminal);
|
||||||
|
|
||||||
|
if ($this->autoShortcuts) {
|
||||||
|
$builder->enableAutoShortcuts($this->autoShortcutsRegex);
|
||||||
|
}
|
||||||
|
|
||||||
|
each($this->extraItemStyles, function (int $i, array $extraItemStyle) use ($builder) {
|
||||||
|
$builder->registerItemStyle($extraItemStyle['class'], $extraItemStyle['style']);
|
||||||
|
});
|
||||||
|
|
||||||
|
$callback($builder);
|
||||||
|
|
||||||
|
$menu = $builder->build();
|
||||||
|
$menu->setParent($this->menu);
|
||||||
|
|
||||||
|
$this->menu->addItem($item = new MenuMenuItem(
|
||||||
|
$text,
|
||||||
|
$menu,
|
||||||
|
$builder->isMenuDisabled()
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->processItemShortcut($item);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSubMenuFromBuilder(string $text, CliMenuBuilder $builder) : self
|
||||||
|
{
|
||||||
|
$menu = $builder->build();
|
||||||
|
$menu->setParent($this->menu);
|
||||||
|
|
||||||
|
$this->menu->addItem($item = new MenuMenuItem(
|
||||||
|
$text,
|
||||||
|
$menu,
|
||||||
|
$builder->isMenuDisabled()
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->processItemShortcut($item);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enableAutoShortcuts(string $regex = null) : self
|
||||||
|
{
|
||||||
|
$this->autoShortcuts = true;
|
||||||
|
|
||||||
|
if (null !== $regex) {
|
||||||
|
$this->autoShortcutsRegex = $regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function extractShortcut(string $title) : ?string
|
||||||
|
{
|
||||||
|
preg_match($this->autoShortcutsRegex, $title, $match);
|
||||||
|
|
||||||
|
if (!isset($match[1])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mb_strlen($match[1]) > 1) {
|
||||||
|
throw InvalidShortcutException::fromShortcut($match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($match[1]) ? strtolower($match[1]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processItemShortcut(MenuItemInterface $item) : void
|
||||||
|
{
|
||||||
|
$this->processIndividualShortcut($item, function (CliMenu $menu) use ($item) {
|
||||||
|
$menu->executeAsSelected($item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processSplitItemShortcuts(SplitItem $splitItem) : void
|
||||||
|
{
|
||||||
|
foreach ($splitItem->getItems() as $item) {
|
||||||
|
$this->processIndividualShortcut($item, function (CliMenu $menu) use ($splitItem, $item) {
|
||||||
|
$current = $splitItem->getSelectedItemIndex();
|
||||||
|
|
||||||
|
$splitItem->setSelectedItemIndex(
|
||||||
|
(int) array_search($item, $splitItem->getItems(), true)
|
||||||
|
);
|
||||||
|
|
||||||
|
$menu->executeAsSelected($splitItem);
|
||||||
|
|
||||||
|
if ($current !== null) {
|
||||||
|
$splitItem->setSelectedItemIndex($current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processIndividualShortcut(MenuItemInterface $item, callable $callback) : void
|
||||||
|
{
|
||||||
|
if (!$this->autoShortcuts) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($shortcut = $this->extractShortcut($item->getText())) {
|
||||||
|
$this->menu->addCustomControlMapping(
|
||||||
|
$shortcut,
|
||||||
|
$callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSplitItem(\Closure $callback) : self
|
||||||
|
{
|
||||||
|
$builder = new SplitItemBuilder($this->menu);
|
||||||
|
|
||||||
|
if ($this->autoShortcuts) {
|
||||||
|
$builder->enableAutoShortcuts($this->autoShortcutsRegex);
|
||||||
|
}
|
||||||
|
|
||||||
|
each($this->extraItemStyles, function (int $i, array $extraItemStyle) use ($builder) {
|
||||||
|
$builder->registerItemStyle($extraItemStyle['class'], $extraItemStyle['style']);
|
||||||
|
});
|
||||||
|
|
||||||
|
$callback($builder);
|
||||||
|
|
||||||
|
$this->menu->addItem($splitItem = $builder->build());
|
||||||
|
|
||||||
|
$this->processSplitItemShortcuts($splitItem);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable a submenu
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function disableMenu() : self
|
||||||
|
{
|
||||||
|
if (!$this->subMenu) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'You can\'t disable the root menu'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->disabled = true;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isMenuDisabled() : bool
|
||||||
|
{
|
||||||
|
return $this->disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGoBackButtonText(string $goBackButtonTest) : self
|
||||||
|
{
|
||||||
|
$this->goBackButtonText = $goBackButtonTest;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExitButtonText(string $exitButtonText) : self
|
||||||
|
{
|
||||||
|
$this->exitButtonText = $exitButtonText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBackgroundColour(string $colour, string $fallback = null) : self
|
||||||
|
{
|
||||||
|
$this->style->setBg($colour, $fallback);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setForegroundColour(string $colour, string $fallback = null) : self
|
||||||
|
{
|
||||||
|
$this->style->setFg($colour, $fallback);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setWidth(int $width) : self
|
||||||
|
{
|
||||||
|
$this->style->setWidth($width);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPadding(int $topBottom, int $leftRight = null) : self
|
||||||
|
{
|
||||||
|
$this->style->setPadding($topBottom, $leftRight);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPaddingTopBottom(int $topBottom) : self
|
||||||
|
{
|
||||||
|
$this->style->setPaddingTopBottom($topBottom);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPaddingLeftRight(int $leftRight) : self
|
||||||
|
{
|
||||||
|
$this->style->setPaddingLeftRight($leftRight);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMarginAuto() : self
|
||||||
|
{
|
||||||
|
$this->style->setMarginAuto();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMargin(int $margin) : self
|
||||||
|
{
|
||||||
|
$this->style->setMargin($margin);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setItemExtra(string $extra) : self
|
||||||
|
{
|
||||||
|
$this->style->setItemExtra($extra);
|
||||||
|
$this->getSelectableStyle()->setItemExtra($extra);
|
||||||
|
|
||||||
|
// if we customise item extra, it means we most likely want to display it
|
||||||
|
$this->displayExtra();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTitleSeparator(string $separator) : self
|
||||||
|
{
|
||||||
|
$this->style->setTitleSeparator($separator);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|string|null $right
|
||||||
|
* @param int|string|null $bottom
|
||||||
|
* @param int|string|null $left
|
||||||
|
*/
|
||||||
|
public function setBorder(int $top, $right = null, $bottom = null, $left = null, string $colour = null) : self
|
||||||
|
{
|
||||||
|
$this->style->setBorder($top, $right, $bottom, $left, $colour);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderTopWidth(int $width) : self
|
||||||
|
{
|
||||||
|
$this->style->setBorderTopWidth($width);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderRightWidth(int $width) : self
|
||||||
|
{
|
||||||
|
$this->style->setBorderRightWidth($width);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderBottomWidth(int $width) : self
|
||||||
|
{
|
||||||
|
$this->style->setBorderBottomWidth($width);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderLeftWidth(int $width) : self
|
||||||
|
{
|
||||||
|
$this->style->setBorderLeftWidth($width);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderColour(string $colour, string $fallback = null) : self
|
||||||
|
{
|
||||||
|
$this->style->setBorderColour($colour, $fallback);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyle() : MenuStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTerminal() : Terminal
|
||||||
|
{
|
||||||
|
return $this->terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDefaultItems() : array
|
||||||
|
{
|
||||||
|
$actions = [];
|
||||||
|
if ($this->subMenu) {
|
||||||
|
$actions[] = new SelectableItem($this->goBackButtonText, new GoBackAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
$actions[] = new SelectableItem($this->exitButtonText, new ExitAction);
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function disableDefaultItems() : self
|
||||||
|
{
|
||||||
|
$this->disableDefaultItems = true;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function displayExtra() : self
|
||||||
|
{
|
||||||
|
$this->style->setDisplaysExtra(true);
|
||||||
|
$this->getSelectableStyle()->setDisplaysExtra(true);
|
||||||
|
$this->getCheckboxStyle()->setDisplaysExtra(true);
|
||||||
|
$this->getRadioStyle()->setDisplaysExtra(true);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function itemsHaveExtra(array $items) : bool
|
||||||
|
{
|
||||||
|
return !empty(array_filter($items, function (MenuItemInterface $item) {
|
||||||
|
return $item->showsItemExtra();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function build() : CliMenu
|
||||||
|
{
|
||||||
|
if (!$this->disableDefaultItems) {
|
||||||
|
$this->menu->addItems($this->getDefaultItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->style->getDisplaysExtra()) {
|
||||||
|
$this->style->setDisplaysExtra($this->itemsHaveExtra($this->menu->getItems()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->subMenu) {
|
||||||
|
$this->menu->propagateStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDefaultStyle() : DefaultStyle
|
||||||
|
{
|
||||||
|
$style = $this->menu->getItemStyle(DefaultStyle::class);
|
||||||
|
assert($style instanceof DefaultStyle);
|
||||||
|
return $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDefaultStyle(DefaultStyle $style) : self
|
||||||
|
{
|
||||||
|
$this->menu->setItemStyle($style, DefaultStyle::class);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modifyDefaultStyle(callable $itemCallable) : self
|
||||||
|
{
|
||||||
|
$itemCallable($this->getDefaultStyle());
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSelectableStyle() : SelectableStyle
|
||||||
|
{
|
||||||
|
$style = $this->menu->getItemStyle(SelectableStyle::class);
|
||||||
|
assert($style instanceof SelectableStyle);
|
||||||
|
return $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSelectableStyle(SelectableStyle $style) : self
|
||||||
|
{
|
||||||
|
$this->menu->setItemStyle($style, SelectableStyle::class);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modifySelectableStyle(callable $itemCallable) : self
|
||||||
|
{
|
||||||
|
$itemCallable($this->getSelectableStyle());
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCheckboxStyle() : CheckboxStyle
|
||||||
|
{
|
||||||
|
$style = $this->menu->getItemStyle(CheckboxStyle::class);
|
||||||
|
assert($style instanceof CheckboxStyle);
|
||||||
|
return $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCheckboxStyle(CheckboxStyle $style) : self
|
||||||
|
{
|
||||||
|
$this->menu->setItemStyle($style, CheckboxStyle::class);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modifyCheckboxStyle(callable $itemCallable) : self
|
||||||
|
{
|
||||||
|
$itemCallable($this->getCheckboxStyle());
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRadioStyle() : RadioStyle
|
||||||
|
{
|
||||||
|
$style = $this->menu->getItemStyle(RadioStyle::class);
|
||||||
|
assert($style instanceof RadioStyle);
|
||||||
|
return $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRadioStyle(RadioStyle $style) : self
|
||||||
|
{
|
||||||
|
$this->menu->setItemStyle($style, RadioItem::class);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modifyRadioStyle(callable $itemCallable) : self
|
||||||
|
{
|
||||||
|
$itemCallable($this->getRadioStyle());
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modifyStyle(string $styleClass, callable $itemCallable) : self
|
||||||
|
{
|
||||||
|
$itemCallable($this->menu->getItemStyle($styleClass));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerItemStyle(string $itemClass, ItemStyle $itemStyle) : self
|
||||||
|
{
|
||||||
|
$this->menu->getStyleLocator()
|
||||||
|
->registerItemStyle($itemClass, $itemStyle);
|
||||||
|
|
||||||
|
$this->extraItemStyles[] = ['class' => $itemClass, 'style' => $itemStyle];
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
169
src/ncc/ThirdParty/php-school/cli-menu/Builder/SplitItemBuilder.php
vendored
Normal file
169
src/ncc/ThirdParty/php-school/cli-menu/Builder/SplitItemBuilder.php
vendored
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Builder;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\CheckboxItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\LineBreakItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuMenuItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\RadioItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\SelectableItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\SplitItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\StaticItem;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
use function \PhpSchool\CliMenu\Util\each;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class SplitItemBuilder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CliMenu
|
||||||
|
*/
|
||||||
|
private $menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SplitItem
|
||||||
|
*/
|
||||||
|
private $splitItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to auto create keyboard shortcuts for items
|
||||||
|
* when they contain square brackets. Eg: [M]y item
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $autoShortcuts = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regex to auto match for shortcuts defaults to looking
|
||||||
|
* for a single character encased in square brackets
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $autoShortcutsRegex = '/\[(.)\]/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $extraItemStyles = [];
|
||||||
|
|
||||||
|
public function __construct(CliMenu $menu)
|
||||||
|
{
|
||||||
|
$this->menu = $menu;
|
||||||
|
$this->splitItem = new SplitItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addItem(
|
||||||
|
string $text,
|
||||||
|
callable $itemCallable,
|
||||||
|
bool $showItemExtra = false,
|
||||||
|
bool $disabled = false
|
||||||
|
) : self {
|
||||||
|
$this->splitItem->addItem(new SelectableItem($text, $itemCallable, $showItemExtra, $disabled));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addCheckboxItem(
|
||||||
|
string $text,
|
||||||
|
callable $itemCallable,
|
||||||
|
bool $showItemExtra = false,
|
||||||
|
bool $disabled = false
|
||||||
|
) : self {
|
||||||
|
$this->splitItem->addItem(new CheckboxItem($text, $itemCallable, $showItemExtra, $disabled));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRadioItem(
|
||||||
|
string $text,
|
||||||
|
callable $itemCallable,
|
||||||
|
bool $showItemExtra = false,
|
||||||
|
bool $disabled = false
|
||||||
|
) : self {
|
||||||
|
$this->splitItem->addItem(new RadioItem($text, $itemCallable, $showItemExtra, $disabled));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addStaticItem(string $text) : self
|
||||||
|
{
|
||||||
|
$this->splitItem->addItem(new StaticItem($text));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addLineBreak(string $breakChar = ' ', int $lines = 1) : self
|
||||||
|
{
|
||||||
|
$this->splitItem->addItem(new LineBreakItem($breakChar, $lines));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSubMenu(string $text, \Closure $callback) : self
|
||||||
|
{
|
||||||
|
$builder = CliMenuBuilder::newSubMenu($this->menu->getTerminal());
|
||||||
|
|
||||||
|
if ($this->autoShortcuts) {
|
||||||
|
$builder->enableAutoShortcuts($this->autoShortcutsRegex);
|
||||||
|
}
|
||||||
|
|
||||||
|
each($this->extraItemStyles, function (int $i, array $extraItemStyle) use ($builder) {
|
||||||
|
$builder->registerItemStyle($extraItemStyle['class'], $extraItemStyle['style']);
|
||||||
|
});
|
||||||
|
|
||||||
|
$callback($builder);
|
||||||
|
|
||||||
|
$menu = $builder->build();
|
||||||
|
$menu->setParent($this->menu);
|
||||||
|
|
||||||
|
$this->splitItem->addItem(new MenuMenuItem(
|
||||||
|
$text,
|
||||||
|
$menu,
|
||||||
|
$builder->isMenuDisabled()
|
||||||
|
));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addMenuItem(MenuItemInterface $item) : self
|
||||||
|
{
|
||||||
|
$this->splitItem->addItem($item);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGutter(int $gutter) : self
|
||||||
|
{
|
||||||
|
$this->splitItem->setGutter($gutter);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enableAutoShortcuts(string $regex = null) : self
|
||||||
|
{
|
||||||
|
$this->autoShortcuts = true;
|
||||||
|
|
||||||
|
if (null !== $regex) {
|
||||||
|
$this->autoShortcutsRegex = $regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerItemStyle(string $itemClass, ItemStyle $itemStyle) : self
|
||||||
|
{
|
||||||
|
$this->extraItemStyles[] = ['class' => $itemClass, 'style' => $itemStyle];
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function build() : SplitItem
|
||||||
|
{
|
||||||
|
return $this->splitItem;
|
||||||
|
}
|
||||||
|
}
|
161
src/ncc/ThirdParty/php-school/cli-menu/CHANGELOG.md
vendored
Normal file
161
src/ncc/ThirdParty/php-school/cli-menu/CHANGELOG.md
vendored
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
# Change Log
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
|
||||||
|
|
||||||
|
## [Unreleased][unreleased]
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
## [4.3.0]
|
||||||
|
### Fixed
|
||||||
|
- PHP 8.1 Support (#252, #249)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- declare(strict_types=1) everywhere
|
||||||
|
|
||||||
|
## [4.2.0]
|
||||||
|
### Added
|
||||||
|
- Yes/no confirmation dialogue (#248)
|
||||||
|
- Ability to add multiple checkbox and radio items (#241)
|
||||||
|
|
||||||
|
## [4.1.0]
|
||||||
|
### Added
|
||||||
|
- Ability to modify password length for password input (#235)
|
||||||
|
- Improve the formatting of disabled menu items in different terminals (#236)
|
||||||
|
- Support for PHP8 (#240)
|
||||||
|
|
||||||
|
## [4.0.0]
|
||||||
|
### Added
|
||||||
|
- Add PHP 7.4 support (#183)
|
||||||
|
- CheckboxItem & RadioItem (#186, #189, #193, #194, #226)
|
||||||
|
- Ability to force display extra (#187)
|
||||||
|
- Individual style objects for each item type (#211, #212, #213, #214, #216, #230)
|
||||||
|
- Method getStyle() to interface PhpSchool\CliMenu\MenuItem\MenuItemInterface
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed item extra rendering outside of menu (#66, £184, #187)
|
||||||
|
- Fix unresponsive menu upon closing and reopening (#198)
|
||||||
|
- Menu styles incorrectly propagating to submenus (#201, #210)
|
||||||
|
- Various issues with the menu width, when the terminal was too small (#223, #220, #219)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Remove rebinding $this in builder closures so we can access the real $this (#191, #192, #196)
|
||||||
|
- Marker methods from PhpSchool\CliMenu\MenuStyle:
|
||||||
|
#getSelectedMarker()
|
||||||
|
#setSelectedMarker()
|
||||||
|
#getUnselectedMarker()
|
||||||
|
#setUnselectedMarker()
|
||||||
|
#getMarker()
|
||||||
|
- PhpSchool\CliMenu\MenuItem\SelectableTrait
|
||||||
|
- Marker methods from PhpSchool\CliMenu\Builder\CliMenuBuilder:
|
||||||
|
#setUnselectedMarker()
|
||||||
|
#setSelectedMarker()
|
||||||
|
|
||||||
|
## [3.2.0]
|
||||||
|
### Added
|
||||||
|
- Allow ESC key to "cancel" editing an input (#174)
|
||||||
|
- Methods for disabling the default VIM mappings and setting your own (#172)
|
||||||
|
- Ability to set custom validator on Text and Number inputs (#177)
|
||||||
|
- Ability to turn on automatic item shortcuts (#176)
|
||||||
|
|
||||||
|
## [3.1.0]
|
||||||
|
### Changed
|
||||||
|
- Update dependencies + fix some static analysis issues
|
||||||
|
|
||||||
|
## [3.0.0]
|
||||||
|
### Changed
|
||||||
|
- Optimise redrawing to reduce flickering (#83)
|
||||||
|
- Use parent menu terminal when creating sub menus to reduce object graph (#94)
|
||||||
|
- Do not print right margin. Causes menu to wrap even when row fits in terminal (#116)
|
||||||
|
- CliMenu throws a \RuntimeException if it is opened with no items added (#146, #130)
|
||||||
|
- Sub Menus are configured via closures (#155)
|
||||||
|
- Remove restriction of 1 character length for markers (#141)
|
||||||
|
- Remove the mandatory space after markers for now they can be of any length (#154)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added type hints everywhere (#79)
|
||||||
|
- Added phpstan to the travis build (#79)
|
||||||
|
- Input dialogue system for prompting users. Comes with text, number and password inputs (#81)
|
||||||
|
- Added ability to pass already prepared CliMenuBuilder instance to CliMenuBuilder#addSubMenuFromBuilder (#85, 155)
|
||||||
|
- Added CliMenu#addItems & CliMenu#setItems to add multiple items and replace them (#86)
|
||||||
|
- Added custom control mapping - link any key to a callable to immediately execute it (#87)
|
||||||
|
- Added MenuMenuItem#getSubMenu (#92)
|
||||||
|
- Added alternate text to AsciiArtItem to display if the ascii art is too large for the current terminal (#93)
|
||||||
|
- Added the ability to pass existing MenuStyle instance to dialogues and inputs for consistent themes and reduced object graph (#99)
|
||||||
|
- Added CSS like borders (#100)
|
||||||
|
- Added option to auto center menu with CliMenuBuilder#setMarginAuto (#103)
|
||||||
|
- Added option to auto center menu with CliMenuBuilder#setMarginAuto (#103)
|
||||||
|
- Added support for 256 colours with automatic and manual fallback to 8 colours (#104)
|
||||||
|
- Added clear option to CliMenu#redraw useful for when reducing the terminal width (#117)
|
||||||
|
- Added ability to set top/bottom and left/right padding independently (#121)
|
||||||
|
- Added a new Split Item item type which allows displaying multiple items on one line (#127)
|
||||||
|
- Added setText methods to various items so they can be modified at runtime (#153)
|
||||||
|
- Added MenuStyle#hasChangedFromDefaults to check if a MenuStyle has been modified (#149)
|
||||||
|
- Added CliMenu#setTitle and CliMenu#setStyle (#155)
|
||||||
|
- Added CliMenuBuilder#getStyle to get the current style object for the menu
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed sub menu go back button freezing menu (#88)
|
||||||
|
- Fixed centering ascii art items with trailing white space (#102)
|
||||||
|
- Enable cursor when exiting menu (#110)
|
||||||
|
- Fixed (#71) - changed padding calculation when row too long to stop php notices (#112)
|
||||||
|
- Fixed wordwrap helper (#134)
|
||||||
|
- Fixed selected item issues when adding/setting/removing items (#156)
|
||||||
|
- Fix infinite loop when no selectable items in menu (#159, #144)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Dropped PHP 5.x and PHP 7.0 support (#79)
|
||||||
|
- Removed the Terminal namespace which has been migrated to php-school/terminal (#81)
|
||||||
|
- Removed MenuStyle::getDefaultStyleValues (#149)
|
||||||
|
- Removed CliMenuBuilder#setTerminal (#149)
|
||||||
|
- Removed CliMenuBuilder#getSubMenu (#155)
|
||||||
|
- Removed CliMenuBuilder#getMenuStyle (#155)
|
||||||
|
- Removed CliMenuBuilder#end (#155)
|
||||||
|
|
||||||
|
## [2.1.0]
|
||||||
|
### Changed
|
||||||
|
- Use new static for submenu to allow subclassing (#68)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Add emacs style up/down shortcuts ctrl+n and ctrl+p (#67)
|
||||||
|
|
||||||
|
## [2.0.2]
|
||||||
|
### Fixed
|
||||||
|
- Don't output ascii art if the terminal width is too small (#63)
|
||||||
|
|
||||||
|
## [2.0.1]
|
||||||
|
### Fixed
|
||||||
|
- Reset array keys after removing an item from the menu (#61)
|
||||||
|
|
||||||
|
## [2.0.0]
|
||||||
|
### Fixed
|
||||||
|
- PHPUnit deprecations - updated to createMock()
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Require ext-posix (#50)
|
||||||
|
- Make MenuStyle easier to construct by only allowing changes to be made via setters (#45)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added getStyle() to CliMenu to get access to the style object from the menu itself (#42)
|
||||||
|
- Added redraw method to CliMenu which can redraw the menu immediately with any style changes. See
|
||||||
|
examples/crazy-redraw.php for an example (#43)
|
||||||
|
- Added tests for child menu style inheritance (#44)
|
||||||
|
- Add getter getItems() to get all items from the menu (#46)
|
||||||
|
- Add method removeItem(ItemInterface $item) to remove an item from the menu (#46)
|
||||||
|
- Ability to toggle item extra while the menu is open - see examples/toggle-item-extra.php (#46)
|
||||||
|
- Added dialogues flash and confirm - they both display some text on top of the menu, flash is dismissed with
|
||||||
|
any key press where the confirm requires enter to be pressed on the provided button.
|
||||||
|
See examples/confirm.php and examples/flash.php (#49)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Removed windows terminal - many required terminal features are unavailable (#50)
|
||||||
|
- Individual component instantiation restrictions (#41)
|
||||||
|
|
||||||
|
## [1.2.0]
|
||||||
|
### Added
|
||||||
|
- Added ability to disable menu items and sub-menus, they will appear dimmed and will be un-selectable (#40)
|
786
src/ncc/ThirdParty/php-school/cli-menu/CliMenu.php
vendored
Normal file
786
src/ncc/ThirdParty/php-school/cli-menu/CliMenu.php
vendored
Normal file
|
@ -0,0 +1,786 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\Dialogue\CancellableConfirm;
|
||||||
|
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\PropagatesStyles;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\SplitItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\StaticItem;
|
||||||
|
use PhpSchool\CliMenu\Dialogue\Confirm;
|
||||||
|
use PhpSchool\CliMenu\Dialogue\Flash;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\Locator;
|
||||||
|
use PhpSchool\CliMenu\Terminal\TerminalFactory;
|
||||||
|
use PhpSchool\CliMenu\Util\StringUtil as s;
|
||||||
|
use PhpSchool\Terminal\InputCharacter;
|
||||||
|
use PhpSchool\Terminal\NonCanonicalReader;
|
||||||
|
use PhpSchool\Terminal\Terminal;
|
||||||
|
use function PhpSchool\CliMenu\Util\collect;
|
||||||
|
use function PhpSchool\CliMenu\Util\each;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class CliMenu
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Terminal
|
||||||
|
*/
|
||||||
|
protected $terminal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuStyle
|
||||||
|
*/
|
||||||
|
protected $style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Locator
|
||||||
|
*/
|
||||||
|
private $itemStyleLocator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ?string
|
||||||
|
*/
|
||||||
|
protected $title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuItemInterface[]
|
||||||
|
*/
|
||||||
|
protected $items = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int|null
|
||||||
|
*/
|
||||||
|
protected $selectedItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $open = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CliMenu|null
|
||||||
|
*/
|
||||||
|
protected $parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $defaultControlMappings = [
|
||||||
|
'^P' => InputCharacter::UP,
|
||||||
|
'k' => InputCharacter::UP,
|
||||||
|
'^K' => InputCharacter::DOWN,
|
||||||
|
'j' => InputCharacter::DOWN,
|
||||||
|
"\r" => InputCharacter::ENTER,
|
||||||
|
' ' => InputCharacter::ENTER,
|
||||||
|
'l' => InputCharacter::LEFT,
|
||||||
|
'm' => InputCharacter::RIGHT,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $customControlMappings = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Frame
|
||||||
|
*/
|
||||||
|
private $currentFrame;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
?string $title,
|
||||||
|
array $items,
|
||||||
|
Terminal $terminal = null,
|
||||||
|
MenuStyle $style = null
|
||||||
|
) {
|
||||||
|
$this->title = $title;
|
||||||
|
$this->items = $items;
|
||||||
|
$this->terminal = $terminal ?: TerminalFactory::fromSystem();
|
||||||
|
$this->style = $style ?: new MenuStyle($this->terminal);
|
||||||
|
|
||||||
|
$this->itemStyleLocator = new Locator();
|
||||||
|
|
||||||
|
$this->selectFirstItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the terminal to work with CliMenu
|
||||||
|
*/
|
||||||
|
protected function configureTerminal() : void
|
||||||
|
{
|
||||||
|
$this->assertTerminalIsValidTTY();
|
||||||
|
|
||||||
|
$this->terminal->disableCanonicalMode();
|
||||||
|
$this->terminal->disableEchoBack();
|
||||||
|
$this->terminal->disableCursor();
|
||||||
|
$this->terminal->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revert changes made to the terminal
|
||||||
|
*/
|
||||||
|
protected function tearDownTerminal() : void
|
||||||
|
{
|
||||||
|
$this->terminal->restoreOriginalConfiguration();
|
||||||
|
$this->terminal->enableCanonicalMode();
|
||||||
|
$this->terminal->enableEchoBack();
|
||||||
|
$this->terminal->enableCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertTerminalIsValidTTY() : void
|
||||||
|
{
|
||||||
|
if (!$this->terminal->isInteractive()) {
|
||||||
|
throw new InvalidTerminalException('Terminal is not interactive (TTY)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTitle(string $title) : void
|
||||||
|
{
|
||||||
|
$this->title = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() : ?string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setParent(CliMenu $parent) : void
|
||||||
|
{
|
||||||
|
$this->parent = $parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent() : ?CliMenu
|
||||||
|
{
|
||||||
|
return $this->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTerminal() : Terminal
|
||||||
|
{
|
||||||
|
return $this->terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isOpen() : bool
|
||||||
|
{
|
||||||
|
return $this->open;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new Item to the menu
|
||||||
|
*/
|
||||||
|
public function addItem(MenuItemInterface $item) : void
|
||||||
|
{
|
||||||
|
$this->items[] = $item;
|
||||||
|
|
||||||
|
$this->selectFirstItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add multiple Items to the menu
|
||||||
|
*/
|
||||||
|
public function addItems(array $items) : void
|
||||||
|
{
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$this->items[] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->selectFirstItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Items of the menu
|
||||||
|
*/
|
||||||
|
public function setItems(array $items) : void
|
||||||
|
{
|
||||||
|
$this->selectedItem = null;
|
||||||
|
$this->items = $items;
|
||||||
|
|
||||||
|
$this->selectFirstItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected pointer to the first selectable item
|
||||||
|
*/
|
||||||
|
private function selectFirstItem() : void
|
||||||
|
{
|
||||||
|
if (null === $this->selectedItem) {
|
||||||
|
foreach ($this->items as $key => $item) {
|
||||||
|
if ($item->canSelect()) {
|
||||||
|
$this->selectedItem = $key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the built-in VIM control mappings
|
||||||
|
*/
|
||||||
|
public function disableDefaultControlMappings() : void
|
||||||
|
{
|
||||||
|
$this->defaultControlMappings = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default control mappings
|
||||||
|
*/
|
||||||
|
public function setDefaultControlMappings(array $defaultControlMappings) : void
|
||||||
|
{
|
||||||
|
$this->defaultControlMappings = $defaultControlMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a custom control mapping
|
||||||
|
*/
|
||||||
|
public function addCustomControlMapping(string $input, callable $callable) : void
|
||||||
|
{
|
||||||
|
if (isset($this->defaultControlMappings[$input]) || isset($this->customControlMappings[$input])) {
|
||||||
|
throw new \InvalidArgumentException('Cannot rebind this input');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->customControlMappings[$input] = $callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCustomControlMappings() : array
|
||||||
|
{
|
||||||
|
return $this->customControlMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthand function to add multiple custom control mapping at once
|
||||||
|
*/
|
||||||
|
public function addCustomControlMappings(array $map) : void
|
||||||
|
{
|
||||||
|
foreach ($map as $input => $callable) {
|
||||||
|
$this->addCustomControlMapping($input, $callable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a custom control mapping
|
||||||
|
*/
|
||||||
|
public function removeCustomControlMapping(string $input) : void
|
||||||
|
{
|
||||||
|
if (!isset($this->customControlMappings[$input])) {
|
||||||
|
throw new \InvalidArgumentException('This input is not registered');
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($this->customControlMappings[$input]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display menu and capture input
|
||||||
|
*/
|
||||||
|
private function display() : void
|
||||||
|
{
|
||||||
|
$this->draw();
|
||||||
|
|
||||||
|
$reader = new NonCanonicalReader($this->terminal);
|
||||||
|
$reader->addControlMappings($this->defaultControlMappings);
|
||||||
|
|
||||||
|
while ($this->isOpen()) {
|
||||||
|
$char = $reader->readCharacter();
|
||||||
|
if (!$char->isHandledControl()) {
|
||||||
|
$rawChar = $char->get();
|
||||||
|
if (isset($this->customControlMappings[$rawChar])) {
|
||||||
|
$this->customControlMappings[$rawChar]($this);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($char->getControl()) {
|
||||||
|
case InputCharacter::UP:
|
||||||
|
case InputCharacter::DOWN:
|
||||||
|
$this->moveSelectionVertically($char->getControl());
|
||||||
|
$this->draw();
|
||||||
|
break;
|
||||||
|
case InputCharacter::LEFT:
|
||||||
|
case InputCharacter::RIGHT:
|
||||||
|
$this->moveSelectionHorizontally($char->getControl());
|
||||||
|
$this->draw();
|
||||||
|
break;
|
||||||
|
case InputCharacter::ENTER:
|
||||||
|
$this->executeCurrentItem();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the selection in a given direction, up / down
|
||||||
|
*/
|
||||||
|
protected function moveSelectionVertically(string $direction) : void
|
||||||
|
{
|
||||||
|
$itemKeys = array_keys($this->items);
|
||||||
|
|
||||||
|
$increments = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$increments++;
|
||||||
|
|
||||||
|
if ($increments > count($itemKeys)) {
|
||||||
|
//full cycle detected, there must be no selected items
|
||||||
|
//in the menu, so stop trying to select one.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$direction === 'UP'
|
||||||
|
? $this->selectedItem--
|
||||||
|
: $this->selectedItem++;
|
||||||
|
|
||||||
|
if ($this->selectedItem !== null && !array_key_exists($this->selectedItem, $this->items)) {
|
||||||
|
$this->selectedItem = $direction === 'UP'
|
||||||
|
? (int) end($itemKeys)
|
||||||
|
: (int) reset($itemKeys);
|
||||||
|
}
|
||||||
|
} while (!$this->canSelect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the selection in a given direction, left / right
|
||||||
|
*/
|
||||||
|
protected function moveSelectionHorizontally(string $direction) : void
|
||||||
|
{
|
||||||
|
if (!$this->items[$this->selectedItem] instanceof SplitItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var SplitItem $item */
|
||||||
|
$item = $this->items[$this->selectedItem];
|
||||||
|
$itemKeys = array_keys($item->getItems());
|
||||||
|
$selectedItemIndex = $item->getSelectedItemIndex();
|
||||||
|
|
||||||
|
if (null === $selectedItemIndex) {
|
||||||
|
$selectedItemIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
$direction === 'LEFT'
|
||||||
|
? $selectedItemIndex--
|
||||||
|
: $selectedItemIndex++;
|
||||||
|
|
||||||
|
if (!array_key_exists($selectedItemIndex, $item->getItems())) {
|
||||||
|
$selectedItemIndex = $direction === 'LEFT'
|
||||||
|
? (int) end($itemKeys)
|
||||||
|
: (int) reset($itemKeys);
|
||||||
|
}
|
||||||
|
} while (!$item->canSelectIndex($selectedItemIndex));
|
||||||
|
|
||||||
|
$item->setSelectedItemIndex($selectedItemIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the currently selected item actually be selected?
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* selectable item -> yes
|
||||||
|
* static item -> no
|
||||||
|
* split item with only static items -> no
|
||||||
|
* split item with at least one selectable item -> yes
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function canSelect() : bool
|
||||||
|
{
|
||||||
|
return $this->items[$this->selectedItem]->canSelect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the item the user actually selected
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function getSelectedItem() : MenuItemInterface
|
||||||
|
{
|
||||||
|
if (null === $this->selectedItem) {
|
||||||
|
throw new \RuntimeException('No selected item');
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->items[$this->selectedItem];
|
||||||
|
return $item instanceof SplitItem
|
||||||
|
? $item->getSelectedItem()
|
||||||
|
: $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSelectedItem(MenuItemInterface $item) : void
|
||||||
|
{
|
||||||
|
$key = array_search($item, $this->items, true);
|
||||||
|
|
||||||
|
if (false === $key) {
|
||||||
|
throw new \InvalidArgumentException('Item does not exist in menu');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->selectedItem = (int) $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSelectedItemIndex() : int
|
||||||
|
{
|
||||||
|
if (null === $this->selectedItem) {
|
||||||
|
throw new \RuntimeException('No selected item');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->selectedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemByIndex(int $index) : MenuItemInterface
|
||||||
|
{
|
||||||
|
if (!isset($this->items[$index])) {
|
||||||
|
throw new \RuntimeException('Item with index does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->items[$index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeAsSelected(MenuItemInterface $item) : void
|
||||||
|
{
|
||||||
|
$current = $this->items[$this->selectedItem];
|
||||||
|
$this->setSelectedItem($item);
|
||||||
|
$this->executeCurrentItem();
|
||||||
|
$this->setSelectedItem($current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the current item
|
||||||
|
*/
|
||||||
|
protected function executeCurrentItem() : void
|
||||||
|
{
|
||||||
|
$item = $this->getSelectedItem();
|
||||||
|
|
||||||
|
if ($item->canSelect()) {
|
||||||
|
$callable = $item->getSelectAction();
|
||||||
|
if ($callable) {
|
||||||
|
$callable($this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true we clear the whole terminal screen, useful
|
||||||
|
* for example when reducing the width of the menu, to not
|
||||||
|
* leave leftovers of the previous wider menu.
|
||||||
|
*
|
||||||
|
* Redraw the menu
|
||||||
|
*/
|
||||||
|
public function redraw(bool $clear = false) : void
|
||||||
|
{
|
||||||
|
if ($clear) {
|
||||||
|
$this->terminal->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertOpen();
|
||||||
|
$this->draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertOpen() : void
|
||||||
|
{
|
||||||
|
if (!$this->isOpen()) {
|
||||||
|
throw new MenuNotOpenException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw the menu to STDOUT
|
||||||
|
*/
|
||||||
|
protected function draw() : void
|
||||||
|
{
|
||||||
|
$frame = new Frame;
|
||||||
|
|
||||||
|
$frame->newLine(2);
|
||||||
|
|
||||||
|
if ($this->style->getBorderTopWidth() > 0) {
|
||||||
|
$frame->addRows($this->style->getBorderTopRows());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->style->getPaddingTopBottom() > 0) {
|
||||||
|
$frame->addRows($this->style->getPaddingTopBottomRows());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->title) {
|
||||||
|
$frame->addRows($this->drawMenuItem(new StaticItem($this->title)));
|
||||||
|
$frame->addRows($this->drawMenuItem(new LineBreakItem($this->style->getTitleSeparator())));
|
||||||
|
}
|
||||||
|
|
||||||
|
array_map(function ($item, $index) use ($frame) {
|
||||||
|
$frame->addRows($this->drawMenuItem($item, $index === $this->selectedItem));
|
||||||
|
}, $this->items, array_keys($this->items));
|
||||||
|
|
||||||
|
|
||||||
|
if ($this->style->getPaddingTopBottom() > 0) {
|
||||||
|
$frame->addRows($this->style->getPaddingTopBottomRows());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->style->getBorderBottomWidth() > 0) {
|
||||||
|
$frame->addRows($this->style->getBorderBottomRows());
|
||||||
|
}
|
||||||
|
|
||||||
|
$frame->newLine(2);
|
||||||
|
|
||||||
|
$this->terminal->moveCursorToTop();
|
||||||
|
foreach ($frame->getRows() as $row) {
|
||||||
|
if ($row == "\n") {
|
||||||
|
$this->terminal->clearLine();
|
||||||
|
}
|
||||||
|
$this->terminal->write($row);
|
||||||
|
}
|
||||||
|
$this->terminal->clearDown();
|
||||||
|
|
||||||
|
$this->currentFrame = $frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw a menu item
|
||||||
|
*/
|
||||||
|
protected function drawMenuItem(MenuItemInterface $item, bool $selected = false) : array
|
||||||
|
{
|
||||||
|
$rows = $item->getRows($this->style, $selected);
|
||||||
|
|
||||||
|
if ($item instanceof SplitItem) {
|
||||||
|
$selected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invertedColoursSetCode = $selected
|
||||||
|
? $this->style->getInvertedColoursSetCode()
|
||||||
|
: '';
|
||||||
|
$invertedColoursUnsetCode = $selected
|
||||||
|
? $this->style->getInvertedColoursUnsetCode()
|
||||||
|
: '';
|
||||||
|
|
||||||
|
if ($this->style->getBorderLeftWidth() || $this->style->getBorderRightWidth()) {
|
||||||
|
$borderColour = $this->style->getBorderColourCode();
|
||||||
|
} else {
|
||||||
|
$borderColour = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_map(function ($row) use ($invertedColoursSetCode, $invertedColoursUnsetCode, $borderColour) {
|
||||||
|
return sprintf(
|
||||||
|
"%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
||||||
|
str_repeat(' ', $this->style->getMargin()),
|
||||||
|
$borderColour,
|
||||||
|
str_repeat(' ', $this->style->getBorderLeftWidth()),
|
||||||
|
$this->style->getColoursSetCode(),
|
||||||
|
$invertedColoursSetCode,
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
$row,
|
||||||
|
str_repeat(' ', $this->style->getRightHandPadding(mb_strlen(s::stripAnsiEscapeSequence($row)))),
|
||||||
|
$invertedColoursUnsetCode,
|
||||||
|
$borderColour,
|
||||||
|
str_repeat(' ', $this->style->getBorderRightWidth()),
|
||||||
|
$this->style->getColoursResetCode()
|
||||||
|
);
|
||||||
|
}, $rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws InvalidTerminalException
|
||||||
|
*/
|
||||||
|
public function open() : void
|
||||||
|
{
|
||||||
|
if ($this->isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($this->items) === 0) {
|
||||||
|
throw new \RuntimeException('Menu must have at least 1 item before it can be opened');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->configureTerminal();
|
||||||
|
$this->open = true;
|
||||||
|
$this->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the menu
|
||||||
|
*
|
||||||
|
* @throws InvalidTerminalException
|
||||||
|
*/
|
||||||
|
public function close() : void
|
||||||
|
{
|
||||||
|
$menu = $this;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$menu->closeThis();
|
||||||
|
$menu = $menu->getParent();
|
||||||
|
} while (null !== $menu);
|
||||||
|
|
||||||
|
$this->tearDownTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function closeThis() : void
|
||||||
|
{
|
||||||
|
$this->terminal->clean();
|
||||||
|
$this->terminal->moveCursorToTop();
|
||||||
|
$this->open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return MenuItemInterface[]
|
||||||
|
*/
|
||||||
|
public function getItems() : array
|
||||||
|
{
|
||||||
|
return $this->items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeItem(MenuItemInterface $item) : void
|
||||||
|
{
|
||||||
|
$key = array_search($item, $this->items, true);
|
||||||
|
|
||||||
|
if (false === $key) {
|
||||||
|
throw new \InvalidArgumentException('Item does not exist in menu');
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($this->items[$key]);
|
||||||
|
$this->items = array_values($this->items);
|
||||||
|
|
||||||
|
if ($this->selectedItem === $key) {
|
||||||
|
$this->selectedItem = null;
|
||||||
|
$this->selectFirstItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyle() : MenuStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(MenuStyle $style) : void
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setItemStyle(ItemStyle $style, string $styleClass) : void
|
||||||
|
{
|
||||||
|
$this->itemStyleLocator->setStyle($style, $styleClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemStyle(string $styleClass) : ItemStyle
|
||||||
|
{
|
||||||
|
return $this->itemStyleLocator->getStyle($styleClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemStyleForItem(MenuItemInterface $item) : ItemStyle
|
||||||
|
{
|
||||||
|
return $this->itemStyleLocator->getStyleForMenuItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyleLocator() : Locator
|
||||||
|
{
|
||||||
|
return $this->itemStyleLocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importStyles(CliMenu $menu) : void
|
||||||
|
{
|
||||||
|
if (!$this->style->hasChangedFromDefaults()) {
|
||||||
|
$this->style = $menu->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->itemStyleLocator->importFrom($menu->itemStyleLocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrentFrame() : Frame
|
||||||
|
{
|
||||||
|
return $this->currentFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function flash(string $text, MenuStyle $style = null) : Flash
|
||||||
|
{
|
||||||
|
$this->guardSingleLine($text);
|
||||||
|
|
||||||
|
$style = $style ?? (new MenuStyle($this->terminal))
|
||||||
|
->setBg('yellow')
|
||||||
|
->setFg('red');
|
||||||
|
|
||||||
|
return new Flash($this, $style, $this->terminal, $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirm(string $text, MenuStyle $style = null) : Confirm
|
||||||
|
{
|
||||||
|
$this->guardSingleLine($text);
|
||||||
|
|
||||||
|
$style = $style ?? (new MenuStyle($this->terminal))
|
||||||
|
->setBg('yellow')
|
||||||
|
->setFg('red');
|
||||||
|
|
||||||
|
return new Confirm($this, $style, $this->terminal, $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancellableConfirm(string $text, MenuStyle $style = null) : CancellableConfirm
|
||||||
|
{
|
||||||
|
$this->guardSingleLine($text);
|
||||||
|
|
||||||
|
$style = $style ?? (new MenuStyle($this->terminal))
|
||||||
|
->setBg('yellow')
|
||||||
|
->setFg('red');
|
||||||
|
|
||||||
|
return new CancellableConfirm($this, $style, $this->terminal, $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function askNumber(MenuStyle $style = null) : Number
|
||||||
|
{
|
||||||
|
$this->assertOpen();
|
||||||
|
|
||||||
|
$style = $style ?? (new MenuStyle($this->terminal))
|
||||||
|
->setBg('yellow')
|
||||||
|
->setFg('red');
|
||||||
|
|
||||||
|
return new Number(new InputIO($this, $this->terminal), $style);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function askText(MenuStyle $style = null) : Text
|
||||||
|
{
|
||||||
|
$this->assertOpen();
|
||||||
|
|
||||||
|
$style = $style ?? (new MenuStyle($this->terminal))
|
||||||
|
->setBg('yellow')
|
||||||
|
->setFg('red');
|
||||||
|
|
||||||
|
return new Text(new InputIO($this, $this->terminal), $style);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function askPassword(MenuStyle $style = null) : Password
|
||||||
|
{
|
||||||
|
$this->assertOpen();
|
||||||
|
|
||||||
|
$style = $style ?? (new MenuStyle($this->terminal))
|
||||||
|
->setBg('yellow')
|
||||||
|
->setFg('red');
|
||||||
|
|
||||||
|
return new Password(new InputIO($this, $this->terminal), $style);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function guardSingleLine(string $text) : void
|
||||||
|
{
|
||||||
|
if (strpos($text, "\n") !== false) {
|
||||||
|
throw new \InvalidArgumentException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function propagateStyles() : void
|
||||||
|
{
|
||||||
|
collect($this->items)
|
||||||
|
->filter(function (int $k, MenuItemInterface $item) {
|
||||||
|
return $this->itemStyleLocator->hasStyleForMenuItem($item);
|
||||||
|
})
|
||||||
|
->filter(function (int $k, MenuItemInterface $item) {
|
||||||
|
return !$item->getStyle()->hasChangedFromDefaults();
|
||||||
|
})
|
||||||
|
->each(function (int $k, $item) {
|
||||||
|
$item->setStyle(clone $this->getItemStyleForItem($item));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
collect($this->items)
|
||||||
|
->filter(function (int $k, MenuItemInterface $item) {
|
||||||
|
return $item instanceof PropagatesStyles;
|
||||||
|
})
|
||||||
|
->each(function (int $k, $item) {
|
||||||
|
$item->propagateStyles($this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
112
src/ncc/ThirdParty/php-school/cli-menu/Dialogue/CancellableConfirm.php
vendored
Normal file
112
src/ncc/ThirdParty/php-school/cli-menu/Dialogue/CancellableConfirm.php
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Dialogue;
|
||||||
|
|
||||||
|
use PhpSchool\Terminal\InputCharacter;
|
||||||
|
use PhpSchool\Terminal\NonCanonicalReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class CancellableConfirm extends Dialogue
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $confirm = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display confirmation with a button with the given text
|
||||||
|
*/
|
||||||
|
public function display(string $confirmText = 'OK', string $cancelText = 'Cancel') : bool
|
||||||
|
{
|
||||||
|
$this->drawDialog($confirmText, $cancelText);
|
||||||
|
|
||||||
|
$reader = new NonCanonicalReader($this->terminal);
|
||||||
|
|
||||||
|
while ($char = $reader->readCharacter()) {
|
||||||
|
if ($char->isControl() && $char->getControl() === InputCharacter::ENTER) {
|
||||||
|
$this->parentMenu->redraw();
|
||||||
|
return $this->confirm;
|
||||||
|
} elseif ($char->isControl() && $char->getControl() === InputCharacter::TAB ||
|
||||||
|
($char->isControl() && $char->getControl() === InputCharacter::RIGHT && $this->confirm) ||
|
||||||
|
($char->isControl() && $char->getControl() === InputCharacter::LEFT && !$this->confirm)
|
||||||
|
) {
|
||||||
|
$this->confirm = !$this->confirm;
|
||||||
|
$this->drawDialog($confirmText, $cancelText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawDialog(string $confirmText = 'OK', string $cancelText = 'Cancel'): void
|
||||||
|
{
|
||||||
|
$this->assertMenuOpen();
|
||||||
|
|
||||||
|
$this->terminal->moveCursorToRow($this->y);
|
||||||
|
|
||||||
|
$promptWidth = mb_strlen($this->text) + 4;
|
||||||
|
|
||||||
|
$buttonLength = mb_strlen($confirmText) + 6;
|
||||||
|
$buttonLength += mb_strlen($cancelText) + 7;
|
||||||
|
|
||||||
|
$confirmButton = sprintf(
|
||||||
|
'%s%s < %s > %s%s',
|
||||||
|
$this->style->getOptionCode($this->confirm ? 'bold' : 'dim'),
|
||||||
|
$this->style->getInvertedColoursSetCode(),
|
||||||
|
$confirmText,
|
||||||
|
$this->style->getInvertedColoursUnsetCode(),
|
||||||
|
$this->style->getOptionCode($this->confirm ? 'bold' : 'dim', false)
|
||||||
|
);
|
||||||
|
|
||||||
|
$cancelButton = sprintf(
|
||||||
|
'%s%s < %s > %s%s',
|
||||||
|
$this->style->getOptionCode($this->confirm ? 'dim' : 'bold'),
|
||||||
|
$this->style->getInvertedColoursSetCode(),
|
||||||
|
$cancelText,
|
||||||
|
$this->style->getInvertedColoursUnsetCode(),
|
||||||
|
$this->style->getOptionCode($this->confirm ? 'dim' : 'bold', false)
|
||||||
|
);
|
||||||
|
|
||||||
|
$buttonRow = $confirmButton . " " . $cancelButton;
|
||||||
|
|
||||||
|
if ($promptWidth < $buttonLength) {
|
||||||
|
$pad = ($buttonLength - $promptWidth) / 2;
|
||||||
|
$this->text = sprintf(
|
||||||
|
'%s%s%s',
|
||||||
|
str_repeat(' ', intval(round($pad, 0, 2) + $this->style->getPaddingLeftRight())),
|
||||||
|
$this->text,
|
||||||
|
str_repeat(' ', intval(round($pad, 0, 1) + $this->style->getPaddingLeftRight()))
|
||||||
|
);
|
||||||
|
$promptWidth = mb_strlen($this->text) + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
$leftFill = (int) (($promptWidth / 2) - ($buttonLength / 2));
|
||||||
|
|
||||||
|
$this->emptyRow();
|
||||||
|
|
||||||
|
$this->write(sprintf(
|
||||||
|
"%s%s%s%s%s\n",
|
||||||
|
$this->style->getColoursSetCode(),
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
$this->text,
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
$this->style->getColoursResetCode()
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->emptyRow();
|
||||||
|
|
||||||
|
$this->write(sprintf(
|
||||||
|
"%s%s%s%s%s\n",
|
||||||
|
$this->style->getColoursSetCode(),
|
||||||
|
str_repeat(' ', $leftFill),
|
||||||
|
$buttonRow,
|
||||||
|
str_repeat(' ', (int) ceil($promptWidth - $leftFill - $buttonLength)),
|
||||||
|
$this->style->getColoursResetCode()
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->emptyRow();
|
||||||
|
|
||||||
|
$this->terminal->moveCursorToTop();
|
||||||
|
}
|
||||||
|
}
|
73
src/ncc/ThirdParty/php-school/cli-menu/Dialogue/Confirm.php
vendored
Normal file
73
src/ncc/ThirdParty/php-school/cli-menu/Dialogue/Confirm.php
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Dialogue;
|
||||||
|
|
||||||
|
use PhpSchool\Terminal\InputCharacter;
|
||||||
|
use PhpSchool\Terminal\NonCanonicalReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class Confirm extends Dialogue
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display confirmation with a button with the given text
|
||||||
|
*/
|
||||||
|
public function display(string $confirmText = 'OK') : void
|
||||||
|
{
|
||||||
|
$this->assertMenuOpen();
|
||||||
|
|
||||||
|
$this->terminal->moveCursorToRow($this->y);
|
||||||
|
|
||||||
|
$promptWidth = mb_strlen($this->text) + 4;
|
||||||
|
|
||||||
|
$this->emptyRow();
|
||||||
|
|
||||||
|
$this->write(sprintf(
|
||||||
|
"%s%s%s%s%s\n",
|
||||||
|
$this->style->getColoursSetCode(),
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
$this->text,
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
$this->style->getColoursResetCode()
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->emptyRow();
|
||||||
|
|
||||||
|
$confirmText = sprintf(' < %s > ', $confirmText);
|
||||||
|
$leftFill = (int) (($promptWidth / 2) - (mb_strlen($confirmText) / 2));
|
||||||
|
|
||||||
|
$this->write(sprintf(
|
||||||
|
"%s%s%s%s%s%s%s\n",
|
||||||
|
$this->style->getColoursSetCode(),
|
||||||
|
str_repeat(' ', $leftFill),
|
||||||
|
$this->style->getInvertedColoursSetCode(),
|
||||||
|
$confirmText,
|
||||||
|
$this->style->getInvertedColoursUnsetCode(),
|
||||||
|
str_repeat(' ', (int) ceil($promptWidth - $leftFill - mb_strlen($confirmText))),
|
||||||
|
$this->style->getColoursResetCode()
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->write(sprintf(
|
||||||
|
"%s%s%s%s%s\n",
|
||||||
|
$this->style->getColoursSetCode(),
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
str_repeat(' ', mb_strlen($this->text)),
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
$this->style->getColoursResetCode()
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->terminal->moveCursorToTop();
|
||||||
|
|
||||||
|
$reader = new NonCanonicalReader($this->terminal);
|
||||||
|
|
||||||
|
while ($char = $reader->readCharacter()) {
|
||||||
|
if ($char->isControl() && $char->getControl() === InputCharacter::ENTER) {
|
||||||
|
$this->parentMenu->redraw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
src/ncc/ThirdParty/php-school/cli-menu/Dialogue/Dialogue.php
vendored
Normal file
121
src/ncc/ThirdParty/php-school/cli-menu/Dialogue/Dialogue.php
vendored
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Dialogue;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use PhpSchool\CliMenu\Exception\MenuNotOpenException;
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\Terminal\Terminal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
abstract class Dialogue
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var MenuStyle
|
||||||
|
*/
|
||||||
|
protected $style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CliMenu
|
||||||
|
*/
|
||||||
|
protected $parentMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Terminal
|
||||||
|
*/
|
||||||
|
protected $terminal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string $text
|
||||||
|
*/
|
||||||
|
protected $text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool $cancellable
|
||||||
|
*/
|
||||||
|
protected $cancellable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $x;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $y;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
CliMenu $parentMenu,
|
||||||
|
MenuStyle $menuStyle,
|
||||||
|
Terminal $terminal,
|
||||||
|
string $text
|
||||||
|
) {
|
||||||
|
$this->style = $menuStyle;
|
||||||
|
$this->terminal = $terminal;
|
||||||
|
$this->text = $text;
|
||||||
|
$this->parentMenu = $parentMenu;
|
||||||
|
|
||||||
|
$this->calculateCoordinates();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws MenuNotOpenException
|
||||||
|
*/
|
||||||
|
protected function assertMenuOpen() : void
|
||||||
|
{
|
||||||
|
if (!$this->parentMenu->isOpen()) {
|
||||||
|
throw new MenuNotOpenException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the co-ordinates to write the messages
|
||||||
|
*/
|
||||||
|
protected function calculateCoordinates() : void
|
||||||
|
{
|
||||||
|
//y
|
||||||
|
$textLines = count(explode("\n", $this->text)) + 2;
|
||||||
|
$this->y = (int) (ceil($this->parentMenu->getCurrentFrame()->count() / 2) - ceil($textLines / 2) + 1);
|
||||||
|
|
||||||
|
//x
|
||||||
|
$parentStyle = $this->parentMenu->getStyle();
|
||||||
|
$dialogueHalfLength = (int) ((mb_strlen($this->text) + ($this->style->getPaddingLeftRight() * 2)) / 2);
|
||||||
|
$widthHalfLength = (int) ceil($parentStyle->getWidth() / 2 + $parentStyle->getMargin());
|
||||||
|
$this->x = $widthHalfLength - $dialogueHalfLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an empty row
|
||||||
|
*/
|
||||||
|
protected function emptyRow() : void
|
||||||
|
{
|
||||||
|
$this->write(
|
||||||
|
sprintf(
|
||||||
|
"%s%s%s%s%s\n",
|
||||||
|
$this->style->getColoursSetCode(),
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
str_repeat(' ', mb_strlen($this->text)),
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
$this->style->getColoursResetCode()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write some text at a particular column
|
||||||
|
*/
|
||||||
|
protected function write(string $text, int $column = null) : void
|
||||||
|
{
|
||||||
|
$this->terminal->moveCursorToColumn($column ?: $this->x);
|
||||||
|
$this->terminal->write($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyle() : MenuStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
}
|
43
src/ncc/ThirdParty/php-school/cli-menu/Dialogue/Flash.php
vendored
Normal file
43
src/ncc/ThirdParty/php-school/cli-menu/Dialogue/Flash.php
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Dialogue;
|
||||||
|
|
||||||
|
use PhpSchool\Terminal\NonCanonicalReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class Flash extends Dialogue
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Flash a message on top of the menu which
|
||||||
|
* disappears on any keystroke.
|
||||||
|
*/
|
||||||
|
public function display() : void
|
||||||
|
{
|
||||||
|
$this->assertMenuOpen();
|
||||||
|
|
||||||
|
$this->terminal->moveCursorToRow($this->y);
|
||||||
|
|
||||||
|
$this->emptyRow();
|
||||||
|
|
||||||
|
$this->write(sprintf(
|
||||||
|
"%s%s%s%s%s\n",
|
||||||
|
$this->style->getColoursSetCode(),
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
$this->text,
|
||||||
|
str_repeat(' ', $this->style->getPaddingLeftRight()),
|
||||||
|
$this->style->getColoursResetCode()
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->emptyRow();
|
||||||
|
|
||||||
|
$this->terminal->moveCursorToTop();
|
||||||
|
|
||||||
|
$reader = new NonCanonicalReader($this->terminal);
|
||||||
|
$reader->readCharacter();
|
||||||
|
|
||||||
|
$this->parentMenu->redraw();
|
||||||
|
}
|
||||||
|
}
|
21
src/ncc/ThirdParty/php-school/cli-menu/Exception/CannotShrinkMenuException.php
vendored
Normal file
21
src/ncc/ThirdParty/php-school/cli-menu/Exception/CannotShrinkMenuException.php
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Exception;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
class CannotShrinkMenuException extends InvalidArgumentException
|
||||||
|
{
|
||||||
|
public static function fromMarginAndTerminalWidth(int $margin, int $terminalWidth) : self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
sprintf(
|
||||||
|
'Cannot shrink menu. Margin: %s * 2 with terminal width: %s leaves no space for menu',
|
||||||
|
$margin,
|
||||||
|
$terminalWidth
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
15
src/ncc/ThirdParty/php-school/cli-menu/Exception/InvalidShortcutException.php
vendored
Normal file
15
src/ncc/ThirdParty/php-school/cli-menu/Exception/InvalidShortcutException.php
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class InvalidShortcutException extends \RuntimeException
|
||||||
|
{
|
||||||
|
public static function fromShortcut(string $shortcut) : self
|
||||||
|
{
|
||||||
|
return new self(sprintf('Shortcut key must be only one character. Got: "%s"', $shortcut));
|
||||||
|
}
|
||||||
|
}
|
12
src/ncc/ThirdParty/php-school/cli-menu/Exception/InvalidTerminalException.php
vendored
Normal file
12
src/ncc/ThirdParty/php-school/cli-menu/Exception/InvalidTerminalException.php
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class InvalidTerminalException extends \Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
12
src/ncc/ThirdParty/php-school/cli-menu/Exception/MenuNotOpenException.php
vendored
Normal file
12
src/ncc/ThirdParty/php-school/cli-menu/Exception/MenuNotOpenException.php
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class MenuNotOpenException extends \RuntimeException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
47
src/ncc/ThirdParty/php-school/cli-menu/Frame.php
vendored
Normal file
47
src/ncc/ThirdParty/php-school/cli-menu/Frame.php
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the current screen being displayed
|
||||||
|
* contains all rows of output
|
||||||
|
*
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class Frame implements \Countable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $rows = [];
|
||||||
|
|
||||||
|
public function newLine(int $count = 1) : void
|
||||||
|
{
|
||||||
|
foreach (range(1, $count) as $i) {
|
||||||
|
$this->rows[] = "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRows(array $rows = []) : void
|
||||||
|
{
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$this->rows[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRow(string $row) : void
|
||||||
|
{
|
||||||
|
$this->rows[] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function count() : int
|
||||||
|
{
|
||||||
|
return count($this->rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRows() : array
|
||||||
|
{
|
||||||
|
return $this->rows;
|
||||||
|
}
|
||||||
|
}
|
32
src/ncc/ThirdParty/php-school/cli-menu/Input/Input.php
vendored
Normal file
32
src/ncc/ThirdParty/php-school/cli-menu/Input/Input.php
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Input;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
interface Input
|
||||||
|
{
|
||||||
|
public function ask() : InputResult;
|
||||||
|
|
||||||
|
public function validate(string $input) : bool;
|
||||||
|
|
||||||
|
public function setPromptText(string $promptText) : Input;
|
||||||
|
|
||||||
|
public function getPromptText() : string;
|
||||||
|
|
||||||
|
public function setValidationFailedText(string $validationFailedText) : Input;
|
||||||
|
|
||||||
|
public function getValidationFailedText() : string;
|
||||||
|
|
||||||
|
public function setPlaceholderText(string $placeholderText) : Input;
|
||||||
|
|
||||||
|
public function getPlaceholderText() : string;
|
||||||
|
|
||||||
|
public function filter(string $value) : string;
|
||||||
|
|
||||||
|
public function getStyle() : MenuStyle;
|
||||||
|
}
|
256
src/ncc/ThirdParty/php-school/cli-menu/Input/InputIO.php
vendored
Normal file
256
src/ncc/ThirdParty/php-school/cli-menu/Input/InputIO.php
vendored
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Input;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use PhpSchool\CliMenu\Util\StringUtil;
|
||||||
|
use PhpSchool\Terminal\InputCharacter;
|
||||||
|
use PhpSchool\Terminal\NonCanonicalReader;
|
||||||
|
use PhpSchool\Terminal\Terminal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class InputIO
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CliMenu
|
||||||
|
*/
|
||||||
|
private $parentMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Terminal
|
||||||
|
*/
|
||||||
|
private $terminal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable[][]
|
||||||
|
*/
|
||||||
|
private $callbacks = [];
|
||||||
|
|
||||||
|
public function __construct(CliMenu $parentMenu, Terminal $terminal)
|
||||||
|
{
|
||||||
|
$this->terminal = $terminal;
|
||||||
|
$this->parentMenu = $parentMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function collect(Input $input) : InputResult
|
||||||
|
{
|
||||||
|
$this->drawInput($input, $input->getPlaceholderText());
|
||||||
|
|
||||||
|
$inputValue = $input->getPlaceholderText();
|
||||||
|
$havePlaceHolderValue = !empty($inputValue);
|
||||||
|
|
||||||
|
$originalValue = $inputValue;
|
||||||
|
|
||||||
|
$reader = new NonCanonicalReader($this->terminal);
|
||||||
|
|
||||||
|
while ($char = $reader->readCharacter()) {
|
||||||
|
if ($char->isNotControl()) {
|
||||||
|
if ($havePlaceHolderValue) {
|
||||||
|
$inputValue = $char->get();
|
||||||
|
$havePlaceHolderValue = false;
|
||||||
|
} else {
|
||||||
|
$inputValue .= $char->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->parentMenu->redraw();
|
||||||
|
$this->drawInput($input, $inputValue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($char->isHandledControl()) {
|
||||||
|
switch ($char->getControl()) {
|
||||||
|
case InputCharacter::ESC:
|
||||||
|
$this->parentMenu->redraw();
|
||||||
|
return new InputResult($originalValue);
|
||||||
|
case InputCharacter::ENTER:
|
||||||
|
if ($input->validate($inputValue)) {
|
||||||
|
$this->parentMenu->redraw();
|
||||||
|
return new InputResult($inputValue);
|
||||||
|
} else {
|
||||||
|
$this->drawInputWithError($input, $inputValue);
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
case InputCharacter::BACKSPACE:
|
||||||
|
$inputValue = substr($inputValue, 0, -1);
|
||||||
|
$this->parentMenu->redraw();
|
||||||
|
$this->drawInput($input, $inputValue);
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->callbacks[$char->getControl()])) {
|
||||||
|
foreach ($this->callbacks[$char->getControl()] as $callback) {
|
||||||
|
$inputValue = $callback($inputValue);
|
||||||
|
$this->drawInput($input, $inputValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerControlCallback(string $control, callable $callback) : void
|
||||||
|
{
|
||||||
|
if (!isset($this->callbacks[$control])) {
|
||||||
|
$this->callbacks[$control] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->callbacks[$control][] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getInputWidth(array $lines) : int
|
||||||
|
{
|
||||||
|
return max(
|
||||||
|
array_map(
|
||||||
|
function (string $line) {
|
||||||
|
return mb_strlen($line);
|
||||||
|
},
|
||||||
|
$lines
|
||||||
|
)
|
||||||
|
) ? : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateYPosition() : int
|
||||||
|
{
|
||||||
|
$lines = 5; //1. empty 2. prompt text 3. empty 4. input 5. empty
|
||||||
|
|
||||||
|
return (int) (ceil($this->parentMenu->getCurrentFrame()->count() / 2) - ceil($lines /2) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateYPositionWithError() : int
|
||||||
|
{
|
||||||
|
$lines = 7; //1. empty 2. prompt text 3. empty 4. input 5. empty 6. error 7. empty
|
||||||
|
|
||||||
|
return (int) (ceil($this->parentMenu->getCurrentFrame()->count() / 2) - ceil($lines /2) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateXPosition(Input $input, string $userInput) : int
|
||||||
|
{
|
||||||
|
$width = $this->getInputWidth(
|
||||||
|
[
|
||||||
|
$input->getPromptText(),
|
||||||
|
$input->getValidationFailedText(),
|
||||||
|
$userInput
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$parentStyle = $this->parentMenu->getStyle();
|
||||||
|
$halfWidth = ($width + ($input->getStyle()->getPaddingLeftRight() * 2)) / 2;
|
||||||
|
$parentHalfWidth = ceil($parentStyle->getWidth() / 2 + $parentStyle->getMargin());
|
||||||
|
|
||||||
|
return (int) ($parentHalfWidth - $halfWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawLine(Input $input, string $userInput, string $text) : void
|
||||||
|
{
|
||||||
|
$this->terminal->moveCursorToColumn($this->calculateXPosition($input, $userInput));
|
||||||
|
|
||||||
|
$line = sprintf(
|
||||||
|
"%s%s%s%s%s\n",
|
||||||
|
$input->getStyle()->getColoursSetCode(),
|
||||||
|
str_repeat(' ', $input->getStyle()->getPaddingLeftRight()),
|
||||||
|
$text,
|
||||||
|
str_repeat(' ', $input->getStyle()->getPaddingLeftRight()),
|
||||||
|
$input->getStyle()->getColoursResetCode()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->terminal->write($line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawCenteredLine(Input $input, string $userInput, string $text) : void
|
||||||
|
{
|
||||||
|
$width = $this->getInputWidth(
|
||||||
|
[
|
||||||
|
$input->getPromptText(),
|
||||||
|
$input->getValidationFailedText(),
|
||||||
|
$userInput
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$textLength = mb_strlen(StringUtil::stripAnsiEscapeSequence($text));
|
||||||
|
$leftFill = (int) (($width / 2) - ($textLength / 2));
|
||||||
|
$rightFill = (int) ceil($width - $leftFill - $textLength);
|
||||||
|
|
||||||
|
$this->drawLine(
|
||||||
|
$input,
|
||||||
|
$userInput,
|
||||||
|
sprintf(
|
||||||
|
'%s%s%s',
|
||||||
|
str_repeat(' ', $leftFill),
|
||||||
|
$text,
|
||||||
|
str_repeat(' ', $rightFill)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawEmptyLine(Input $input, string $userInput) : void
|
||||||
|
{
|
||||||
|
$width = $this->getInputWidth(
|
||||||
|
[
|
||||||
|
$input->getPromptText(),
|
||||||
|
$input->getValidationFailedText(),
|
||||||
|
$userInput
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->drawLine(
|
||||||
|
$input,
|
||||||
|
$userInput,
|
||||||
|
str_repeat(' ', $width)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawInput(Input $input, string $userInput) : void
|
||||||
|
{
|
||||||
|
$this->terminal->moveCursorToRow($this->calculateYPosition());
|
||||||
|
|
||||||
|
$this->drawEmptyLine($input, $userInput);
|
||||||
|
$this->drawTitle($input, $userInput);
|
||||||
|
$this->drawEmptyLine($input, $userInput);
|
||||||
|
$this->drawInputField($input, $input->filter($userInput));
|
||||||
|
$this->drawEmptyLine($input, $userInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawInputWithError(Input $input, string $userInput) : void
|
||||||
|
{
|
||||||
|
$this->terminal->moveCursorToRow($this->calculateYPositionWithError());
|
||||||
|
|
||||||
|
$this->drawEmptyLine($input, $userInput);
|
||||||
|
$this->drawTitle($input, $userInput);
|
||||||
|
$this->drawEmptyLine($input, $userInput);
|
||||||
|
$this->drawInputField($input, $input->filter($userInput));
|
||||||
|
$this->drawEmptyLine($input, $userInput);
|
||||||
|
$this->drawCenteredLine(
|
||||||
|
$input,
|
||||||
|
$userInput,
|
||||||
|
$input->getValidationFailedText()
|
||||||
|
);
|
||||||
|
$this->drawEmptyLine($input, $userInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawTitle(Input $input, string $userInput) : void
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->drawCenteredLine(
|
||||||
|
$input,
|
||||||
|
$userInput,
|
||||||
|
$input->getPromptText()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drawInputField(Input $input, string $userInput) : void
|
||||||
|
{
|
||||||
|
$this->drawCenteredLine(
|
||||||
|
$input,
|
||||||
|
$userInput,
|
||||||
|
sprintf(
|
||||||
|
'%s%s%s',
|
||||||
|
$input->getStyle()->getInvertedColoursSetCode(),
|
||||||
|
$userInput,
|
||||||
|
$input->getStyle()->getInvertedColoursUnsetCode()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
25
src/ncc/ThirdParty/php-school/cli-menu/Input/InputResult.php
vendored
Normal file
25
src/ncc/ThirdParty/php-school/cli-menu/Input/InputResult.php
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class InputResult
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $input;
|
||||||
|
|
||||||
|
public function __construct(string $input)
|
||||||
|
{
|
||||||
|
$this->input = $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetch() : string
|
||||||
|
{
|
||||||
|
return $this->input;
|
||||||
|
}
|
||||||
|
}
|
130
src/ncc/ThirdParty/php-school/cli-menu/Input/Number.php
vendored
Normal file
130
src/ncc/ThirdParty/php-school/cli-menu/Input/Number.php
vendored
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Input;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\Terminal\InputCharacter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class Number implements Input
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var InputIO
|
||||||
|
*/
|
||||||
|
private $inputIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $promptText = 'Enter a number:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $validationFailedText = 'Not a valid number, try again';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $placeholderText = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|callable
|
||||||
|
*/
|
||||||
|
private $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
public function __construct(InputIO $inputIO, MenuStyle $style)
|
||||||
|
{
|
||||||
|
$this->inputIO = $inputIO;
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPromptText(string $promptText) : Input
|
||||||
|
{
|
||||||
|
$this->promptText = $promptText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPromptText() : string
|
||||||
|
{
|
||||||
|
return $this->promptText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValidationFailedText(string $validationFailedText) : Input
|
||||||
|
{
|
||||||
|
$this->validationFailedText = $validationFailedText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValidationFailedText() : string
|
||||||
|
{
|
||||||
|
return $this->validationFailedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlaceholderText(string $placeholderText) : Input
|
||||||
|
{
|
||||||
|
$this->placeholderText = $placeholderText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() : string
|
||||||
|
{
|
||||||
|
return $this->placeholderText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValidator(callable $validator) : Input
|
||||||
|
{
|
||||||
|
$this->validator = $validator;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ask() : InputResult
|
||||||
|
{
|
||||||
|
$this->inputIO->registerControlCallback(InputCharacter::UP, function (string $input) {
|
||||||
|
return $this->validate($input) ? (string) ((int) $input + 1) : $input;
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->inputIO->registerControlCallback(InputCharacter::DOWN, function (string $input) {
|
||||||
|
return $this->validate($input) ? (string) ((int) $input - 1) : $input;
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this->inputIO->collect($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(string $input) : bool
|
||||||
|
{
|
||||||
|
if ($this->validator) {
|
||||||
|
$validator = $this->validator;
|
||||||
|
|
||||||
|
if ($validator instanceof \Closure) {
|
||||||
|
$validator = $validator->bindTo($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $validator($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) preg_match('/^-?\d+$/', $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter(string $value) : string
|
||||||
|
{
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyle() : MenuStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
}
|
131
src/ncc/ThirdParty/php-school/cli-menu/Input/Password.php
vendored
Normal file
131
src/ncc/ThirdParty/php-school/cli-menu/Input/Password.php
vendored
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Input;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class Password implements Input
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var InputIO
|
||||||
|
*/
|
||||||
|
private $inputIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $promptText = 'Enter password:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $validationFailedText = 'Invalid password, try again';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $placeholderText = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|callable
|
||||||
|
*/
|
||||||
|
private $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $passwordLength = 16;
|
||||||
|
|
||||||
|
public function __construct(InputIO $inputIO, MenuStyle $style)
|
||||||
|
{
|
||||||
|
$this->inputIO = $inputIO;
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPromptText(string $promptText) : Input
|
||||||
|
{
|
||||||
|
$this->promptText = $promptText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPromptText() : string
|
||||||
|
{
|
||||||
|
return $this->promptText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValidationFailedText(string $validationFailedText) : Input
|
||||||
|
{
|
||||||
|
$this->validationFailedText = $validationFailedText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValidationFailedText() : string
|
||||||
|
{
|
||||||
|
return $this->validationFailedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlaceholderText(string $placeholderText) : Input
|
||||||
|
{
|
||||||
|
$this->placeholderText = $placeholderText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() : string
|
||||||
|
{
|
||||||
|
return $this->placeholderText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValidator(callable $validator) : Input
|
||||||
|
{
|
||||||
|
$this->validator = $validator;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ask() : InputResult
|
||||||
|
{
|
||||||
|
return $this->inputIO->collect($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(string $input) : bool
|
||||||
|
{
|
||||||
|
if ($this->validator) {
|
||||||
|
$validator = $this->validator;
|
||||||
|
|
||||||
|
if ($validator instanceof \Closure) {
|
||||||
|
$validator = $validator->bindTo($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $validator($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mb_strlen($input) >= $this->passwordLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter(string $value) : string
|
||||||
|
{
|
||||||
|
return str_repeat('*', mb_strlen($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyle() : MenuStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPasswordLength(int $length) : int
|
||||||
|
{
|
||||||
|
return $this->passwordLength = $length;
|
||||||
|
}
|
||||||
|
}
|
121
src/ncc/ThirdParty/php-school/cli-menu/Input/Text.php
vendored
Normal file
121
src/ncc/ThirdParty/php-school/cli-menu/Input/Text.php
vendored
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Input;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||||
|
*/
|
||||||
|
class Text implements Input
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var InputIO
|
||||||
|
*/
|
||||||
|
private $inputIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $promptText = 'Enter text:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $validationFailedText = 'Invalid, try again';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $placeholderText = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|callable
|
||||||
|
*/
|
||||||
|
private $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
public function __construct(InputIO $inputIO, MenuStyle $style)
|
||||||
|
{
|
||||||
|
$this->inputIO = $inputIO;
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPromptText(string $promptText) : Input
|
||||||
|
{
|
||||||
|
$this->promptText = $promptText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPromptText() : string
|
||||||
|
{
|
||||||
|
return $this->promptText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValidationFailedText(string $validationFailedText) : Input
|
||||||
|
{
|
||||||
|
$this->validationFailedText = $validationFailedText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValidationFailedText() : string
|
||||||
|
{
|
||||||
|
return $this->validationFailedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlaceholderText(string $placeholderText) : Input
|
||||||
|
{
|
||||||
|
$this->placeholderText = $placeholderText;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() : string
|
||||||
|
{
|
||||||
|
return $this->placeholderText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValidator(callable $validator) : Input
|
||||||
|
{
|
||||||
|
$this->validator = $validator;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ask() : InputResult
|
||||||
|
{
|
||||||
|
return $this->inputIO->collect($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(string $input) : bool
|
||||||
|
{
|
||||||
|
if ($this->validator) {
|
||||||
|
$validator = $this->validator;
|
||||||
|
|
||||||
|
if ($validator instanceof \Closure) {
|
||||||
|
$validator = $validator->bindTo($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $validator($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !empty($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter(string $value) : string
|
||||||
|
{
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyle() : MenuStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
}
|
20
src/ncc/ThirdParty/php-school/cli-menu/LICENSE
vendored
Normal file
20
src/ncc/ThirdParty/php-school/cli-menu/LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2016 PHP School
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
187
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/AsciiArtItem.php
vendored
Normal file
187
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/AsciiArtItem.php
vendored
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use Assert\Assertion;
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\DefaultStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class AsciiArtItem implements MenuItemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Possible positions of the ascii art
|
||||||
|
*/
|
||||||
|
const POSITION_CENTER = 'center';
|
||||||
|
const POSITION_LEFT = 'left';
|
||||||
|
const POSITION_RIGHT = 'right';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $position;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $alternateText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $artLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DefaultStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
public function __construct(string $text, string $position = self::POSITION_CENTER, string $alt = '')
|
||||||
|
{
|
||||||
|
Assertion::inArray($position, [self::POSITION_CENTER, self::POSITION_RIGHT, self::POSITION_LEFT]);
|
||||||
|
|
||||||
|
$this->setText($text);
|
||||||
|
$this->position = $position;
|
||||||
|
$this->alternateText = $alt;
|
||||||
|
|
||||||
|
$this->style = new DefaultStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output text for the item
|
||||||
|
*/
|
||||||
|
public function getRows(MenuStyle $style, bool $selected = false) : array
|
||||||
|
{
|
||||||
|
if ($this->artLength > $style->getContentWidth()) {
|
||||||
|
$alternate = new StaticItem($this->alternateText);
|
||||||
|
return $alternate->getRows($style, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$padding = $style->getContentWidth() - $this->artLength;
|
||||||
|
|
||||||
|
return array_map(function ($row) use ($padding) {
|
||||||
|
switch ($this->position) {
|
||||||
|
case self::POSITION_LEFT:
|
||||||
|
break;
|
||||||
|
case self::POSITION_RIGHT:
|
||||||
|
$row = sprintf('%s%s', str_repeat(' ', $padding), $row);
|
||||||
|
break;
|
||||||
|
case self::POSITION_CENTER:
|
||||||
|
default:
|
||||||
|
$left = (int) ceil($padding / 2);
|
||||||
|
$row = sprintf('%s%s', str_repeat(' ', $left), $row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $row;
|
||||||
|
}, explode("\n", $this->text));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the item be selected
|
||||||
|
*/
|
||||||
|
public function canSelect() : bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the items callable if required
|
||||||
|
*/
|
||||||
|
public function getSelectAction() : ?callable
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the raw string of text
|
||||||
|
*/
|
||||||
|
public function getText() : string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the raw string of text
|
||||||
|
*/
|
||||||
|
public function setText(string $text) : void
|
||||||
|
{
|
||||||
|
$this->text = implode("\n", array_map(function (string $line) {
|
||||||
|
return rtrim($line, ' ');
|
||||||
|
}, explode("\n", $text)));
|
||||||
|
|
||||||
|
$this->calculateArtLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the length of the art
|
||||||
|
*/
|
||||||
|
private function calculateArtLength() : void
|
||||||
|
{
|
||||||
|
$this->artLength = (int) max(array_map('mb_strlen', explode("\n", $this->text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the length of the art
|
||||||
|
*/
|
||||||
|
public function getArtLength() : int
|
||||||
|
{
|
||||||
|
return $this->artLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPosition() : string
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAlternateText() : string
|
||||||
|
{
|
||||||
|
return $this->alternateText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the menu item is showing the menustyle extra value
|
||||||
|
*/
|
||||||
|
public function showsItemExtra() : bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable showing item extra
|
||||||
|
*/
|
||||||
|
public function showItemExtra() : void
|
||||||
|
{
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable showing item extra
|
||||||
|
*/
|
||||||
|
public function hideItemExtra() : void
|
||||||
|
{
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DefaultStyle
|
||||||
|
*/
|
||||||
|
public function getStyle() : ItemStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(DefaultStyle $style) : void
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
}
|
172
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/CheckboxItem.php
vendored
Normal file
172
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/CheckboxItem.php
vendored
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\CheckboxStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
|
||||||
|
class CheckboxItem implements MenuItemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
private $selectAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showItemExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $disabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current checkbox state
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $checked = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CheckboxStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $text,
|
||||||
|
callable $selectAction,
|
||||||
|
bool $showItemExtra = false,
|
||||||
|
bool $disabled = false
|
||||||
|
) {
|
||||||
|
$this->text = $text;
|
||||||
|
$this->selectAction = $selectAction;
|
||||||
|
$this->showItemExtra = $showItemExtra;
|
||||||
|
$this->disabled = $disabled;
|
||||||
|
|
||||||
|
$this->style = new CheckboxStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output text for the item
|
||||||
|
*/
|
||||||
|
public function getRows(MenuStyle $style, bool $selected = false) : array
|
||||||
|
{
|
||||||
|
return (new SelectableItemRenderer())->render($style, $this, $selected, $this->disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the raw string of text
|
||||||
|
*/
|
||||||
|
public function getText() : string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the raw string of text
|
||||||
|
*/
|
||||||
|
public function setText(string $text) : void
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the items callable if required
|
||||||
|
*/
|
||||||
|
public function getSelectAction() : ?callable
|
||||||
|
{
|
||||||
|
return function (CliMenu $cliMenu) {
|
||||||
|
$this->toggle();
|
||||||
|
$cliMenu->redraw();
|
||||||
|
|
||||||
|
return ($this->selectAction)($cliMenu);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the item be selected
|
||||||
|
*/
|
||||||
|
public function canSelect() : bool
|
||||||
|
{
|
||||||
|
return !$this->disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not we are showing item extra
|
||||||
|
*/
|
||||||
|
public function showsItemExtra() : bool
|
||||||
|
{
|
||||||
|
return $this->showItemExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable showing item extra
|
||||||
|
*/
|
||||||
|
public function showItemExtra() : void
|
||||||
|
{
|
||||||
|
$this->showItemExtra = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable showing item extra
|
||||||
|
*/
|
||||||
|
public function hideItemExtra() : void
|
||||||
|
{
|
||||||
|
$this->showItemExtra = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the item is checked
|
||||||
|
*/
|
||||||
|
public function getChecked() : bool
|
||||||
|
{
|
||||||
|
return $this->checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets checked state to true
|
||||||
|
*/
|
||||||
|
public function setChecked() : void
|
||||||
|
{
|
||||||
|
$this->checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets checked state to false
|
||||||
|
*/
|
||||||
|
public function setUnchecked() : void
|
||||||
|
{
|
||||||
|
$this->checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles checked state
|
||||||
|
*/
|
||||||
|
public function toggle() : void
|
||||||
|
{
|
||||||
|
$this->checked = !$this->checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CheckboxStyle
|
||||||
|
*/
|
||||||
|
public function getStyle() : ItemStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(CheckboxStyle $style) : void
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
}
|
126
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/LineBreakItem.php
vendored
Normal file
126
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/LineBreakItem.php
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use Assert\Assertion;
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\DefaultStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class LineBreakItem implements MenuItemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $breakChar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $lines;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DefaultStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
public function __construct(string $breakChar = ' ', int $lines = 1)
|
||||||
|
{
|
||||||
|
$this->breakChar = $breakChar;
|
||||||
|
$this->lines = $lines;
|
||||||
|
|
||||||
|
$this->style = new DefaultStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output text for the item
|
||||||
|
*/
|
||||||
|
public function getRows(MenuStyle $style, bool $selected = false) : array
|
||||||
|
{
|
||||||
|
return explode(
|
||||||
|
"\n",
|
||||||
|
rtrim(str_repeat(sprintf(
|
||||||
|
"%s\n",
|
||||||
|
mb_substr(str_repeat($this->breakChar, $style->getContentWidth()), 0, $style->getContentWidth())
|
||||||
|
), $this->lines))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the item be selected
|
||||||
|
*/
|
||||||
|
public function canSelect() : bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the items callable if required
|
||||||
|
*/
|
||||||
|
public function getSelectAction() : ?callable
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the raw string of text
|
||||||
|
*/
|
||||||
|
public function getText() : string
|
||||||
|
{
|
||||||
|
return $this->breakChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the raw string of text
|
||||||
|
*/
|
||||||
|
public function setText(string $text) : void
|
||||||
|
{
|
||||||
|
$this->breakChar = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the menu item is showing the menustyle extra value
|
||||||
|
*/
|
||||||
|
public function showsItemExtra() : bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLines() : int
|
||||||
|
{
|
||||||
|
return $this->lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable showing item extra
|
||||||
|
*/
|
||||||
|
public function showItemExtra() : void
|
||||||
|
{
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable showing item extra
|
||||||
|
*/
|
||||||
|
public function hideItemExtra() : void
|
||||||
|
{
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DefaultStyle
|
||||||
|
*/
|
||||||
|
public function getStyle() : ItemStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(DefaultStyle $style) : void
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
}
|
55
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/MenuItemInterface.php
vendored
Normal file
55
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/MenuItemInterface.php
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
interface MenuItemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The output text for the item
|
||||||
|
*/
|
||||||
|
public function getRows(MenuStyle $style, bool $selected = false) : array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the raw string of text
|
||||||
|
*/
|
||||||
|
public function getText() : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the item be selected
|
||||||
|
*/
|
||||||
|
public function canSelect() : bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the items callable if required
|
||||||
|
*/
|
||||||
|
public function getSelectAction() : ?callable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the menu item is showing the menustyle extra value
|
||||||
|
*/
|
||||||
|
public function showsItemExtra() : bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable showing item extra
|
||||||
|
*/
|
||||||
|
public function showItemExtra() : void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable showing item extra
|
||||||
|
*/
|
||||||
|
public function hideItemExtra() : void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the items style object. Can and
|
||||||
|
* should be subclassed to provide bespoke
|
||||||
|
* behaviour.
|
||||||
|
*/
|
||||||
|
public function getStyle() : ItemStyle;
|
||||||
|
}
|
157
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/MenuMenuItem.php
vendored
Normal file
157
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/MenuMenuItem.php
vendored
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\SelectableStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class MenuMenuItem implements MenuItemInterface, PropagatesStyles
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CliMenu
|
||||||
|
*/
|
||||||
|
private $subMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showItemExtra = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $disabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SelectableStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $text,
|
||||||
|
CliMenu $subMenu,
|
||||||
|
bool $disabled = false
|
||||||
|
) {
|
||||||
|
$this->text = $text;
|
||||||
|
$this->subMenu = $subMenu;
|
||||||
|
$this->disabled = $disabled;
|
||||||
|
|
||||||
|
$this->style = new SelectableStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output text for the item
|
||||||
|
*/
|
||||||
|
public function getRows(MenuStyle $style, bool $selected = false) : array
|
||||||
|
{
|
||||||
|
return (new SelectableItemRenderer())->render($style, $this, $selected, $this->disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the raw string of text
|
||||||
|
*/
|
||||||
|
public function getText() : string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the raw string of text
|
||||||
|
*/
|
||||||
|
public function setText(string $text) : void
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the items callable if required
|
||||||
|
*/
|
||||||
|
public function getSelectAction() : ?callable
|
||||||
|
{
|
||||||
|
return function (CliMenu $menu) {
|
||||||
|
$this->showSubMenu($menu);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sub menu
|
||||||
|
*/
|
||||||
|
public function getSubMenu() : CliMenu
|
||||||
|
{
|
||||||
|
return $this->subMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the sub menu
|
||||||
|
*/
|
||||||
|
public function showSubMenu(CliMenu $parentMenu) : void
|
||||||
|
{
|
||||||
|
$parentMenu->closeThis();
|
||||||
|
$this->subMenu->open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the item be selected
|
||||||
|
*/
|
||||||
|
public function canSelect() : bool
|
||||||
|
{
|
||||||
|
return !$this->disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable showing item extra
|
||||||
|
*/
|
||||||
|
public function showItemExtra() : void
|
||||||
|
{
|
||||||
|
$this->showItemExtra = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not we are showing item extra
|
||||||
|
*/
|
||||||
|
public function showsItemExtra() : bool
|
||||||
|
{
|
||||||
|
return $this->showItemExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable showing item extra
|
||||||
|
*/
|
||||||
|
public function hideItemExtra() : void
|
||||||
|
{
|
||||||
|
$this->showItemExtra = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SelectableStyle
|
||||||
|
*/
|
||||||
|
public function getStyle() : ItemStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(SelectableStyle $style) : void
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function propagateStyles(CliMenu $parent): void
|
||||||
|
{
|
||||||
|
$this->getSubMenu()->importStyles($parent);
|
||||||
|
$this->getSubMenu()->propagateStyles();
|
||||||
|
}
|
||||||
|
}
|
16
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/PropagatesStyles.php
vendored
Normal file
16
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/PropagatesStyles.php
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
|
||||||
|
interface PropagatesStyles
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Push the parents styles to any
|
||||||
|
* child items or menus.
|
||||||
|
*/
|
||||||
|
public function propagateStyles(CliMenu $parent) : void;
|
||||||
|
}
|
192
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/RadioItem.php
vendored
Normal file
192
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/RadioItem.php
vendored
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\RadioStyle;
|
||||||
|
|
||||||
|
class RadioItem implements MenuItemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
private $selectAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showItemExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $disabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current checkbox state
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $checked = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RadioStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $text,
|
||||||
|
callable $selectAction,
|
||||||
|
bool $showItemExtra = false,
|
||||||
|
bool $disabled = false
|
||||||
|
) {
|
||||||
|
$this->text = $text;
|
||||||
|
$this->selectAction = $selectAction;
|
||||||
|
$this->showItemExtra = $showItemExtra;
|
||||||
|
$this->disabled = $disabled;
|
||||||
|
|
||||||
|
$this->style = new RadioStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output text for the item
|
||||||
|
*/
|
||||||
|
public function getRows(MenuStyle $style, bool $selected = false) : array
|
||||||
|
{
|
||||||
|
return (new SelectableItemRenderer())->render($style, $this, $selected, $this->disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the raw string of text
|
||||||
|
*/
|
||||||
|
public function getText() : string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the raw string of text
|
||||||
|
*/
|
||||||
|
public function setText(string $text) : void
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the items callable if required
|
||||||
|
*/
|
||||||
|
public function getSelectAction() : ?callable
|
||||||
|
{
|
||||||
|
return function (CliMenu $cliMenu) {
|
||||||
|
$parentItem = $cliMenu->getItemByIndex($cliMenu->getSelectedItemIndex());
|
||||||
|
|
||||||
|
$siblings = $parentItem instanceof SplitItem
|
||||||
|
? $parentItem->getItems()
|
||||||
|
: $cliMenu->getItems();
|
||||||
|
|
||||||
|
$filtered = array_filter(
|
||||||
|
$siblings,
|
||||||
|
function (MenuItemInterface $item) {
|
||||||
|
return $item instanceof self;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
array_walk(
|
||||||
|
$filtered,
|
||||||
|
function (RadioItem $item) {
|
||||||
|
$item->setUnchecked();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->setChecked();
|
||||||
|
$cliMenu->redraw();
|
||||||
|
|
||||||
|
return ($this->selectAction)($cliMenu);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the item be selected
|
||||||
|
*/
|
||||||
|
public function canSelect() : bool
|
||||||
|
{
|
||||||
|
return !$this->disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not we are showing item extra
|
||||||
|
*/
|
||||||
|
public function showsItemExtra() : bool
|
||||||
|
{
|
||||||
|
return $this->showItemExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable showing item extra
|
||||||
|
*/
|
||||||
|
public function showItemExtra() : void
|
||||||
|
{
|
||||||
|
$this->showItemExtra = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable showing item extra
|
||||||
|
*/
|
||||||
|
public function hideItemExtra() : void
|
||||||
|
{
|
||||||
|
$this->showItemExtra = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the item is checked
|
||||||
|
*/
|
||||||
|
public function getChecked() : bool
|
||||||
|
{
|
||||||
|
return $this->checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets checked state to true
|
||||||
|
*/
|
||||||
|
public function setChecked() : void
|
||||||
|
{
|
||||||
|
$this->checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets checked state to false
|
||||||
|
*/
|
||||||
|
public function setUnchecked() : void
|
||||||
|
{
|
||||||
|
$this->checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles checked state
|
||||||
|
*/
|
||||||
|
public function toggle() : void
|
||||||
|
{
|
||||||
|
$this->checked = !$this->checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return RadioStyle
|
||||||
|
*/
|
||||||
|
public function getStyle() : ItemStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(RadioStyle $style) : void
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
}
|
132
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/SelectableItem.php
vendored
Normal file
132
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/SelectableItem.php
vendored
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
use PhpSchool\CliMenu\Util\StringUtil;
|
||||||
|
use PhpSchool\CliMenu\Style\SelectableStyle;
|
||||||
|
use function PhpSchool\CliMenu\Util\mapWithKeys;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class SelectableItem implements MenuItemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
private $selectAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showItemExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $disabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SelectableStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $text,
|
||||||
|
callable $selectAction,
|
||||||
|
bool $showItemExtra = false,
|
||||||
|
bool $disabled = false
|
||||||
|
) {
|
||||||
|
$this->text = $text;
|
||||||
|
$this->selectAction = $selectAction;
|
||||||
|
$this->showItemExtra = $showItemExtra;
|
||||||
|
$this->disabled = $disabled;
|
||||||
|
|
||||||
|
$this->style = new SelectableStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output text for the item
|
||||||
|
*/
|
||||||
|
public function getRows(MenuStyle $style, bool $selected = false) : array
|
||||||
|
{
|
||||||
|
return (new SelectableItemRenderer())->render($style, $this, $selected, $this->disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the raw string of text
|
||||||
|
*/
|
||||||
|
public function getText() : string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the raw string of text
|
||||||
|
*/
|
||||||
|
public function setText(string $text) : void
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the items callable if required
|
||||||
|
*/
|
||||||
|
public function getSelectAction() : ?callable
|
||||||
|
{
|
||||||
|
return $this->selectAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the item be selected
|
||||||
|
*/
|
||||||
|
public function canSelect() : bool
|
||||||
|
{
|
||||||
|
return !$this->disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not we are showing item extra
|
||||||
|
*/
|
||||||
|
public function showsItemExtra() : bool
|
||||||
|
{
|
||||||
|
return $this->showItemExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable showing item extra
|
||||||
|
*/
|
||||||
|
public function showItemExtra() : void
|
||||||
|
{
|
||||||
|
$this->showItemExtra = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable showing item extra
|
||||||
|
*/
|
||||||
|
public function hideItemExtra() : void
|
||||||
|
{
|
||||||
|
$this->showItemExtra = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SelectableStyle
|
||||||
|
*/
|
||||||
|
public function getStyle() : ItemStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(SelectableStyle $style) : void
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
}
|
66
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/SelectableItemRenderer.php
vendored
Normal file
66
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/SelectableItemRenderer.php
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\Selectable;
|
||||||
|
use PhpSchool\CliMenu\Util\StringUtil as s;
|
||||||
|
use function PhpSchool\CliMenu\Util\mapWithKeys;
|
||||||
|
|
||||||
|
class SelectableItemRenderer
|
||||||
|
{
|
||||||
|
public function render(MenuStyle $menuStyle, MenuItemInterface $item, bool $selected, bool $disabled) : array
|
||||||
|
{
|
||||||
|
$itemStyle = $item->getStyle();
|
||||||
|
$marker = $itemStyle->getMarker($item, $selected);
|
||||||
|
$availableTextWidth = $this->getAvailableTextWidth($menuStyle, $itemStyle);
|
||||||
|
|
||||||
|
return mapWithKeys(
|
||||||
|
$this->wrapAndIndentText($marker, $item->getText(), $availableTextWidth),
|
||||||
|
function (int $key, string $row) use ($menuStyle, $item, $availableTextWidth, $disabled) {
|
||||||
|
$text = $disabled ? $menuStyle->getDisabledItemText($row) : $row;
|
||||||
|
|
||||||
|
return $key === 0 && $item->showsItemExtra()
|
||||||
|
? $this->lineWithExtra($text, $availableTextWidth, $item->getStyle())
|
||||||
|
: $text;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function wrapAndIndentText(string $marker, string $text, int $availableWidth) : array
|
||||||
|
{
|
||||||
|
return explode(
|
||||||
|
"\n",
|
||||||
|
s::wordwrap(
|
||||||
|
"{$marker}{$text}",
|
||||||
|
$availableWidth,
|
||||||
|
sprintf("\n%s", $this->emptyString(mb_strlen($marker)))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lineWithExtra(string $text, int $availableWidth, ItemStyle $itemStyle) : string
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'%s%s %s',
|
||||||
|
$text,
|
||||||
|
$this->emptyString($availableWidth - s::length($text)),
|
||||||
|
$itemStyle->getItemExtra()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function emptyString(int $numCharacters) : string
|
||||||
|
{
|
||||||
|
return str_repeat(' ', $numCharacters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAvailableTextWidth(MenuStyle $menuStyle, ItemStyle $itemStyle) : int
|
||||||
|
{
|
||||||
|
return $itemStyle->getDisplaysExtra()
|
||||||
|
? $menuStyle->getContentWidth() - (mb_strlen($itemStyle->getItemExtra()) + 2)
|
||||||
|
: $menuStyle->getContentWidth();
|
||||||
|
}
|
||||||
|
}
|
388
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/SplitItem.php
vendored
Normal file
388
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/SplitItem.php
vendored
Normal file
|
@ -0,0 +1,388 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use Assert\Assertion;
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\DefaultStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\Selectable;
|
||||||
|
use PhpSchool\CliMenu\Util\StringUtil;
|
||||||
|
use function PhpSchool\CliMenu\Util\collect;
|
||||||
|
use function PhpSchool\CliMenu\Util\each;
|
||||||
|
use function PhpSchool\CliMenu\Util\mapWithKeys;
|
||||||
|
use function PhpSchool\CliMenu\Util\max;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class SplitItem implements MenuItemInterface, PropagatesStyles
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $items = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int|null
|
||||||
|
*/
|
||||||
|
private $selectedItemIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $canBeSelected = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $gutter = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DefaultStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $blacklistedItems = [
|
||||||
|
\PhpSchool\CliMenu\MenuItem\AsciiArtItem::class,
|
||||||
|
\PhpSchool\CliMenu\MenuItem\LineBreakItem::class,
|
||||||
|
\PhpSchool\CliMenu\MenuItem\SplitItem::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(array $items = [])
|
||||||
|
{
|
||||||
|
$this->addItems($items);
|
||||||
|
$this->setDefaultSelectedItem();
|
||||||
|
|
||||||
|
$this->style = new DefaultStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGutter() : int
|
||||||
|
{
|
||||||
|
return $this->gutter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGutter(int $gutter) : void
|
||||||
|
{
|
||||||
|
Assertion::greaterOrEqualThan($gutter, 0);
|
||||||
|
$this->gutter = $gutter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addItem(MenuItemInterface $item) : self
|
||||||
|
{
|
||||||
|
foreach (self::$blacklistedItems as $bl) {
|
||||||
|
if ($item instanceof $bl) {
|
||||||
|
throw new \InvalidArgumentException("Cannot add a $bl to a SplitItem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->items[] = $item;
|
||||||
|
$this->setDefaultSelectedItem();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addItems(array $items) : self
|
||||||
|
{
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$this->addItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setItems(array $items) : self
|
||||||
|
{
|
||||||
|
$this->items = [];
|
||||||
|
$this->addItems($items);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select default item
|
||||||
|
*/
|
||||||
|
private function setDefaultSelectedItem() : void
|
||||||
|
{
|
||||||
|
foreach ($this->items as $index => $item) {
|
||||||
|
if ($item->canSelect()) {
|
||||||
|
$this->canBeSelected = true;
|
||||||
|
$this->selectedItemIndex = $index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->canBeSelected = false;
|
||||||
|
$this->selectedItemIndex = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output text for the item
|
||||||
|
*/
|
||||||
|
public function getRows(MenuStyle $style, bool $selected = false) : array
|
||||||
|
{
|
||||||
|
$numberOfItems = count($this->items);
|
||||||
|
|
||||||
|
if ($numberOfItems === 0) {
|
||||||
|
throw new \RuntimeException(sprintf('There should be at least one item added to: %s', __CLASS__));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$selected) {
|
||||||
|
$this->setDefaultSelectedItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
$largestItemExtra = $this->calculateItemExtra();
|
||||||
|
|
||||||
|
$length = $largestItemExtra > 0
|
||||||
|
? floor($style->getContentWidth() / $numberOfItems) - ($largestItemExtra + 2)
|
||||||
|
: floor($style->getContentWidth() / $numberOfItems);
|
||||||
|
|
||||||
|
$length -= $this->gutter;
|
||||||
|
$length = (int) $length;
|
||||||
|
|
||||||
|
$missingLength = $style->getContentWidth() % $numberOfItems;
|
||||||
|
|
||||||
|
return $this->buildRows(
|
||||||
|
mapWithKeys($this->items, function (int $index, MenuItemInterface $item) use ($selected, $length, $style) {
|
||||||
|
$isSelected = $selected && $index === $this->selectedItemIndex;
|
||||||
|
|
||||||
|
$marker = $item->getStyle()->getMarker($item, $isSelected);
|
||||||
|
|
||||||
|
$itemExtra = '';
|
||||||
|
if ($item->getStyle()->getDisplaysExtra()) {
|
||||||
|
$itemExtraVal = $item->getStyle()->getItemExtra();
|
||||||
|
$itemExtra = $item->showsItemExtra()
|
||||||
|
? sprintf(' %s', $itemExtraVal)
|
||||||
|
: sprintf(' %s', str_repeat(' ', mb_strlen($itemExtraVal)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->buildCell(
|
||||||
|
explode(
|
||||||
|
"\n",
|
||||||
|
StringUtil::wordwrap(
|
||||||
|
sprintf('%s%s', $marker, $item->getText()),
|
||||||
|
$length,
|
||||||
|
sprintf("\n%s", str_repeat(' ', mb_strlen($marker)))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$length,
|
||||||
|
$style,
|
||||||
|
$isSelected,
|
||||||
|
$itemExtra
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
$missingLength,
|
||||||
|
$length,
|
||||||
|
$largestItemExtra
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildRows(array $cells, int $missingLength, int $length, int $largestItemExtra) : array
|
||||||
|
{
|
||||||
|
$extraPadLength = $largestItemExtra > 0 ? 2 + $largestItemExtra : 0;
|
||||||
|
|
||||||
|
return array_map(
|
||||||
|
function ($i) use ($cells, $length, $missingLength, $extraPadLength) {
|
||||||
|
return $this->buildRow($cells, $i, $length, $missingLength, $extraPadLength);
|
||||||
|
},
|
||||||
|
range(0, max(array_map('count', $cells)) - 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildRow(array $cells, int $index, int $length, int $missingLength, int $extraPadLength) : string
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'%s%s',
|
||||||
|
implode(
|
||||||
|
'',
|
||||||
|
array_map(
|
||||||
|
function ($cell) use ($index, $length, $extraPadLength) {
|
||||||
|
return $cell[$index] ?? str_repeat(' ', $length + $this->gutter + $extraPadLength);
|
||||||
|
},
|
||||||
|
$cells
|
||||||
|
)
|
||||||
|
),
|
||||||
|
str_repeat(' ', $missingLength)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildCell(
|
||||||
|
array $content,
|
||||||
|
int $length,
|
||||||
|
MenuStyle $style,
|
||||||
|
bool $isSelected,
|
||||||
|
string $itemExtra
|
||||||
|
) : array {
|
||||||
|
return array_map(function ($row, $index) use ($length, $style, $isSelected, $itemExtra) {
|
||||||
|
$invertedColoursSetCode = $isSelected
|
||||||
|
? $style->getInvertedColoursSetCode()
|
||||||
|
: '';
|
||||||
|
$invertedColoursUnsetCode = $isSelected
|
||||||
|
? $style->getInvertedColoursUnsetCode()
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'%s%s%s%s%s%s',
|
||||||
|
$invertedColoursSetCode,
|
||||||
|
$row,
|
||||||
|
str_repeat(' ', $length - mb_strlen($row)),
|
||||||
|
$index === 0 ? $itemExtra : str_repeat(' ', mb_strlen($itemExtra)),
|
||||||
|
$invertedColoursUnsetCode,
|
||||||
|
str_repeat(' ', $this->gutter)
|
||||||
|
);
|
||||||
|
}, $content, array_keys($content));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is there an item with this index and can it be
|
||||||
|
* selected?
|
||||||
|
*/
|
||||||
|
public function canSelectIndex(int $index) : bool
|
||||||
|
{
|
||||||
|
return isset($this->items[$index]) && $this->items[$index]->canSelect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the item index which should be selected. If the item does
|
||||||
|
* not exist then throw an exception.
|
||||||
|
*/
|
||||||
|
public function setSelectedItemIndex(int $index) : void
|
||||||
|
{
|
||||||
|
if (!isset($this->items[$index])) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Index: "%s" does not exist', $index));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->selectedItemIndex = $index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently select item index.
|
||||||
|
* May be null in case of no selectable item.
|
||||||
|
*/
|
||||||
|
public function getSelectedItemIndex() : ?int
|
||||||
|
{
|
||||||
|
return $this->selectedItemIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently selected item - if no items are selectable
|
||||||
|
* then throw an exception.
|
||||||
|
*/
|
||||||
|
public function getSelectedItem() : MenuItemInterface
|
||||||
|
{
|
||||||
|
if (null === $this->selectedItemIndex) {
|
||||||
|
throw new \RuntimeException('No item is selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->items[$this->selectedItemIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItems() : array
|
||||||
|
{
|
||||||
|
return $this->items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the item be selected
|
||||||
|
* In this case, it indicates if at least 1 item inside the SplitItem can be selected
|
||||||
|
*/
|
||||||
|
public function canSelect() : bool
|
||||||
|
{
|
||||||
|
return $this->canBeSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the items callable if required
|
||||||
|
*/
|
||||||
|
public function getSelectAction() : ?callable
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the menu item is showing the menustyle extra value
|
||||||
|
*/
|
||||||
|
public function showsItemExtra() : bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable showing item extra
|
||||||
|
*/
|
||||||
|
public function showItemExtra() : void
|
||||||
|
{
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable showing item extra
|
||||||
|
*/
|
||||||
|
public function hideItemExtra() : void
|
||||||
|
{
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nothing to return with SplitItem
|
||||||
|
*/
|
||||||
|
public function getText() : string
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException(sprintf('Not supported on: %s', __CLASS__));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds largest itemExtra value in items
|
||||||
|
*/
|
||||||
|
private function calculateItemExtra() : int
|
||||||
|
{
|
||||||
|
return max(array_map(
|
||||||
|
function (MenuItemInterface $item) {
|
||||||
|
return mb_strlen($item->getStyle()->getItemExtra());
|
||||||
|
},
|
||||||
|
array_filter($this->items, function (MenuItemInterface $item) {
|
||||||
|
return $item->getStyle()->getDisplaysExtra();
|
||||||
|
})
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DefaultStyle
|
||||||
|
*/
|
||||||
|
public function getStyle(): ItemStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(DefaultStyle $style): void
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function propagateStyles(CliMenu $parent): void
|
||||||
|
{
|
||||||
|
collect($this->items)
|
||||||
|
->filter(function (int $k, MenuItemInterface $item) use ($parent) {
|
||||||
|
return $parent->getStyleLocator()->hasStyleForMenuItem($item);
|
||||||
|
})
|
||||||
|
->filter(function (int $k, MenuItemInterface $item) {
|
||||||
|
return !$item->getStyle()->hasChangedFromDefaults();
|
||||||
|
})
|
||||||
|
->each(function (int $k, $item) use ($parent) {
|
||||||
|
$item->setStyle(clone $parent->getItemStyleForItem($item));
|
||||||
|
});
|
||||||
|
|
||||||
|
collect($this->items)
|
||||||
|
->filter(function (int $k, MenuItemInterface $item) {
|
||||||
|
return $item instanceof PropagatesStyles;
|
||||||
|
})
|
||||||
|
->each(function (int $k, $item) use ($parent) {
|
||||||
|
$item->propagateStyles($parent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
109
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/StaticItem.php
vendored
Normal file
109
src/ncc/ThirdParty/php-school/cli-menu/MenuItem/StaticItem.php
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\MenuItem;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\DefaultStyle;
|
||||||
|
use PhpSchool\CliMenu\Style\ItemStyle;
|
||||||
|
use PhpSchool\CliMenu\Util\StringUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class StaticItem implements MenuItemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DefaultStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
public function __construct(string $text)
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
|
||||||
|
$this->style = new DefaultStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output text for the item
|
||||||
|
*/
|
||||||
|
public function getRows(MenuStyle $style, bool $selected = false) : array
|
||||||
|
{
|
||||||
|
return explode("\n", StringUtil::wordwrap($this->text, $style->getContentWidth()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the raw string of text
|
||||||
|
*/
|
||||||
|
public function getText() : string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the raw string of text
|
||||||
|
*/
|
||||||
|
public function setText(string $text) : void
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the items callable if required
|
||||||
|
*/
|
||||||
|
public function getSelectAction() : ?callable
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the item be selected
|
||||||
|
*/
|
||||||
|
public function canSelect() : bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not we are showing item extra
|
||||||
|
*/
|
||||||
|
public function showsItemExtra() : bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable showing item extra
|
||||||
|
*/
|
||||||
|
public function showItemExtra() : void
|
||||||
|
{
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable showing item extra
|
||||||
|
*/
|
||||||
|
public function hideItemExtra() : void
|
||||||
|
{
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DefaultStyle
|
||||||
|
*/
|
||||||
|
public function getStyle() : ItemStyle
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(DefaultStyle $style) : void
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
}
|
852
src/ncc/ThirdParty/php-school/cli-menu/MenuStyle.php
vendored
Normal file
852
src/ncc/ThirdParty/php-school/cli-menu/MenuStyle.php
vendored
Normal file
|
@ -0,0 +1,852 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\Exception\CannotShrinkMenuException;
|
||||||
|
use PhpSchool\CliMenu\Terminal\TerminalFactory;
|
||||||
|
use PhpSchool\CliMenu\Util\ColourUtil;
|
||||||
|
use PhpSchool\CliMenu\Util\StringUtil as s;
|
||||||
|
use PhpSchool\Terminal\Terminal;
|
||||||
|
use Assert\Assertion;
|
||||||
|
|
||||||
|
//TODO: B/W fallback
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class MenuStyle
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Terminal
|
||||||
|
*/
|
||||||
|
protected $terminal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $fg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $bg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The width of the menu. Including borders and padding.
|
||||||
|
* Does not include margin.
|
||||||
|
*
|
||||||
|
* May not be the value that was requested in the
|
||||||
|
* circumstance that the terminal is smaller then the
|
||||||
|
* requested width.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $width;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In case the requested width is wider than the terminal
|
||||||
|
* then we shrink the width to fit the terminal. We keep
|
||||||
|
* the requested size in case the margins are changed and
|
||||||
|
* we need to recalculate the width.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $requestedWidth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $margin = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $paddingTopBottom = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $paddingLeftRight = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $paddingTopBottomRows = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $contentWidth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $itemExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $displaysExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $titleSeparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $coloursSetCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $invertedColoursSetCode = "\033[7m";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $invertedColoursUnsetCode = "\033[27m";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $coloursResetCode = "\033[0m";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $borderTopWidth = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $borderRightWidth = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $borderBottomWidth = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $borderLeftWidth = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $borderColour = 'white';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $borderTopRows = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $borderBottomRows = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $marginAuto = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $debugMode = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Values
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $defaultStyleValues = [
|
||||||
|
'fg' => 'white',
|
||||||
|
'bg' => 'blue',
|
||||||
|
'width' => 100,
|
||||||
|
'paddingTopBottom' => 1,
|
||||||
|
'paddingLeftRight' => 2,
|
||||||
|
'margin' => 2,
|
||||||
|
'itemExtra' => '✔',
|
||||||
|
'displaysExtra' => false,
|
||||||
|
'titleSeparator' => '=',
|
||||||
|
'borderTopWidth' => 0,
|
||||||
|
'borderRightWidth' => 0,
|
||||||
|
'borderBottomWidth' => 0,
|
||||||
|
'borderLeftWidth' => 0,
|
||||||
|
'borderColour' => 'white',
|
||||||
|
'marginAuto' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $availableForegroundColors = [
|
||||||
|
'black' => 30,
|
||||||
|
'red' => 31,
|
||||||
|
'green' => 32,
|
||||||
|
'yellow' => 33,
|
||||||
|
'blue' => 34,
|
||||||
|
'magenta' => 35,
|
||||||
|
'cyan' => 36,
|
||||||
|
'white' => 37,
|
||||||
|
'default' => 39,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $availableBackgroundColors = [
|
||||||
|
'black' => 40,
|
||||||
|
'red' => 41,
|
||||||
|
'green' => 42,
|
||||||
|
'yellow' => 43,
|
||||||
|
'blue' => 44,
|
||||||
|
'magenta' => 45,
|
||||||
|
'cyan' => 46,
|
||||||
|
'white' => 47,
|
||||||
|
'default' => 49,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $availableOptions = [
|
||||||
|
'bold' => ['set' => 1, 'unset' => 22],
|
||||||
|
'dim' => ['set' => 2, 'unset' => 22],
|
||||||
|
'underscore' => ['set' => 4, 'unset' => 24],
|
||||||
|
'blink' => ['set' => 5, 'unset' => 25],
|
||||||
|
'reverse' => ['set' => 7, 'unset' => 27],
|
||||||
|
'conceal' => ['set' => 8, 'unset' => 28]
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise style
|
||||||
|
*/
|
||||||
|
public function __construct(Terminal $terminal = null)
|
||||||
|
{
|
||||||
|
$this->terminal = $terminal ?: TerminalFactory::fromSystem();
|
||||||
|
|
||||||
|
$this->fg = self::$defaultStyleValues['fg'];
|
||||||
|
$this->bg = self::$defaultStyleValues['bg'];
|
||||||
|
|
||||||
|
$this->generateColoursSetCode();
|
||||||
|
|
||||||
|
$this->setWidth(self::$defaultStyleValues['width']);
|
||||||
|
$this->setPaddingTopBottom(self::$defaultStyleValues['paddingTopBottom']);
|
||||||
|
$this->setPaddingLeftRight(self::$defaultStyleValues['paddingLeftRight']);
|
||||||
|
$this->setMargin(self::$defaultStyleValues['margin']);
|
||||||
|
$this->setItemExtra(self::$defaultStyleValues['itemExtra']);
|
||||||
|
$this->setDisplaysExtra(self::$defaultStyleValues['displaysExtra']);
|
||||||
|
$this->setTitleSeparator(self::$defaultStyleValues['titleSeparator']);
|
||||||
|
$this->setBorderTopWidth(self::$defaultStyleValues['borderTopWidth']);
|
||||||
|
$this->setBorderRightWidth(self::$defaultStyleValues['borderRightWidth']);
|
||||||
|
$this->setBorderBottomWidth(self::$defaultStyleValues['borderBottomWidth']);
|
||||||
|
$this->setBorderLeftWidth(self::$defaultStyleValues['borderLeftWidth']);
|
||||||
|
$this->setBorderColour(self::$defaultStyleValues['borderColour']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasChangedFromDefaults() : bool
|
||||||
|
{
|
||||||
|
$currentValues = [
|
||||||
|
$this->fg,
|
||||||
|
$this->bg,
|
||||||
|
$this->width,
|
||||||
|
$this->paddingTopBottom,
|
||||||
|
$this->paddingLeftRight,
|
||||||
|
$this->margin,
|
||||||
|
$this->itemExtra,
|
||||||
|
$this->displaysExtra,
|
||||||
|
$this->titleSeparator,
|
||||||
|
$this->borderTopWidth,
|
||||||
|
$this->borderRightWidth,
|
||||||
|
$this->borderBottomWidth,
|
||||||
|
$this->borderLeftWidth,
|
||||||
|
$this->borderColour,
|
||||||
|
$this->marginAuto,
|
||||||
|
];
|
||||||
|
|
||||||
|
$defaultStyleValues = self::$defaultStyleValues;
|
||||||
|
if ($this->width !== $this->requestedWidth) {
|
||||||
|
$defaultStyleValues['width'] = $this->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $currentValues !== array_values($defaultStyleValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get text for a disabled menu item.
|
||||||
|
*
|
||||||
|
* This sets the foreground colour to the ansi bright equivalent,
|
||||||
|
* and on supported terminals, adds additional dim formatting.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDisabledItemText(string $text) : string
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
"\033[%sm\033[%sm%s\033[%sm\033[%sm",
|
||||||
|
self::$availableOptions['dim']['set'],
|
||||||
|
$this->getForegroundColourCode(true),
|
||||||
|
$text,
|
||||||
|
$this->getForegroundColourCode(),
|
||||||
|
self::$availableOptions['dim']['unset']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ansi escape sequence for the foreground colour.
|
||||||
|
*
|
||||||
|
* @param bool $bright Whether to modify to the ansi bright variation
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getForegroundColourCode(bool $bright = false) : string
|
||||||
|
{
|
||||||
|
if (!ctype_digit($this->fg)) {
|
||||||
|
$fgCode = (int)self::$availableForegroundColors[$this->fg];
|
||||||
|
$fgCode += ($bright ? 60 : 0);
|
||||||
|
} else {
|
||||||
|
$fgCode = sprintf("38;5;%s", ((int)$this->fg + ($bright ? 60 : 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string)$fgCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ansi escape sequence for the background colour.
|
||||||
|
*
|
||||||
|
* @param bool $bright Whether to modify to the ansi bright variation
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getBackgroundColourCode(bool $bright = false) : string
|
||||||
|
{
|
||||||
|
if (!ctype_digit($this->bg)) {
|
||||||
|
$bgCode = (int)self::$availableBackgroundColors[$this->bg];
|
||||||
|
$bgCode += ($bright ? 60 : 0);
|
||||||
|
} else {
|
||||||
|
$bgCode = sprintf("48;5;%s", ((int)$this->bg + ($bright ? 60 : 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string)$bgCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the ansi escape sequence to set the colours
|
||||||
|
*/
|
||||||
|
private function generateColoursSetCode() : void
|
||||||
|
{
|
||||||
|
$this->coloursSetCode = sprintf(
|
||||||
|
"\033[%s;%sm",
|
||||||
|
$this->getForegroundColourCode(),
|
||||||
|
$this->getBackgroundColourCode()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the colour code for Bg and Fg
|
||||||
|
*/
|
||||||
|
public function getColoursSetCode() : string
|
||||||
|
{
|
||||||
|
return $this->coloursSetCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the inverted escape sequence (used for selected elements)
|
||||||
|
*/
|
||||||
|
public function getInvertedColoursSetCode() : string
|
||||||
|
{
|
||||||
|
return $this->invertedColoursSetCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the inverted escape sequence (used for selected elements)
|
||||||
|
*/
|
||||||
|
public function getInvertedColoursUnsetCode() : string
|
||||||
|
{
|
||||||
|
return $this->invertedColoursUnsetCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the escape sequence used to reset colours to default
|
||||||
|
*/
|
||||||
|
public function getColoursResetCode() : string
|
||||||
|
{
|
||||||
|
return $this->coloursResetCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the contents width
|
||||||
|
*
|
||||||
|
* The content width is menu width minus borders and padding.
|
||||||
|
*/
|
||||||
|
protected function calculateContentWidth() : void
|
||||||
|
{
|
||||||
|
$this->contentWidth = $this->width
|
||||||
|
- ($this->paddingLeftRight * 2)
|
||||||
|
- ($this->borderRightWidth + $this->borderLeftWidth);
|
||||||
|
|
||||||
|
if ($this->contentWidth < 0) {
|
||||||
|
$this->contentWidth = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFg() : string
|
||||||
|
{
|
||||||
|
return $this->fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFg(string $fg, string $fallback = null) : self
|
||||||
|
{
|
||||||
|
$this->fg = ColourUtil::validateColour(
|
||||||
|
$this->terminal,
|
||||||
|
$fg,
|
||||||
|
$fallback
|
||||||
|
);
|
||||||
|
$this->generateColoursSetCode();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBg() : string
|
||||||
|
{
|
||||||
|
return $this->bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBg(string $bg, string $fallback = null) : self
|
||||||
|
{
|
||||||
|
$this->bg = ColourUtil::validateColour(
|
||||||
|
$this->terminal,
|
||||||
|
$bg,
|
||||||
|
$fallback
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->generateColoursSetCode();
|
||||||
|
$this->generatePaddingTopBottomRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWidth() : int
|
||||||
|
{
|
||||||
|
return $this->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setWidth(int $width) : self
|
||||||
|
{
|
||||||
|
Assertion::greaterOrEqualThan($width, 0);
|
||||||
|
|
||||||
|
$this->requestedWidth = $width;
|
||||||
|
|
||||||
|
$this->width = $this->maybeShrinkWidth($this->marginAuto ? 0 : $this->margin, $width);
|
||||||
|
|
||||||
|
if ($this->marginAuto) {
|
||||||
|
$this->calculateMarginAuto($this->width);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->calculateContentWidth();
|
||||||
|
$this->generateBorderRows();
|
||||||
|
$this->generatePaddingTopBottomRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function maybeShrinkWidth(int $margin, int $width) : int
|
||||||
|
{
|
||||||
|
if ($width + ($margin * 2) >= $this->terminal->getWidth()) {
|
||||||
|
$width = $this->terminal->getWidth() - ($margin * 2);
|
||||||
|
|
||||||
|
if ($width <= 0) {
|
||||||
|
throw CannotShrinkMenuException::fromMarginAndTerminalWidth($margin, $this->terminal->getWidth());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPaddingTopBottom() : int
|
||||||
|
{
|
||||||
|
return $this->paddingTopBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPaddingLeftRight() : int
|
||||||
|
{
|
||||||
|
return $this->paddingLeftRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generatePaddingTopBottomRows() : void
|
||||||
|
{
|
||||||
|
if ($this->borderLeftWidth || $this->borderRightWidth) {
|
||||||
|
$borderColour = $this->getBorderColourCode();
|
||||||
|
} else {
|
||||||
|
$borderColour = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$paddingRow = sprintf(
|
||||||
|
"%s%s%s%s%s%s%s%s%s%s\n",
|
||||||
|
$this->debugMode ? $this->getDebugString($this->margin) : str_repeat(' ', $this->margin),
|
||||||
|
$borderColour,
|
||||||
|
str_repeat(' ', $this->borderLeftWidth),
|
||||||
|
$this->getColoursSetCode(),
|
||||||
|
str_repeat(' ', $this->paddingLeftRight),
|
||||||
|
str_repeat(' ', $this->contentWidth),
|
||||||
|
str_repeat(' ', $this->paddingLeftRight),
|
||||||
|
$borderColour,
|
||||||
|
str_repeat(' ', $this->borderRightWidth),
|
||||||
|
$this->coloursResetCode
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if ($this->debugMode && s::length($paddingRow) <= $this->terminal->getWidth()) {
|
||||||
|
$paddingRow = substr_replace(
|
||||||
|
$paddingRow,
|
||||||
|
sprintf("%s\n", $this->getDebugString($this->terminal->getWidth() - (s::length($paddingRow) - 1))),
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->paddingTopBottom = max($this->paddingTopBottom, 0);
|
||||||
|
$this->paddingTopBottomRows = array_fill(0, $this->paddingTopBottom, $paddingRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getPaddingTopBottomRows() : array
|
||||||
|
{
|
||||||
|
return $this->paddingTopBottomRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPadding(int $topBottom, int $leftRight = null) : self
|
||||||
|
{
|
||||||
|
if ($leftRight === null) {
|
||||||
|
$leftRight = $topBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setPaddingTopBottom($topBottom);
|
||||||
|
$this->setPaddingLeftRight($leftRight);
|
||||||
|
|
||||||
|
$this->calculateContentWidth();
|
||||||
|
$this->generatePaddingTopBottomRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPaddingTopBottom(int $topBottom) : self
|
||||||
|
{
|
||||||
|
Assertion::greaterOrEqualThan($topBottom, 0);
|
||||||
|
$this->paddingTopBottom = $topBottom;
|
||||||
|
|
||||||
|
$this->generatePaddingTopBottomRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPaddingLeftRight(int $leftRight) : self
|
||||||
|
{
|
||||||
|
Assertion::greaterOrEqualThan($leftRight, 0);
|
||||||
|
$this->paddingLeftRight = $leftRight;
|
||||||
|
|
||||||
|
$this->calculateContentWidth();
|
||||||
|
$this->generatePaddingTopBottomRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMargin() : int
|
||||||
|
{
|
||||||
|
return $this->margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMarginAuto() : self
|
||||||
|
{
|
||||||
|
$this->marginAuto = true;
|
||||||
|
$this->margin = 0;
|
||||||
|
|
||||||
|
$this->setWidth($this->requestedWidth);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateMarginAuto(int $width) : void
|
||||||
|
{
|
||||||
|
$this->margin = (int) floor(($this->terminal->getWidth() - ($width)) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMargin(int $margin) : self
|
||||||
|
{
|
||||||
|
Assertion::greaterOrEqualThan($margin, 0);
|
||||||
|
|
||||||
|
$this->marginAuto = false;
|
||||||
|
$this->margin = $margin;
|
||||||
|
|
||||||
|
//margin + width may now exceed terminal size
|
||||||
|
//so set width again to trigger width check + maybe resize
|
||||||
|
$this->setWidth($this->requestedWidth);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContentWidth() : int
|
||||||
|
{
|
||||||
|
return $this->contentWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get padding for right had side of content
|
||||||
|
*/
|
||||||
|
public function getRightHandPadding(int $contentLength) : int
|
||||||
|
{
|
||||||
|
$rightPadding = $this->getContentWidth() - $contentLength + $this->getPaddingLeftRight();
|
||||||
|
|
||||||
|
if ($rightPadding < 0) {
|
||||||
|
$rightPadding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rightPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setItemExtra(string $itemExtra) : self
|
||||||
|
{
|
||||||
|
$this->itemExtra = $itemExtra;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemExtra() : string
|
||||||
|
{
|
||||||
|
return $this->itemExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplaysExtra() : bool
|
||||||
|
{
|
||||||
|
return $this->displaysExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDisplaysExtra(bool $displaysExtra) : self
|
||||||
|
{
|
||||||
|
$this->displaysExtra = $displaysExtra;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitleSeparator() : string
|
||||||
|
{
|
||||||
|
return $this->titleSeparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTitleSeparator(string $actionSeparator) : self
|
||||||
|
{
|
||||||
|
$this->titleSeparator = $actionSeparator;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateBorderRows() : void
|
||||||
|
{
|
||||||
|
$borderRow = sprintf(
|
||||||
|
"%s%s%s%s\n",
|
||||||
|
$this->debugMode ? $this->getDebugString($this->margin) : str_repeat(' ', $this->margin),
|
||||||
|
$this->getBorderColourCode(),
|
||||||
|
str_repeat(' ', $this->width),
|
||||||
|
$this->getColoursResetCode()
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($this->debugMode && s::length($borderRow) <= $this->terminal->getWidth()) {
|
||||||
|
$borderRow = substr_replace(
|
||||||
|
$borderRow,
|
||||||
|
sprintf("%s\n", $this->getDebugString($this->terminal->getWidth() - (s::length($borderRow) - 1))),
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->borderTopWidth = max($this->borderTopWidth, 0);
|
||||||
|
$this->borderBottomWidth = max($this->borderBottomWidth, 0);
|
||||||
|
|
||||||
|
$this->borderTopRows = array_fill(0, $this->borderTopWidth, $borderRow);
|
||||||
|
$this->borderBottomRows = array_fill(0, $this->borderBottomWidth, $borderRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getBorderTopRows() : array
|
||||||
|
{
|
||||||
|
return $this->borderTopRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getBorderBottomRows() : array
|
||||||
|
{
|
||||||
|
return $this->borderBottomRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|string|null $rightWidth
|
||||||
|
* @param int|string|null $bottomWidth
|
||||||
|
* @param int|string|null $leftWidth
|
||||||
|
*
|
||||||
|
* Shorthand function to set all borders values at once
|
||||||
|
*/
|
||||||
|
public function setBorder(
|
||||||
|
int $topWidth,
|
||||||
|
$rightWidth = null,
|
||||||
|
$bottomWidth = null,
|
||||||
|
$leftWidth = null,
|
||||||
|
string $colour = null
|
||||||
|
) : self {
|
||||||
|
if (!is_int($rightWidth)) {
|
||||||
|
$colour = $rightWidth;
|
||||||
|
$rightWidth = $bottomWidth = $leftWidth = $topWidth;
|
||||||
|
} elseif (!is_int($bottomWidth)) {
|
||||||
|
$colour = $bottomWidth;
|
||||||
|
$bottomWidth = $topWidth;
|
||||||
|
$leftWidth = $rightWidth;
|
||||||
|
} elseif (!is_int($leftWidth)) {
|
||||||
|
$colour = $leftWidth;
|
||||||
|
$leftWidth = $rightWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->borderTopWidth = $topWidth;
|
||||||
|
$this->borderRightWidth = $rightWidth;
|
||||||
|
$this->borderBottomWidth = $bottomWidth;
|
||||||
|
$this->borderLeftWidth = $leftWidth;
|
||||||
|
|
||||||
|
if (is_string($colour)) {
|
||||||
|
$this->setBorderColour($colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->calculateContentWidth();
|
||||||
|
$this->generateBorderRows();
|
||||||
|
$this->generatePaddingTopBottomRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderTopWidth(int $width) : self
|
||||||
|
{
|
||||||
|
$this->borderTopWidth = $width;
|
||||||
|
|
||||||
|
$this->generateBorderRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderRightWidth(int $width) : self
|
||||||
|
{
|
||||||
|
$this->borderRightWidth = $width;
|
||||||
|
$this->calculateContentWidth();
|
||||||
|
|
||||||
|
$this->generatePaddingTopBottomRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderBottomWidth(int $width) : self
|
||||||
|
{
|
||||||
|
$this->borderBottomWidth = $width;
|
||||||
|
|
||||||
|
$this->generateBorderRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderLeftWidth(int $width) : self
|
||||||
|
{
|
||||||
|
$this->borderLeftWidth = $width;
|
||||||
|
$this->calculateContentWidth();
|
||||||
|
|
||||||
|
$this->generatePaddingTopBottomRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBorderColour(string $colour, string $fallback = null) : self
|
||||||
|
{
|
||||||
|
$this->borderColour = ColourUtil::validateColour(
|
||||||
|
$this->terminal,
|
||||||
|
$colour,
|
||||||
|
$fallback
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->generateBorderRows();
|
||||||
|
$this->generatePaddingTopBottomRows();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBorderTopWidth() : int
|
||||||
|
{
|
||||||
|
return $this->borderTopWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBorderRightWidth() : int
|
||||||
|
{
|
||||||
|
return $this->borderRightWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBorderBottomWidth() : int
|
||||||
|
{
|
||||||
|
return $this->borderBottomWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBorderLeftWidth() : int
|
||||||
|
{
|
||||||
|
return $this->borderLeftWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBorderColour() : string
|
||||||
|
{
|
||||||
|
return $this->borderColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBorderColourCode() : string
|
||||||
|
{
|
||||||
|
if (!ctype_digit($this->borderColour)) {
|
||||||
|
$borderColourCode = self::$availableBackgroundColors[$this->borderColour];
|
||||||
|
} else {
|
||||||
|
$borderColourCode = sprintf("48;5;%s", $this->borderColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf("\033[%sm", $borderColourCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ansi escape sequence for setting or unsetting the specified option code.
|
||||||
|
*
|
||||||
|
* @param string $string Option code (bold|dim|underscore|blink|reverse|conceal)
|
||||||
|
* @param bool $set Whether to set or unset the code
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getOptionCode(string $string, bool $set = true): string
|
||||||
|
{
|
||||||
|
return sprintf("\033[%sm", self::$availableOptions[$string][$set ? 'set' : 'unset']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a string of given length consisting of 0-9
|
||||||
|
* eg $length = 15 : 012345678901234
|
||||||
|
*/
|
||||||
|
private function getDebugString(int $length) : string
|
||||||
|
{
|
||||||
|
$nums = [];
|
||||||
|
for ($i = 0, $j = 0; $i < $length; $i++, $j++) {
|
||||||
|
if ($j === 10) {
|
||||||
|
$j = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nums[] = $j;
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('', $nums);
|
||||||
|
}
|
||||||
|
}
|
1473
src/ncc/ThirdParty/php-school/cli-menu/README.md
vendored
Normal file
1473
src/ncc/ThirdParty/php-school/cli-menu/README.md
vendored
Normal file
File diff suppressed because it is too large
Load diff
119
src/ncc/ThirdParty/php-school/cli-menu/Style/CheckboxStyle.php
vendored
Normal file
119
src/ncc/ThirdParty/php-school/cli-menu/Style/CheckboxStyle.php
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Style;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuItem\CheckboxItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
|
||||||
|
|
||||||
|
class CheckboxStyle implements ItemStyle
|
||||||
|
{
|
||||||
|
private const DEFAULT_STYLES = [
|
||||||
|
'checkedMarker' => '[✔] ',
|
||||||
|
'uncheckedMarker' => '[ ] ',
|
||||||
|
'itemExtra' => '✔',
|
||||||
|
'displaysExtra' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $checkedMarker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $uncheckedMarker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $itemExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $displaysExtra;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->checkedMarker = self::DEFAULT_STYLES['checkedMarker'];
|
||||||
|
$this->uncheckedMarker = self::DEFAULT_STYLES['uncheckedMarker'];
|
||||||
|
$this->itemExtra = self::DEFAULT_STYLES['itemExtra'];
|
||||||
|
$this->displaysExtra = self::DEFAULT_STYLES['displaysExtra'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasChangedFromDefaults() : bool
|
||||||
|
{
|
||||||
|
$currentValues = [
|
||||||
|
$this->checkedMarker,
|
||||||
|
$this->uncheckedMarker,
|
||||||
|
$this->itemExtra,
|
||||||
|
$this->displaysExtra,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $currentValues !== array_values(self::DEFAULT_STYLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMarker(MenuItemInterface $item, bool $selected) : string
|
||||||
|
{
|
||||||
|
if (!$item instanceof CheckboxItem) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
sprintf('Expected an instance of: %s. Got: %s', CheckboxItem::class, get_class($item))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item->getChecked() ? $this->checkedMarker : $this->uncheckedMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCheckedMarker() : string
|
||||||
|
{
|
||||||
|
return $this->checkedMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCheckedMarker(string $marker) : self
|
||||||
|
{
|
||||||
|
$this->checkedMarker = $marker;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUncheckedMarker() : string
|
||||||
|
{
|
||||||
|
return $this->uncheckedMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUncheckedMarker(string $marker) : self
|
||||||
|
{
|
||||||
|
$this->uncheckedMarker = $marker;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemExtra() : string
|
||||||
|
{
|
||||||
|
return $this->itemExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setItemExtra(string $itemExtra) : self
|
||||||
|
{
|
||||||
|
$this->itemExtra = $itemExtra;
|
||||||
|
|
||||||
|
// if we customise item extra, it means we most likely want to display it
|
||||||
|
$this->setDisplaysExtra(true);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplaysExtra() : bool
|
||||||
|
{
|
||||||
|
return $this->displaysExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDisplaysExtra(bool $displaysExtra) : self
|
||||||
|
{
|
||||||
|
$this->displaysExtra = $displaysExtra;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
30
src/ncc/ThirdParty/php-school/cli-menu/Style/DefaultStyle.php
vendored
Normal file
30
src/ncc/ThirdParty/php-school/cli-menu/Style/DefaultStyle.php
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Style;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
|
||||||
|
|
||||||
|
class DefaultStyle implements ItemStyle
|
||||||
|
{
|
||||||
|
public function hasChangedFromDefaults() : bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplaysExtra() : bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemExtra() : string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMarker(MenuItemInterface $item, bool $isSelected) : string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
30
src/ncc/ThirdParty/php-school/cli-menu/Style/Exception/InvalidStyle.php
vendored
Normal file
30
src/ncc/ThirdParty/php-school/cli-menu/Style/Exception/InvalidStyle.php
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Style\Exception;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
|
||||||
|
|
||||||
|
class InvalidStyle extends \RuntimeException
|
||||||
|
{
|
||||||
|
public static function unregisteredStyle(string $styleClass) : self
|
||||||
|
{
|
||||||
|
return new self("Style class: '$styleClass' is not registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function notSubClassOf(string $styleClass) : self
|
||||||
|
{
|
||||||
|
return new self("Style instance must be a subclass of: '$styleClass'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function unregisteredItem(string $itemClass) : self
|
||||||
|
{
|
||||||
|
return new self("Menu item: '$itemClass' does not have a registered style class");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function itemAlreadyRegistered(string $itemClass) : self
|
||||||
|
{
|
||||||
|
return new self("Menu item: '$itemClass' already has a registered style class");
|
||||||
|
}
|
||||||
|
}
|
18
src/ncc/ThirdParty/php-school/cli-menu/Style/ItemStyle.php
vendored
Normal file
18
src/ncc/ThirdParty/php-school/cli-menu/Style/ItemStyle.php
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Style;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
|
||||||
|
|
||||||
|
interface ItemStyle
|
||||||
|
{
|
||||||
|
public function hasChangedFromDefaults() : bool;
|
||||||
|
|
||||||
|
public function getDisplaysExtra() : bool;
|
||||||
|
|
||||||
|
public function getItemExtra() : string;
|
||||||
|
|
||||||
|
public function getMarker(MenuItemInterface $menuItem, bool $isSelected) : string;
|
||||||
|
}
|
115
src/ncc/ThirdParty/php-school/cli-menu/Style/Locator.php
vendored
Normal file
115
src/ncc/ThirdParty/php-school/cli-menu/Style/Locator.php
vendored
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Style;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuItem\AsciiArtItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\CheckboxItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\LineBreakItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuMenuItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\RadioItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\SelectableItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\SplitItem;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\StaticItem;
|
||||||
|
use PhpSchool\CliMenu\Style\Exception\InvalidStyle;
|
||||||
|
use function PhpSchool\CliMenu\Util\mapWithKeys;
|
||||||
|
|
||||||
|
class Locator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $itemStyleMap = [
|
||||||
|
StaticItem::class => DefaultStyle::class,
|
||||||
|
AsciiArtItem::class => DefaultStyle::class,
|
||||||
|
LineBreakItem::class => DefaultStyle::class,
|
||||||
|
SplitItem::class => DefaultStyle::class,
|
||||||
|
SelectableItem::class => SelectableStyle::class,
|
||||||
|
MenuMenuItem::class => SelectableStyle::class,
|
||||||
|
CheckboxItem::class => CheckboxStyle::class,
|
||||||
|
RadioItem::class => RadioStyle::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $styles;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->styles = [
|
||||||
|
DefaultStyle::class => new DefaultStyle(),
|
||||||
|
SelectableStyle::class => new SelectableStyle(),
|
||||||
|
CheckboxStyle::class => new CheckboxStyle(),
|
||||||
|
RadioStyle::class => new RadioStyle()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each of our unmodified item styles, we replace ours with the versions
|
||||||
|
* from the given style locator.
|
||||||
|
*
|
||||||
|
* @param Locator $other
|
||||||
|
*/
|
||||||
|
public function importFrom(self $other) : void
|
||||||
|
{
|
||||||
|
$this->styles = mapWithKeys(
|
||||||
|
$this->styles,
|
||||||
|
function ($styleClass, ItemStyle $instance) use ($other) {
|
||||||
|
return $instance->hasChangedFromDefaults()
|
||||||
|
? $instance
|
||||||
|
: $other->getStyle($styleClass);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyle(string $styleClass) : ItemStyle
|
||||||
|
{
|
||||||
|
if (!isset($this->styles[$styleClass])) {
|
||||||
|
throw InvalidStyle::unregisteredStyle($styleClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->styles[$styleClass];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(ItemStyle $itemStyle, string $styleClass) : void
|
||||||
|
{
|
||||||
|
if (!isset($this->styles[$styleClass])) {
|
||||||
|
throw InvalidStyle::unregisteredStyle($styleClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$itemStyle instanceof $styleClass) {
|
||||||
|
throw InvalidStyle::notSubClassOf($styleClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->styles[$styleClass] = $itemStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasStyleForMenuItem(MenuItemInterface $item) : bool
|
||||||
|
{
|
||||||
|
return isset($this->itemStyleMap[get_class($item)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyleForMenuItem(MenuItemInterface $item) : ItemStyle
|
||||||
|
{
|
||||||
|
if (!isset($this->itemStyleMap[get_class($item)])) {
|
||||||
|
throw InvalidStyle::unregisteredItem(get_class($item));
|
||||||
|
}
|
||||||
|
|
||||||
|
$styleClass = $this->itemStyleMap[get_class($item)];
|
||||||
|
|
||||||
|
return $this->getStyle($styleClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerItemStyle(string $itemClass, ItemStyle $itemStyle) : void
|
||||||
|
{
|
||||||
|
if (isset($this->itemStyleMap[$itemClass])) {
|
||||||
|
throw InvalidStyle::itemAlreadyRegistered($itemClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->itemStyleMap[$itemClass] = get_class($itemStyle);
|
||||||
|
$this->styles[get_class($itemStyle)] = $itemStyle;
|
||||||
|
}
|
||||||
|
}
|
119
src/ncc/ThirdParty/php-school/cli-menu/Style/RadioStyle.php
vendored
Normal file
119
src/ncc/ThirdParty/php-school/cli-menu/Style/RadioStyle.php
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Style;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
|
||||||
|
use PhpSchool\CliMenu\MenuItem\RadioItem;
|
||||||
|
|
||||||
|
class RadioStyle implements ItemStyle
|
||||||
|
{
|
||||||
|
private const DEFAULT_STYLES = [
|
||||||
|
'checkedMarker' => '[●] ',
|
||||||
|
'uncheckedMarker' => '[○] ',
|
||||||
|
'itemExtra' => '✔',
|
||||||
|
'displaysExtra' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $checkedMarker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $uncheckedMarker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $itemExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $displaysExtra;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->checkedMarker = self::DEFAULT_STYLES['checkedMarker'];
|
||||||
|
$this->uncheckedMarker = self::DEFAULT_STYLES['uncheckedMarker'];
|
||||||
|
$this->itemExtra = self::DEFAULT_STYLES['itemExtra'];
|
||||||
|
$this->displaysExtra = self::DEFAULT_STYLES['displaysExtra'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasChangedFromDefaults() : bool
|
||||||
|
{
|
||||||
|
$currentValues = [
|
||||||
|
$this->checkedMarker,
|
||||||
|
$this->uncheckedMarker,
|
||||||
|
$this->itemExtra,
|
||||||
|
$this->displaysExtra,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $currentValues !== array_values(self::DEFAULT_STYLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMarker(MenuItemInterface $item, bool $selected) : string
|
||||||
|
{
|
||||||
|
if (!$item instanceof RadioItem) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
sprintf('Expected an instance of: %s. Got: %s', RadioItem::class, get_class($item))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item->getChecked() ? $this->checkedMarker : $this->uncheckedMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCheckedMarker() : string
|
||||||
|
{
|
||||||
|
return $this->checkedMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCheckedMarker(string $marker) : self
|
||||||
|
{
|
||||||
|
$this->checkedMarker = $marker;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUncheckedMarker() : string
|
||||||
|
{
|
||||||
|
return $this->uncheckedMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUncheckedMarker(string $marker) : self
|
||||||
|
{
|
||||||
|
$this->uncheckedMarker = $marker;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemExtra() : string
|
||||||
|
{
|
||||||
|
return $this->itemExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setItemExtra(string $itemExtra) : self
|
||||||
|
{
|
||||||
|
$this->itemExtra = $itemExtra;
|
||||||
|
|
||||||
|
// if we customise item extra, it means we most likely want to display it
|
||||||
|
$this->setDisplaysExtra(true);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplaysExtra() : bool
|
||||||
|
{
|
||||||
|
return $this->displaysExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDisplaysExtra(bool $displaysExtra) : self
|
||||||
|
{
|
||||||
|
$this->displaysExtra = $displaysExtra;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
112
src/ncc/ThirdParty/php-school/cli-menu/Style/SelectableStyle.php
vendored
Normal file
112
src/ncc/ThirdParty/php-school/cli-menu/Style/SelectableStyle.php
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Style;
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
|
||||||
|
|
||||||
|
class SelectableStyle implements ItemStyle
|
||||||
|
{
|
||||||
|
private const DEFAULT_STYLES = [
|
||||||
|
'selectedMarker' => '● ',
|
||||||
|
'unselectedMarker' => '○ ',
|
||||||
|
'itemExtra' => '✔',
|
||||||
|
'displaysExtra' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $selectedMarker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $unselectedMarker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $itemExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $displaysExtra;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->selectedMarker = self::DEFAULT_STYLES['selectedMarker'];
|
||||||
|
$this->unselectedMarker = self::DEFAULT_STYLES['unselectedMarker'];
|
||||||
|
$this->itemExtra = self::DEFAULT_STYLES['itemExtra'];
|
||||||
|
$this->displaysExtra = self::DEFAULT_STYLES['displaysExtra'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasChangedFromDefaults() : bool
|
||||||
|
{
|
||||||
|
$currentValues = [
|
||||||
|
$this->selectedMarker,
|
||||||
|
$this->unselectedMarker,
|
||||||
|
$this->itemExtra,
|
||||||
|
$this->displaysExtra,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $currentValues !== array_values(self::DEFAULT_STYLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMarker(MenuItemInterface $item, bool $selected) : string
|
||||||
|
{
|
||||||
|
return $selected ? $this->selectedMarker : $this->unselectedMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSelectedMarker() : string
|
||||||
|
{
|
||||||
|
return $this->selectedMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSelectedMarker(string $marker) : self
|
||||||
|
{
|
||||||
|
$this->selectedMarker = $marker;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUnselectedMarker() : string
|
||||||
|
{
|
||||||
|
return $this->unselectedMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUnselectedMarker(string $marker) : self
|
||||||
|
{
|
||||||
|
$this->unselectedMarker = $marker;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemExtra() : string
|
||||||
|
{
|
||||||
|
return $this->itemExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setItemExtra(string $itemExtra) : self
|
||||||
|
{
|
||||||
|
$this->itemExtra = $itemExtra;
|
||||||
|
|
||||||
|
// if we customise item extra, it means we most likely want to display it
|
||||||
|
$this->setDisplaysExtra(true);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplaysExtra() : bool
|
||||||
|
{
|
||||||
|
return $this->displaysExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDisplaysExtra(bool $displaysExtra) : self
|
||||||
|
{
|
||||||
|
$this->displaysExtra = $displaysExtra;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
20
src/ncc/ThirdParty/php-school/cli-menu/Terminal/TerminalFactory.php
vendored
Normal file
20
src/ncc/ThirdParty/php-school/cli-menu/Terminal/TerminalFactory.php
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Terminal;
|
||||||
|
|
||||||
|
use PhpSchool\Terminal\IO\ResourceInputStream;
|
||||||
|
use PhpSchool\Terminal\IO\ResourceOutputStream;
|
||||||
|
use PhpSchool\Terminal\Terminal;
|
||||||
|
use PhpSchool\Terminal\UnixTerminal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class TerminalFactory
|
||||||
|
{
|
||||||
|
public static function fromSystem() : Terminal
|
||||||
|
{
|
||||||
|
return new UnixTerminal(new ResourceInputStream, new ResourceOutputStream);
|
||||||
|
}
|
||||||
|
}
|
113
src/ncc/ThirdParty/php-school/cli-menu/UPGRADE.md
vendored
Normal file
113
src/ncc/ThirdParty/php-school/cli-menu/UPGRADE.md
vendored
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
# Upgrade Documentation
|
||||||
|
|
||||||
|
This document serves as a reference to upgrade your current cli-menu installation if improvements, deprecations
|
||||||
|
or backwards compatibility (BC) breakages occur.
|
||||||
|
|
||||||
|
## 4.0.0
|
||||||
|
|
||||||
|
### BC breaks
|
||||||
|
|
||||||
|
* Trait `PhpSchool\CliMenu\MenuItem\SelectableTrait` was removed. Copy the old code into your menu item
|
||||||
|
if you need it.
|
||||||
|
* Methods `PhpSchool\CliMenu\Builder\CliMenuBuilder#setUnselectedMarker()` & `PhpSchool\CliMenu\Builder\CliMenuBuilder#setSelectedMarker()` were removed.
|
||||||
|
Customise markers on the individual item styles:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\Builder\CliMenuBuilder;
|
||||||
|
use PhpSchool\CliMenu\Style\SelectableStyle;
|
||||||
|
|
||||||
|
$menu = (new CliMenuBuilder)
|
||||||
|
->modifySelectableStyle(function (SelectableStyle $style) {
|
||||||
|
$style->setUnselectedMarker('❅ ')
|
||||||
|
->setSelectedMarker('✏ ')
|
||||||
|
|
||||||
|
// disable unselected marker
|
||||||
|
->setUnselectedMarker('')
|
||||||
|
;
|
||||||
|
})
|
||||||
|
->build();
|
||||||
|
```
|
||||||
|
* Method getStyle() was added to interface PhpSchool\CliMenu\MenuItem\MenuItemInterface. Items must now implement this
|
||||||
|
method. For selectable items use `\PhpSchool\CliMenu\Style\SelectableStyle` or a subclass of. For static items use
|
||||||
|
`\PhpSchool\CliMenu\Style\DefaultStyle` or a subclass of.
|
||||||
|
* `PhpSchool\CliMenu\MenuStyle` marker methods have been removed. If you were using these directly. Operate on the item
|
||||||
|
style object instead.
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
### BC breaks
|
||||||
|
|
||||||
|
* Class `PhpSchool\CliMenu\CliMenuBuilder` has been moved, use
|
||||||
|
`PhpSchool\CliMenu\Builder\CliMenuBuilder` instead. Please migrate to the new namespace.
|
||||||
|
* `PhpSchool\CliMenu\Builder\CliMenuBuilder#addSubMenu` now takes a text and a closure used to configure the submenu. The callback
|
||||||
|
invoked with a new instance of `PhpSchool\CliMenu\Builder\CliMenuBuilder` as a parameter. `addSubMenu` now returns itself instead of
|
||||||
|
the sub menu `PhpSchool\CliMenu\Builder\CliMenuBuilder`. See below for upgrade example.
|
||||||
|
* Removed `PhpSchool\CliMenu\Terminal` namespace, the code has been migrated to the `php-school/terminal` package and is
|
||||||
|
largely modified.
|
||||||
|
* Removed methods `setTerminal`, `getSubMenu`, `getMenuStyle` and `end` from `PhpSchool\CliMenu\CliMenuBuilder`.
|
||||||
|
* Removed static method `getDefaultStyleValues` on `PhpSchool\CliMenu\MenuStyle`.
|
||||||
|
|
||||||
|
|
||||||
|
#### Migrating to new `addSubMenu` method in `CliMenuBuilder`
|
||||||
|
|
||||||
|
Previous code:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use PhpSchool\CliMenu\CliMenuBuilder;
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/../vendor/autoload.php');
|
||||||
|
|
||||||
|
$itemCallable = function (CliMenu $menu) {
|
||||||
|
echo $menu->getSelectedItem()->getText();
|
||||||
|
};
|
||||||
|
|
||||||
|
$menu = (new CliMenuBuilder)
|
||||||
|
->setTitle('CLI Menu')
|
||||||
|
->addItem('First Item', $itemCallable)
|
||||||
|
->addLineBreak('-')
|
||||||
|
->addSubMenu('Options')
|
||||||
|
->setTitle('CLI Menu > Options')
|
||||||
|
->addItem('First option', function (CliMenu $menu) {
|
||||||
|
echo sprintf('Executing option: %s', $menu->getSelectedItem()->getText());
|
||||||
|
})
|
||||||
|
->addLineBreak('-')
|
||||||
|
->end()
|
||||||
|
->build();
|
||||||
|
|
||||||
|
$menu->open();
|
||||||
|
```
|
||||||
|
|
||||||
|
Would now become:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpSchool\CliMenu\CliMenu;
|
||||||
|
use \PhpSchool\CliMenu\Builder\CliMenuBuilder;
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/../vendor/autoload.php');
|
||||||
|
|
||||||
|
$itemCallable = function (CliMenu $menu) {
|
||||||
|
echo $menu->getSelectedItem()->getText();
|
||||||
|
};
|
||||||
|
|
||||||
|
$menu = (new CliMenuBuilder)
|
||||||
|
->setTitle('CLI Menu')
|
||||||
|
->addItem('First Item', $itemCallable)
|
||||||
|
->addLineBreak('-')
|
||||||
|
->addSubMenu('Options', function (CliMenuBuilder $b) {
|
||||||
|
$b->setTitle('CLI Menu > Options')
|
||||||
|
->addItem('First option', function (CliMenu $menu) {
|
||||||
|
echo sprintf('Executing option: %s', $menu->getSelectedItem()->getText());
|
||||||
|
})
|
||||||
|
->addLineBreak('-');
|
||||||
|
})
|
||||||
|
->build();
|
||||||
|
|
||||||
|
$menu->open();
|
||||||
|
```
|
41
src/ncc/ThirdParty/php-school/cli-menu/Util/ArrayUtils.php
vendored
Normal file
41
src/ncc/ThirdParty/php-school/cli-menu/Util/ArrayUtils.php
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Util;
|
||||||
|
|
||||||
|
function mapWithKeys(array $array, callable $callback) : array
|
||||||
|
{
|
||||||
|
$arr = array_combine(
|
||||||
|
array_keys($array),
|
||||||
|
array_map($callback, array_keys($array), $array)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(is_array($arr));
|
||||||
|
|
||||||
|
return $arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function filter(array $array, callable $callback) : array
|
||||||
|
{
|
||||||
|
return array_filter($array, function ($v, $k) use ($callback) {
|
||||||
|
return $callback($k, $v);
|
||||||
|
}, ARRAY_FILTER_USE_BOTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
function each(array $array, callable $callback) : void
|
||||||
|
{
|
||||||
|
foreach ($array as $k => $v) {
|
||||||
|
$callback($k, $v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function max(array $items) : int
|
||||||
|
{
|
||||||
|
return count($items) > 0 ? \max($items) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function collect(array $items) : Collection
|
||||||
|
{
|
||||||
|
return new Collection($items);
|
||||||
|
}
|
45
src/ncc/ThirdParty/php-school/cli-menu/Util/Collection.php
vendored
Normal file
45
src/ncc/ThirdParty/php-school/cli-menu/Util/Collection.php
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Util;
|
||||||
|
|
||||||
|
class Collection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $items;
|
||||||
|
|
||||||
|
public function __construct(array $items)
|
||||||
|
{
|
||||||
|
$this->items = $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function map(callable $cb) : self
|
||||||
|
{
|
||||||
|
return new self(mapWithKeys($this->items, $cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter(callable $cb) : self
|
||||||
|
{
|
||||||
|
return new self(filter($this->items, $cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function values() : self
|
||||||
|
{
|
||||||
|
return new self(array_values($this->items));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function each(callable $cb) : self
|
||||||
|
{
|
||||||
|
each($this->items, $cb);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function all() : array
|
||||||
|
{
|
||||||
|
return $this->items;
|
||||||
|
}
|
||||||
|
}
|
334
src/ncc/ThirdParty/php-school/cli-menu/Util/ColourUtil.php
vendored
Normal file
334
src/ncc/ThirdParty/php-school/cli-menu/Util/ColourUtil.php
vendored
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Util;
|
||||||
|
|
||||||
|
use Assert\Assertion;
|
||||||
|
use PhpSchool\Terminal\Terminal;
|
||||||
|
|
||||||
|
class ColourUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $defaultColoursNames = [
|
||||||
|
'black',
|
||||||
|
'red',
|
||||||
|
'green',
|
||||||
|
'yellow',
|
||||||
|
'blue',
|
||||||
|
'magenta',
|
||||||
|
'cyan',
|
||||||
|
'white',
|
||||||
|
'default',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $coloursMap = [
|
||||||
|
0 => 'black',
|
||||||
|
1 => 'red',
|
||||||
|
2 => 'green',
|
||||||
|
3 => 'yellow',
|
||||||
|
4 => 'blue',
|
||||||
|
5 => 'magenta',
|
||||||
|
6 => 'cyan',
|
||||||
|
7 => 'white',
|
||||||
|
8 => 'black',
|
||||||
|
9 => 'red',
|
||||||
|
10 => 'green',
|
||||||
|
11 => 'yellow',
|
||||||
|
12 => 'blue',
|
||||||
|
13 => 'magenta',
|
||||||
|
14 => 'cyan',
|
||||||
|
15 => 'white',
|
||||||
|
16 => 'black',
|
||||||
|
17 => 'blue',
|
||||||
|
18 => 'blue',
|
||||||
|
19 => 'blue',
|
||||||
|
20 => 'blue',
|
||||||
|
21 => 'blue',
|
||||||
|
22 => 'green',
|
||||||
|
23 => 'cyan',
|
||||||
|
24 => 'cyan',
|
||||||
|
25 => 'blue',
|
||||||
|
26 => 'blue',
|
||||||
|
27 => 'blue',
|
||||||
|
28 => 'green',
|
||||||
|
29 => 'green',
|
||||||
|
30 => 'cyan',
|
||||||
|
31 => 'cyan',
|
||||||
|
32 => 'blue',
|
||||||
|
33 => 'blue',
|
||||||
|
34 => 'green',
|
||||||
|
35 => 'green',
|
||||||
|
36 => 'green',
|
||||||
|
37 => 'cyan',
|
||||||
|
38 => 'cyan',
|
||||||
|
39 => 'cyan',
|
||||||
|
40 => 'green',
|
||||||
|
41 => 'green',
|
||||||
|
42 => 'green',
|
||||||
|
43 => 'cyan',
|
||||||
|
44 => 'cyan',
|
||||||
|
45 => 'cyan',
|
||||||
|
46 => 'green',
|
||||||
|
47 => 'green',
|
||||||
|
48 => 'green',
|
||||||
|
49 => 'green',
|
||||||
|
50 => 'cyan',
|
||||||
|
51 => 'cyan',
|
||||||
|
52 => 'red',
|
||||||
|
53 => 'blue',
|
||||||
|
54 => 'blue',
|
||||||
|
55 => 'blue',
|
||||||
|
56 => 'blue',
|
||||||
|
57 => 'blue',
|
||||||
|
58 => 'yellow',
|
||||||
|
59 => 'black',
|
||||||
|
60 => 'blue',
|
||||||
|
61 => 'blue',
|
||||||
|
62 => 'blue',
|
||||||
|
63 => 'blue',
|
||||||
|
64 => 'green',
|
||||||
|
65 => 'green',
|
||||||
|
66 => 'cyan',
|
||||||
|
67 => 'cyan',
|
||||||
|
68 => 'blue',
|
||||||
|
69 => 'blue',
|
||||||
|
70 => 'green',
|
||||||
|
71 => 'green',
|
||||||
|
72 => 'green',
|
||||||
|
73 => 'cyan',
|
||||||
|
74 => 'cyan',
|
||||||
|
75 => 'cyan',
|
||||||
|
76 => 'green',
|
||||||
|
77 => 'green',
|
||||||
|
78 => 'green',
|
||||||
|
79 => 'green',
|
||||||
|
80 => 'cyan',
|
||||||
|
81 => 'cyan',
|
||||||
|
82 => 'green',
|
||||||
|
83 => 'green',
|
||||||
|
84 => 'green',
|
||||||
|
85 => 'green',
|
||||||
|
86 => 'cyan',
|
||||||
|
87 => 'cyan',
|
||||||
|
88 => 'red',
|
||||||
|
89 => 'magenta',
|
||||||
|
90 => 'magenta',
|
||||||
|
91 => 'magenta',
|
||||||
|
92 => 'blue',
|
||||||
|
93 => 'blue',
|
||||||
|
94 => 'yellow',
|
||||||
|
95 => 'red',
|
||||||
|
96 => 'magenta',
|
||||||
|
97 => 'magenta',
|
||||||
|
98 => 'blue',
|
||||||
|
99 => 'blue',
|
||||||
|
100 => 'yellow',
|
||||||
|
101 => 'yellow',
|
||||||
|
102 => 'white',
|
||||||
|
103 => 'blue',
|
||||||
|
104 => 'blue',
|
||||||
|
105 => 'blue',
|
||||||
|
106 => 'green',
|
||||||
|
107 => 'green',
|
||||||
|
108 => 'green',
|
||||||
|
109 => 'cyan',
|
||||||
|
110 => 'cyan',
|
||||||
|
111 => 'cyan',
|
||||||
|
112 => 'green',
|
||||||
|
113 => 'green',
|
||||||
|
114 => 'green',
|
||||||
|
115 => 'green',
|
||||||
|
116 => 'cyan',
|
||||||
|
117 => 'cyan',
|
||||||
|
118 => 'green',
|
||||||
|
119 => 'green',
|
||||||
|
120 => 'green',
|
||||||
|
121 => 'green',
|
||||||
|
122 => 'green',
|
||||||
|
123 => 'cyan',
|
||||||
|
124 => 'red',
|
||||||
|
125 => 'magenta',
|
||||||
|
126 => 'magenta',
|
||||||
|
127 => 'magenta',
|
||||||
|
128 => 'magenta',
|
||||||
|
129 => 'magenta',
|
||||||
|
130 => 'red',
|
||||||
|
131 => 'red',
|
||||||
|
132 => 'magenta',
|
||||||
|
133 => 'magenta',
|
||||||
|
134 => 'magenta',
|
||||||
|
135 => 'magenta',
|
||||||
|
136 => 'yellow',
|
||||||
|
137 => 'red',
|
||||||
|
138 => 'red',
|
||||||
|
139 => 'magenta',
|
||||||
|
140 => 'magenta',
|
||||||
|
141 => 'magenta',
|
||||||
|
142 => 'yellow',
|
||||||
|
143 => 'yellow',
|
||||||
|
144 => 'yellow',
|
||||||
|
145 => 'white',
|
||||||
|
146 => 'white',
|
||||||
|
147 => 'white',
|
||||||
|
148 => 'yellow',
|
||||||
|
149 => 'green',
|
||||||
|
150 => 'green',
|
||||||
|
151 => 'green',
|
||||||
|
152 => 'cyan',
|
||||||
|
153 => 'white',
|
||||||
|
154 => 'green',
|
||||||
|
155 => 'green',
|
||||||
|
156 => 'green',
|
||||||
|
157 => 'green',
|
||||||
|
158 => 'green',
|
||||||
|
159 => 'cyan',
|
||||||
|
160 => 'red',
|
||||||
|
161 => 'magenta',
|
||||||
|
162 => 'magenta',
|
||||||
|
163 => 'magenta',
|
||||||
|
164 => 'magenta',
|
||||||
|
165 => 'magenta',
|
||||||
|
166 => 'red',
|
||||||
|
167 => 'red',
|
||||||
|
168 => 'magenta',
|
||||||
|
169 => 'magenta',
|
||||||
|
170 => 'magenta',
|
||||||
|
171 => 'magenta',
|
||||||
|
172 => 'red',
|
||||||
|
173 => 'red',
|
||||||
|
174 => 'red',
|
||||||
|
175 => 'magenta',
|
||||||
|
176 => 'magenta',
|
||||||
|
177 => 'magenta',
|
||||||
|
178 => 'yellow',
|
||||||
|
179 => 'yellow',
|
||||||
|
180 => 'white',
|
||||||
|
181 => 'white',
|
||||||
|
182 => 'magenta',
|
||||||
|
183 => 'magenta',
|
||||||
|
184 => 'yellow',
|
||||||
|
185 => 'yellow',
|
||||||
|
186 => 'yellow',
|
||||||
|
187 => 'yellow',
|
||||||
|
188 => 'white',
|
||||||
|
189 => 'white',
|
||||||
|
190 => 'yellow',
|
||||||
|
191 => 'yellow',
|
||||||
|
192 => 'green',
|
||||||
|
193 => 'green',
|
||||||
|
194 => 'green',
|
||||||
|
195 => 'cyan',
|
||||||
|
196 => 'red',
|
||||||
|
197 => 'red',
|
||||||
|
198 => 'magenta',
|
||||||
|
199 => 'magenta',
|
||||||
|
200 => 'magenta',
|
||||||
|
201 => 'magenta',
|
||||||
|
202 => 'red',
|
||||||
|
203 => 'red',
|
||||||
|
204 => 'magenta',
|
||||||
|
205 => 'magenta',
|
||||||
|
206 => 'magenta',
|
||||||
|
207 => 'magenta',
|
||||||
|
208 => 'red',
|
||||||
|
209 => 'red',
|
||||||
|
210 => 'red',
|
||||||
|
211 => 'magenta',
|
||||||
|
212 => 'magenta',
|
||||||
|
213 => 'magenta',
|
||||||
|
214 => 'red',
|
||||||
|
215 => 'white',
|
||||||
|
216 => 'red',
|
||||||
|
217 => 'red',
|
||||||
|
218 => 'magenta',
|
||||||
|
219 => 'magenta',
|
||||||
|
220 => 'yellow',
|
||||||
|
221 => 'yellow',
|
||||||
|
222 => 'yellow',
|
||||||
|
223 => 'white',
|
||||||
|
224 => 'white',
|
||||||
|
225 => 'magenta',
|
||||||
|
226 => 'yellow',
|
||||||
|
227 => 'yellow',
|
||||||
|
228 => 'yellow',
|
||||||
|
229 => 'yellow',
|
||||||
|
230 => 'yellow',
|
||||||
|
231 => 'white',
|
||||||
|
232 => 'black',
|
||||||
|
233 => 'black',
|
||||||
|
234 => 'black',
|
||||||
|
235 => 'black',
|
||||||
|
236 => 'black',
|
||||||
|
237 => 'black',
|
||||||
|
238 => 'black',
|
||||||
|
239 => 'black',
|
||||||
|
240 => 'black',
|
||||||
|
241 => 'black',
|
||||||
|
242 => 'black',
|
||||||
|
243 => 'black',
|
||||||
|
244 => 'white',
|
||||||
|
245 => 'white',
|
||||||
|
246 => 'white',
|
||||||
|
247 => 'white',
|
||||||
|
248 => 'white',
|
||||||
|
249 => 'white',
|
||||||
|
250 => 'white',
|
||||||
|
251 => 'white',
|
||||||
|
252 => 'white',
|
||||||
|
253 => 'white',
|
||||||
|
254 => 'white',
|
||||||
|
255 => 'white',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function getDefaultColourNames() : array
|
||||||
|
{
|
||||||
|
return self::$defaultColoursNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple function to transform a 8-bit (256 colours) colour code
|
||||||
|
* to one of the default 8 colors available in the terminal
|
||||||
|
*/
|
||||||
|
public static function map256To8(int $colourCode) : string
|
||||||
|
{
|
||||||
|
if (!isset(self::$coloursMap[$colourCode])) {
|
||||||
|
throw new \InvalidArgumentException('Invalid colour code');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$coloursMap[$colourCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if $colour exists
|
||||||
|
* If it's a 256-colours code and $terminal doesn't support it, returns a fallback value
|
||||||
|
*/
|
||||||
|
public static function validateColour(Terminal $terminal, string $colour, string $fallback = null) : string
|
||||||
|
{
|
||||||
|
if (!ctype_digit($colour)) {
|
||||||
|
return self::validateColourName($colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assertion::between($colour, 0, 255, 'Invalid colour code');
|
||||||
|
|
||||||
|
if ($terminal->getColourSupport() >= 256) {
|
||||||
|
return $colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fallback !== null) {
|
||||||
|
return self::validateColourName($fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::map256To8((int) $colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function validateColourName(string $colourName) : string
|
||||||
|
{
|
||||||
|
Assertion::inArray($colourName, static::getDefaultColourNames());
|
||||||
|
return $colourName;
|
||||||
|
}
|
||||||
|
}
|
52
src/ncc/ThirdParty/php-school/cli-menu/Util/StringUtil.php
vendored
Normal file
52
src/ncc/ThirdParty/php-school/cli-menu/Util/StringUtil.php
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpSchool\CliMenu\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||||
|
*/
|
||||||
|
class StringUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Minimal multi-byte wordwrap implementation
|
||||||
|
* which also takes break length into consideration
|
||||||
|
*/
|
||||||
|
public static function wordwrap(string $string, int $width, string $break = "\n") : string
|
||||||
|
{
|
||||||
|
return implode(
|
||||||
|
$break,
|
||||||
|
array_map(function (string $line) use ($width, $break) {
|
||||||
|
$line = rtrim($line);
|
||||||
|
if (mb_strlen($line) <= $width) {
|
||||||
|
return $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
$words = explode(' ', $line);
|
||||||
|
$line = '';
|
||||||
|
$actual = '';
|
||||||
|
foreach ($words as $word) {
|
||||||
|
if (mb_strlen($actual . $word) <= $width) {
|
||||||
|
$actual .= $word . ' ';
|
||||||
|
} else {
|
||||||
|
if ($actual !== '') {
|
||||||
|
$line .= rtrim($actual) . $break;
|
||||||
|
}
|
||||||
|
$actual = $word . ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $line . trim($actual);
|
||||||
|
}, explode("\n", $string))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function stripAnsiEscapeSequence(string $str) : string
|
||||||
|
{
|
||||||
|
return (string) preg_replace('/\x1b[^m]*m/', '', $str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function length(string $str, bool $ignoreAnsiEscapeSequence = true) : int
|
||||||
|
{
|
||||||
|
return mb_strlen($ignoreAnsiEscapeSequence ? self::stripAnsiEscapeSequence($str) : $str);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ spl_autoload_register(
|
||||||
$classes = array(
|
$classes = array(
|
||||||
'ncc\\abstracts\\exceptioncodes' => '/Abstracts/ExceptionCodes.php',
|
'ncc\\abstracts\\exceptioncodes' => '/Abstracts/ExceptionCodes.php',
|
||||||
'ncc\\abstracts\\regexpatterns' => '/Abstracts/RegexPatterns.php',
|
'ncc\\abstracts\\regexpatterns' => '/Abstracts/RegexPatterns.php',
|
||||||
|
'ncc\\exceptions\\filenotfoundexception' => '/Exceptions/FileNotFoundException.php',
|
||||||
'ncc\\exceptions\\invalidprojectconfigurationexception' => '/Exceptions/InvalidProjectConfigurationException.php',
|
'ncc\\exceptions\\invalidprojectconfigurationexception' => '/Exceptions/InvalidProjectConfigurationException.php',
|
||||||
'ncc\\ncc' => '/ncc.php',
|
'ncc\\ncc' => '/ncc.php',
|
||||||
'ncc\\objects\\projectconfiguration' => '/Objects/ProjectConfiguration.php',
|
'ncc\\objects\\projectconfiguration' => '/Objects/ProjectConfiguration.php',
|
||||||
|
@ -20,6 +21,49 @@ spl_autoload_register(
|
||||||
'ncc\\objects\\projectconfiguration\\project' => '/Objects/ProjectConfiguration/Project.php',
|
'ncc\\objects\\projectconfiguration\\project' => '/Objects/ProjectConfiguration/Project.php',
|
||||||
'ncc\\utilities\\functions' => '/Utilities/Functions.php',
|
'ncc\\utilities\\functions' => '/Utilities/Functions.php',
|
||||||
'ncc\\utilities\\validate' => '/Utilities/Validate.php',
|
'ncc\\utilities\\validate' => '/Utilities/Validate.php',
|
||||||
|
'phpschool\\climenu\\action\\exitaction' => '/ThirdParty/php-school/cli-menu/Action/ExitAction.php',
|
||||||
|
'phpschool\\climenu\\action\\gobackaction' => '/ThirdParty/php-school/cli-menu/Action/GoBackAction.php',
|
||||||
|
'phpschool\\climenu\\builder\\climenubuilder' => '/ThirdParty/php-school/cli-menu/Builder/CliMenuBuilder.php',
|
||||||
|
'phpschool\\climenu\\builder\\splititembuilder' => '/ThirdParty/php-school/cli-menu/Builder/SplitItemBuilder.php',
|
||||||
|
'phpschool\\climenu\\climenu' => '/ThirdParty/php-school/cli-menu/CliMenu.php',
|
||||||
|
'phpschool\\climenu\\dialogue\\cancellableconfirm' => '/ThirdParty/php-school/cli-menu/Dialogue/CancellableConfirm.php',
|
||||||
|
'phpschool\\climenu\\dialogue\\confirm' => '/ThirdParty/php-school/cli-menu/Dialogue/Confirm.php',
|
||||||
|
'phpschool\\climenu\\dialogue\\dialogue' => '/ThirdParty/php-school/cli-menu/Dialogue/Dialogue.php',
|
||||||
|
'phpschool\\climenu\\dialogue\\flash' => '/ThirdParty/php-school/cli-menu/Dialogue/Flash.php',
|
||||||
|
'phpschool\\climenu\\exception\\cannotshrinkmenuexception' => '/ThirdParty/php-school/cli-menu/Exception/CannotShrinkMenuException.php',
|
||||||
|
'phpschool\\climenu\\exception\\invalidshortcutexception' => '/ThirdParty/php-school/cli-menu/Exception/InvalidShortcutException.php',
|
||||||
|
'phpschool\\climenu\\exception\\invalidterminalexception' => '/ThirdParty/php-school/cli-menu/Exception/InvalidTerminalException.php',
|
||||||
|
'phpschool\\climenu\\exception\\menunotopenexception' => '/ThirdParty/php-school/cli-menu/Exception/MenuNotOpenException.php',
|
||||||
|
'phpschool\\climenu\\frame' => '/ThirdParty/php-school/cli-menu/Frame.php',
|
||||||
|
'phpschool\\climenu\\input\\input' => '/ThirdParty/php-school/cli-menu/Input/Input.php',
|
||||||
|
'phpschool\\climenu\\input\\inputio' => '/ThirdParty/php-school/cli-menu/Input/InputIO.php',
|
||||||
|
'phpschool\\climenu\\input\\inputresult' => '/ThirdParty/php-school/cli-menu/Input/InputResult.php',
|
||||||
|
'phpschool\\climenu\\input\\number' => '/ThirdParty/php-school/cli-menu/Input/Number.php',
|
||||||
|
'phpschool\\climenu\\input\\password' => '/ThirdParty/php-school/cli-menu/Input/Password.php',
|
||||||
|
'phpschool\\climenu\\input\\text' => '/ThirdParty/php-school/cli-menu/Input/Text.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\asciiartitem' => '/ThirdParty/php-school/cli-menu/MenuItem/AsciiArtItem.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\checkboxitem' => '/ThirdParty/php-school/cli-menu/MenuItem/CheckboxItem.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\linebreakitem' => '/ThirdParty/php-school/cli-menu/MenuItem/LineBreakItem.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\menuiteminterface' => '/ThirdParty/php-school/cli-menu/MenuItem/MenuItemInterface.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\menumenuitem' => '/ThirdParty/php-school/cli-menu/MenuItem/MenuMenuItem.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\propagatesstyles' => '/ThirdParty/php-school/cli-menu/MenuItem/PropagatesStyles.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\radioitem' => '/ThirdParty/php-school/cli-menu/MenuItem/RadioItem.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\selectableitem' => '/ThirdParty/php-school/cli-menu/MenuItem/SelectableItem.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\selectableitemrenderer' => '/ThirdParty/php-school/cli-menu/MenuItem/SelectableItemRenderer.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\splititem' => '/ThirdParty/php-school/cli-menu/MenuItem/SplitItem.php',
|
||||||
|
'phpschool\\climenu\\menuitem\\staticitem' => '/ThirdParty/php-school/cli-menu/MenuItem/StaticItem.php',
|
||||||
|
'phpschool\\climenu\\menustyle' => '/ThirdParty/php-school/cli-menu/MenuStyle.php',
|
||||||
|
'phpschool\\climenu\\style\\checkboxstyle' => '/ThirdParty/php-school/cli-menu/Style/CheckboxStyle.php',
|
||||||
|
'phpschool\\climenu\\style\\defaultstyle' => '/ThirdParty/php-school/cli-menu/Style/DefaultStyle.php',
|
||||||
|
'phpschool\\climenu\\style\\exception\\invalidstyle' => '/ThirdParty/php-school/cli-menu/Style/Exception/InvalidStyle.php',
|
||||||
|
'phpschool\\climenu\\style\\itemstyle' => '/ThirdParty/php-school/cli-menu/Style/ItemStyle.php',
|
||||||
|
'phpschool\\climenu\\style\\locator' => '/ThirdParty/php-school/cli-menu/Style/Locator.php',
|
||||||
|
'phpschool\\climenu\\style\\radiostyle' => '/ThirdParty/php-school/cli-menu/Style/RadioStyle.php',
|
||||||
|
'phpschool\\climenu\\style\\selectablestyle' => '/ThirdParty/php-school/cli-menu/Style/SelectableStyle.php',
|
||||||
|
'phpschool\\climenu\\terminal\\terminalfactory' => '/ThirdParty/php-school/cli-menu/Terminal/TerminalFactory.php',
|
||||||
|
'phpschool\\climenu\\util\\collection' => '/ThirdParty/php-school/cli-menu/Util/Collection.php',
|
||||||
|
'phpschool\\climenu\\util\\colourutil' => '/ThirdParty/php-school/cli-menu/Util/ColourUtil.php',
|
||||||
|
'phpschool\\climenu\\util\\stringutil' => '/ThirdParty/php-school/cli-menu/Util/StringUtil.php',
|
||||||
'symfony\\component\\nccprocess\\exception\\exceptioninterface' => '/ThirdParty/Symfony/Process/Exception/ExceptionInterface.php',
|
'symfony\\component\\nccprocess\\exception\\exceptioninterface' => '/ThirdParty/Symfony/Process/Exception/ExceptionInterface.php',
|
||||||
'symfony\\component\\nccprocess\\exception\\invalidargumentexception' => '/ThirdParty/Symfony/Process/Exception/InvalidArgumentException.php',
|
'symfony\\component\\nccprocess\\exception\\invalidargumentexception' => '/ThirdParty/Symfony/Process/Exception/InvalidArgumentException.php',
|
||||||
'symfony\\component\\nccprocess\\exception\\logicexception' => '/ThirdParty/Symfony/Process/Exception/LogicException.php',
|
'symfony\\component\\nccprocess\\exception\\logicexception' => '/ThirdParty/Symfony/Process/Exception/LogicException.php',
|
||||||
|
|
|
@ -1 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
$SourceDirectory = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'ncc';
|
||||||
|
|
||||||
|
if(file_exists($SourceDirectory . DIRECTORY_SEPARATOR . 'autoload.php') == false)
|
||||||
|
throw new RuntimeException('The autoload file was not found in \'' . $SourceDirectory . '\'');
|
||||||
|
|
||||||
|
require($SourceDirectory . DIRECTORY_SEPARATOR . 'autoload.php');
|
|
@ -1 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
require(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'autoload.php');
|
Loading…
Add table
Reference in a new issue