Skip to content

Commit c7bdb1b

Browse files
authored
Merge pull request #40 from php-school/feature/disable-items
Add disabled item functionality
2 parents a6989b8 + a02ca7f commit c7bdb1b

File tree

10 files changed

+153
-16
lines changed

10 files changed

+153
-16
lines changed

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ Check out the [examples](examples) directory and run them to check out what is p
8585
<img width="600" alt="submenu" src="https://cloud.githubusercontent.com/assets/2817002/11442401/e6f03ef2-950c-11e5-897a-6d55496a4105.png">
8686
<img width="600" alt="submenu-options" src="https://cloud.githubusercontent.com/assets/2817002/11442403/eaf4782e-950c-11e5-82c5-ab57f84cd6bc.png">
8787

88+
##### Disabled Items & Submenus
89+
<img width="600" alt="submenu" src="https://cloud.githubusercontent.com/assets/2174476/19047849/868fa8c0-899b-11e6-9004-811c8da6d435.png">
8890

8991
### API
9092

@@ -329,7 +331,38 @@ $subMenu = $mainMenuBuilder->getSubMenu('Super Sub Menu');
329331

330332
You can only do this after the main menu has been built, this is because the main menu builder takes care of building all sub menus.
331333

334+
#### Disabling Items & Sub Menus
332335

336+
In this example we are disabling certain items and a submenu but still having them shown in the output.
337+
338+
```php
339+
$itemCallable = function (CliMenu $menu) {
340+
echo $menu->getSelectedItem()->getText();
341+
};
342+
343+
$menu = (new CliMenuBuilder)
344+
->setTitle('Basic CLI Menu Disabled Items')
345+
->addItem('First Item', $itemCallable)
346+
->addItem('Second Item', $itemCallable, false, true)
347+
->addItem('Third Item', $itemCallable, false, true)
348+
->addSubMenu('Submenu')
349+
->setTitle('Basic CLI Menu Disabled Items > Submenu')
350+
->addItem('You can go in here!', $itemCallable)
351+
->end()
352+
->addSubMenu('Disabled Submenu')
353+
->setTitle('Basic CLI Menu Disabled Items > Disabled Submenu')
354+
->addItem('Nope can\'t see this!', $itemCallable)
355+
->disableMenu()
356+
->end()
357+
->addLineBreak('-')
358+
->build();
359+
```
360+
361+
The third param on the `->addItem` call is what disables an item while the `->disableMenu()` call disables the relevent menu.
362+
363+
The outcome is a full menu with dimmed rows to denote them being disabled. When a user navigates these items are jumped over to the next available selectable item.
364+
365+
---
333366

334367
Once you get going you might just end up with something that looks a little like this...
335368

examples/disabled-items.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
use PhpSchool\CliMenu\CliMenu;
4+
use PhpSchool\CliMenu\CliMenuBuilder;
5+
6+
require_once(__DIR__ . '/../vendor/autoload.php');
7+
8+
$itemCallable = function (CliMenu $menu) {
9+
echo $menu->getSelectedItem()->getText();
10+
};
11+
12+
$menu = (new CliMenuBuilder)
13+
->setTitle('Basic CLI Menu Disabled Items')
14+
->addItem('First Item', $itemCallable)
15+
->addItem('Second Item', $itemCallable, false, true)
16+
->addItem('Third Item', $itemCallable, false, true)
17+
->addSubMenu('Submenu')
18+
->setTitle('Basic CLI Menu Disabled Items > Submenu')
19+
->addItem('You can go in here!', $itemCallable)
20+
->end()
21+
->addSubMenu('Disabled Submenu')
22+
->setTitle('Basic CLI Menu Disabled Items > Disabled Submenu')
23+
->addItem('Nope can\'t see this!', $itemCallable)
24+
->disableMenu()
25+
->end()
26+
->addLineBreak('-')
27+
->build();
28+
29+
$menu->open();

src/CliMenu.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
use PhpSchool\CliMenu\Exception\InvalidInstantiationException;
66
use PhpSchool\CliMenu\Exception\InvalidTerminalException;
77
use PhpSchool\CliMenu\MenuItem\LineBreakItem;
8-
use PhpSchool\CliMenu\MenuItem\MenuItem;
98
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
109
use PhpSchool\CliMenu\MenuItem\StaticItem;
1110
use PhpSchool\CliMenu\Terminal\TerminalFactory;
12-
use \PhpSchool\CliMenu\Terminal\TerminalInterface;
11+
use PhpSchool\CliMenu\Terminal\TerminalInterface;
12+
use PhpSchool\CliMenu\Util\StringUtil as s;
1313

1414
/**
1515
* Class CliMenu
@@ -295,7 +295,7 @@ protected function drawMenuItem(MenuItemInterface $item, $selected = false)
295295
$setColour,
296296
str_repeat(' ', $this->style->getPadding()),
297297
$row,
298-
str_repeat(' ', $this->style->getRightHandPadding(mb_strlen($row))),
298+
str_repeat(' ', $this->style->getRightHandPadding(mb_strlen(s::stripAnsiEscapeSequence($row)))),
299299
$unsetColour,
300300
str_repeat(' ', $this->style->getMargin())
301301
);

src/CliMenuBuilder.php

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ class CliMenuBuilder
7474
*/
7575
private $disableDefaultItems = false;
7676

77+
/**
78+
* @var bool
79+
*/
80+
private $disabled = false;
81+
7782
/**
7883
* @param CliMenuBuilder|null $parent
7984
*/
@@ -130,13 +135,14 @@ public function addMenuItem(MenuItemInterface $item)
130135
* @param string $text
131136
* @param callable $itemCallable
132137
* @param bool $showItemExtra
138+
* @param bool $disabled
133139
* @return $this
134140
*/
135-
public function addItem($text, callable $itemCallable, $showItemExtra = false)
141+
public function addItem($text, callable $itemCallable, $showItemExtra = false, $disabled = false)
136142
{
137143
Assertion::string($text);
138144

139-
$this->addMenuItem(new SelectableItem($text, $itemCallable, $showItemExtra));
145+
$this->addMenuItem(new SelectableItem($text, $itemCallable, $showItemExtra, $disabled));
140146

141147
return $this;
142148
}
@@ -211,6 +217,32 @@ public function addSubMenu($id)
211217
return $this->subMenus[$id];
212218
}
213219

220+
/**
221+
* Disable a submenu
222+
* @throws \InvalidArgumentException
223+
* @return $this
224+
*/
225+
public function disableMenu()
226+
{
227+
if (!$this->parent) {
228+
throw new \InvalidArgumentException(
229+
'You can\'t disable the root menu'
230+
);
231+
}
232+
233+
$this->disabled = true;
234+
235+
return $this;
236+
}
237+
238+
/**
239+
* @return bool
240+
*/
241+
public function isMenuDisabled()
242+
{
243+
return $this->disabled;
244+
}
245+
214246
/**
215247
* @param string $goBackButtonTest
216248
* @return $this
@@ -462,7 +494,7 @@ private function buildSubMenus(array $items)
462494
$menuBuilder = $this->subMenus[$item];
463495
$this->subMenus[$item] = $menuBuilder->build();
464496

465-
return new MenuMenuItem($item, $this->subMenus[$item]);
497+
return new MenuMenuItem($item, $this->subMenus[$item], $menuBuilder->isMenuDisabled());
466498
}, $items);
467499
}
468500

src/MenuItem/MenuMenuItem.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ class MenuMenuItem implements MenuItemInterface
2323
/**
2424
* @param string $text
2525
* @param CliMenu $subMenu
26+
* @param bool $disabled
2627
*/
27-
public function __construct($text, CliMenu $subMenu)
28+
public function __construct($text, CliMenu $subMenu, $disabled = false)
2829
{
2930
Assertion::string($text);
3031

31-
$this->text = $text;
32-
$this->subMenu = $subMenu;
32+
$this->text = $text;
33+
$this->subMenu = $subMenu;
34+
$this->disabled = $disabled;
3335
}
3436

3537
/**

src/MenuItem/SelectableItem.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@ class SelectableItem implements MenuItemInterface
2323
* @param string $text
2424
* @param callable $selectAction
2525
* @param bool $showItemExtra
26+
* @param bool $disabled
2627
*/
27-
public function __construct($text, callable $selectAction, $showItemExtra = false)
28+
public function __construct($text, callable $selectAction, $showItemExtra = false, $disabled = false)
2829
{
2930
Assertion::string($text);
3031

3132
$this->text = $text;
3233
$this->selectAction = $selectAction;
3334
$this->showItemExtra = (bool) $showItemExtra;
35+
$this->disabled = $disabled;
3436
}
3537

3638
/**

src/MenuItem/SelectableTrait.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@ trait SelectableTrait
1616
/**
1717
* @var string
1818
*/
19-
private $text;
19+
private $text = '';
2020

2121
/**
2222
* @var bool
2323
*/
2424
private $showItemExtra = false;
2525

26+
/**
27+
* @var bool
28+
*/
29+
private $disabled = false;
30+
2631
/**
2732
* The output text for the item
2833
*
@@ -48,13 +53,15 @@ public function getRows(MenuStyle $style, $selected = false)
4853
);
4954

5055
return array_map(function ($row, $key) use ($style, $marker, $length) {
56+
$text = $this->disabled ? $style->getDisabledItemText($row) : $row;
57+
5158
if ($key === 0) {
5259
return $this->showItemExtra
53-
? sprintf('%s%s %s', $row, str_repeat(' ', $length - mb_strlen($row)), $style->getItemExtra())
54-
: $row;
60+
? sprintf('%s%s %s', $text, str_repeat(' ', $length - mb_strlen($row)), $style->getItemExtra())
61+
: $text;
5562
}
5663

57-
return $row;
64+
return $text;
5865
}, $rows, array_keys($rows));
5966
}
6067

@@ -65,7 +72,7 @@ public function getRows(MenuStyle $style, $selected = false)
6572
*/
6673
public function canSelect()
6774
{
68-
return true;
75+
return !$this->disabled;
6976
}
7077

7178
/**

src/MenuStyle.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,11 @@ class MenuStyle
116116
*/
117117
private static $availableOptions = array(
118118
'bold' => array('set' => 1, 'unset' => 22),
119+
'dim' => array('set' => 2, 'unset' => 22),
119120
'underscore' => array('set' => 4, 'unset' => 24),
120121
'blink' => array('set' => 5, 'unset' => 25),
121122
'reverse' => array('set' => 7, 'unset' => 27),
122-
'conceal' => array('set' => 8, 'unset' => 28),
123+
'conceal' => array('set' => 8, 'unset' => 28)
123124
);
124125

125126
/**
@@ -180,6 +181,20 @@ public static function getAvailableColours()
180181
{
181182
return array_keys(self::$availableBackgroundColors);
182183
}
184+
185+
/**
186+
* @param string $text
187+
* @return string
188+
*/
189+
public function getDisabledItemText($text)
190+
{
191+
return sprintf(
192+
"\033[%sm%s\033[%sm",
193+
self::$availableOptions['dim']['set'],
194+
$text,
195+
self::$availableOptions['dim']['unset']
196+
);
197+
}
183198

184199
/**
185200
* Get the colour code set for Bg and Fg

src/Util/StringUtil.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,13 @@ public static function wordwrap($str, $width, $break = "\n")
3333
return $word;
3434
}, explode(" ", $str)));
3535
}
36+
37+
/**
38+
* @param string $str
39+
* @return string
40+
*/
41+
public static function stripAnsiEscapeSequence($str)
42+
{
43+
return preg_replace('/\x1b[^m]*m/', '', $str);
44+
}
3645
}

test/CliMenuBuilderTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,14 @@ public function testDisableDefaultItemsDisablesExitAndGoBackOnSubMenu()
371371

372372
$this->checkVariable($subMenu, 'items', []);
373373
}
374+
375+
public function testThrowsExceptionWhenDisablingRootMenu()
376+
{
377+
$this->expectException(\InvalidArgumentException::class);
378+
$this->expectExceptionMessage('You can\'t disable the root menu');
379+
380+
(new CliMenuBuilder)->disableMenu();
381+
}
374382

375383
/**
376384
* @param CliMenu $menu

0 commit comments

Comments
 (0)