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(
|
||||
'ncc\\abstracts\\exceptioncodes' => '/Abstracts/ExceptionCodes.php',
|
||||
'ncc\\abstracts\\regexpatterns' => '/Abstracts/RegexPatterns.php',
|
||||
'ncc\\exceptions\\filenotfoundexception' => '/Exceptions/FileNotFoundException.php',
|
||||
'ncc\\exceptions\\invalidprojectconfigurationexception' => '/Exceptions/InvalidProjectConfigurationException.php',
|
||||
'ncc\\ncc' => '/ncc.php',
|
||||
'ncc\\objects\\projectconfiguration' => '/Objects/ProjectConfiguration.php',
|
||||
|
@ -20,6 +21,49 @@ spl_autoload_register(
|
|||
'ncc\\objects\\projectconfiguration\\project' => '/Objects/ProjectConfiguration/Project.php',
|
||||
'ncc\\utilities\\functions' => '/Utilities/Functions.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\\invalidargumentexception' => '/ThirdParty/Symfony/Process/Exception/InvalidArgumentException.php',
|
||||
'symfony\\component\\nccprocess\\exception\\logicexception' => '/ThirdParty/Symfony/Process/Exception/LogicException.php',
|
||||
|
|
|
@ -1 +1,8 @@
|
|||
<?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
|
||||
|
||||
require(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'autoload.php');
|
Loading…
Add table
Reference in a new issue