Skip to content

Commit aed1839

Browse files
authored
Merge pull request #231 from php-school/selectable-item-renderer
Selectable item renderer
2 parents d97347e + 7ba829f commit aed1839

File tree

5 files changed

+220
-91
lines changed

5 files changed

+220
-91
lines changed

src/MenuItem/CheckboxItem.php

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use PhpSchool\CliMenu\CliMenu;
66
use PhpSchool\CliMenu\MenuStyle;
7-
use PhpSchool\CliMenu\Util\StringUtil;
87
use PhpSchool\CliMenu\Style\CheckboxStyle;
98
use PhpSchool\CliMenu\Style\ItemStyle;
109

@@ -61,34 +60,7 @@ public function __construct(
6160
*/
6261
public function getRows(MenuStyle $style, bool $selected = false) : array
6362
{
64-
$marker = sprintf("%s", $this->style->getMarker($this, $selected));
65-
66-
$itemExtra = $this->style->getItemExtra();
67-
68-
$length = $this->style->getDisplaysExtra()
69-
? $style->getContentWidth() - (mb_strlen($itemExtra) + 2)
70-
: $style->getContentWidth();
71-
72-
$rows = explode(
73-
"\n",
74-
StringUtil::wordwrap(
75-
sprintf('%s%s', $marker, $this->text),
76-
$length,
77-
sprintf("\n%s", str_repeat(' ', mb_strlen($marker)))
78-
)
79-
);
80-
81-
return array_map(function ($row, $key) use ($style, $length, $itemExtra) {
82-
$text = $this->disabled ? $style->getDisabledItemText($row) : $row;
83-
84-
if ($key === 0) {
85-
return $this->showItemExtra
86-
? sprintf('%s%s %s', $text, str_repeat(' ', $length - mb_strlen($row)), $itemExtra)
87-
: $text;
88-
}
89-
90-
return $text;
91-
}, $rows, array_keys($rows));
63+
return (new SelectableItemRenderer())->render($style, $this, $selected, $this->disabled);
9264
}
9365

9466
/**

src/MenuItem/MenuMenuItem.php

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44

55
use PhpSchool\CliMenu\CliMenu;
66
use PhpSchool\CliMenu\MenuStyle;
7-
use PhpSchool\CliMenu\Util\StringUtil;
87
use PhpSchool\CliMenu\Style\ItemStyle;
98
use PhpSchool\CliMenu\Style\SelectableStyle;
10-
use function PhpSchool\CliMenu\Util\mapWithKeys;
119

1210
/**
1311
* @author Michael Woodward <[email protected]>
@@ -56,37 +54,7 @@ public function __construct(
5654
*/
5755
public function getRows(MenuStyle $style, bool $selected = false) : array
5856
{
59-
$marker = sprintf("%s", $this->style->getMarker($this, $selected));
60-
61-
$length = $this->style->getDisplaysExtra()
62-
? $style->getContentWidth() - (mb_strlen($this->style->getItemExtra()) + 2)
63-
: $style->getContentWidth();
64-
65-
$rows = explode(
66-
"\n",
67-
StringUtil::wordwrap(
68-
sprintf('%s%s', $marker, $this->text),
69-
$length,
70-
sprintf("\n%s", str_repeat(' ', mb_strlen($marker)))
71-
)
72-
);
73-
74-
return mapWithKeys($rows, function (int $key, string $row) use ($style, $length) {
75-
$text = $this->disabled ? $style->getDisabledItemText($row) : $row;
76-
77-
if ($key === 0) {
78-
return $this->showItemExtra
79-
? sprintf(
80-
'%s%s %s',
81-
$text,
82-
str_repeat(' ', $length - mb_strlen($row)),
83-
$this->style->getItemExtra()
84-
)
85-
: $text;
86-
}
87-
88-
return $text;
89-
});
57+
return (new SelectableItemRenderer())->render($style, $this, $selected, $this->disabled);
9058
}
9159

9260
/**

src/MenuItem/RadioItem.php

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use PhpSchool\CliMenu\CliMenu;
66
use PhpSchool\CliMenu\MenuStyle;
7-
use PhpSchool\CliMenu\Util\StringUtil;
87
use PhpSchool\CliMenu\Style\ItemStyle;
98
use PhpSchool\CliMenu\Style\RadioStyle;
109

@@ -61,34 +60,7 @@ public function __construct(
6160
*/
6261
public function getRows(MenuStyle $style, bool $selected = false) : array
6362
{
64-
$marker = sprintf("%s", $this->style->getMarker($this, $selected));
65-
66-
$itemExtra = $this->style->getItemExtra();
67-
68-
$length = $this->style->getDisplaysExtra()
69-
? $style->getContentWidth() - (mb_strlen($itemExtra) + 2)
70-
: $style->getContentWidth();
71-
72-
$rows = explode(
73-
"\n",
74-
StringUtil::wordwrap(
75-
sprintf('%s%s', $marker, $this->text),
76-
$length,
77-
sprintf("\n%s", str_repeat(' ', mb_strlen($marker)))
78-
)
79-
);
80-
81-
return array_map(function ($row, $key) use ($style, $length, $itemExtra) {
82-
$text = $this->disabled ? $style->getDisabledItemText($row) : $row;
83-
84-
if ($key === 0) {
85-
return $this->showItemExtra
86-
? sprintf('%s%s %s', $text, str_repeat(' ', $length - mb_strlen($row)), $itemExtra)
87-
: $text;
88-
}
89-
90-
return $text;
91-
}, $rows, array_keys($rows));
63+
return (new SelectableItemRenderer())->render($style, $this, $selected, $this->disabled);
9264
}
9365

9466
/**
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpSchool\CliMenu\MenuItem;
6+
7+
use PhpSchool\CliMenu\MenuStyle;
8+
use PhpSchool\CliMenu\Style\ItemStyle;
9+
use PhpSchool\CliMenu\Style\Selectable;
10+
use PhpSchool\CliMenu\Util\StringUtil as s;
11+
use function PhpSchool\CliMenu\Util\mapWithKeys;
12+
13+
class SelectableItemRenderer
14+
{
15+
public function render(MenuStyle $menuStyle, MenuItemInterface $item, bool $selected, bool $disabled) : array
16+
{
17+
$itemStyle = $item->getStyle();
18+
$marker = $itemStyle->getMarker($item, $selected);
19+
$availableTextWidth = $this->getAvailableTextWidth($menuStyle, $itemStyle);
20+
21+
return mapWithKeys(
22+
$this->wrapAndIndentText($marker, $item->getText(), $availableTextWidth),
23+
function (int $key, string $row) use ($menuStyle, $itemStyle, $availableTextWidth, $disabled) {
24+
$text = $disabled ? $menuStyle->getDisabledItemText($row) : $row;
25+
26+
return $key === 0 && $itemStyle->getDisplaysExtra()
27+
? $this->lineWithExtra($text, $availableTextWidth, $itemStyle)
28+
: $text;
29+
}
30+
);
31+
}
32+
33+
public function wrapAndIndentText(string $marker, string $text, int $availableWidth) : array
34+
{
35+
return explode(
36+
"\n",
37+
s::wordwrap(
38+
"{$marker}{$text}",
39+
$availableWidth,
40+
sprintf("\n%s", $this->emptyString(mb_strlen($marker)))
41+
)
42+
);
43+
}
44+
45+
public function lineWithExtra(string $text, int $availableWidth, ItemStyle $itemStyle) : string
46+
{
47+
return sprintf(
48+
'%s%s %s',
49+
$text,
50+
$this->emptyString($availableWidth - s::length($text)),
51+
$itemStyle->getItemExtra()
52+
);
53+
}
54+
55+
public function emptyString(int $numCharacters) : string
56+
{
57+
return str_repeat(' ', $numCharacters);
58+
}
59+
60+
public function getAvailableTextWidth(MenuStyle $menuStyle, ItemStyle $itemStyle) : int
61+
{
62+
return $itemStyle->getDisplaysExtra()
63+
? $menuStyle->getContentWidth() - (mb_strlen($itemStyle->getItemExtra()) + 2)
64+
: $menuStyle->getContentWidth();
65+
}
66+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpSchool\CliMenuTest\MenuItem;
6+
7+
use PhpSchool\CliMenu\MenuItem\SelectableItem;
8+
use PhpSchool\CliMenu\MenuItem\SelectableItemRenderer;
9+
use PhpSchool\CliMenu\MenuStyle;
10+
use PhpSchool\CliMenu\Style\SelectableStyle;
11+
use PhpSchool\CliMenuTest\MockTerminal;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class SelectableItemRendererTest extends TestCase
15+
{
16+
public function testRender() : void
17+
{
18+
$renderer = new SelectableItemRenderer();
19+
20+
$menuStyle = new MenuStyle(new MockTerminal);
21+
$menuStyle->setWidth(35);
22+
23+
$style = (new SelectableStyle())->setItemExtra('[DONE]');
24+
25+
$item = new SelectableItem('SOME TEXT', function () {
26+
});
27+
$item->setStyle($style);
28+
29+
self::assertEquals(
30+
[
31+
'● SOME TEXT [DONE]',
32+
],
33+
$renderer->render($menuStyle, $item, true, false)
34+
);
35+
}
36+
public function testRenderMultiLine() : void
37+
{
38+
$renderer = new SelectableItemRenderer();
39+
40+
$menuStyle = new MenuStyle(new MockTerminal);
41+
$menuStyle->setWidth(35);
42+
$style = (new SelectableStyle())->setItemExtra('[DONE]');
43+
44+
$item = new SelectableItem('SOME TEXT THAT IS MUCH LONGER THAN THE AVAILABLE WIDTH', function () {
45+
});
46+
$item->setStyle($style);
47+
48+
self::assertEquals(
49+
[
50+
'● SOME TEXT THAT IS [DONE]',
51+
' MUCH LONGER THAN THE',
52+
' AVAILABLE WIDTH',
53+
],
54+
$renderer->render($menuStyle, $item, true, false)
55+
);
56+
}
57+
public function testRenderUnselected() : void
58+
{
59+
$renderer = new SelectableItemRenderer();
60+
61+
$menuStyle = new MenuStyle(new MockTerminal);
62+
$menuStyle->setWidth(35);
63+
$style = (new SelectableStyle())->setItemExtra('[DONE]');
64+
65+
$item = new SelectableItem('SOME TEXT', function () {
66+
});
67+
$item->setStyle($style);
68+
69+
self::assertEquals(
70+
[
71+
'○ SOME TEXT [DONE]',
72+
],
73+
$renderer->render($menuStyle, $item, false, false)
74+
);
75+
}
76+
public function testRenderDisabled() : void
77+
{
78+
$renderer = new SelectableItemRenderer();
79+
80+
$menuStyle = new MenuStyle(new MockTerminal);
81+
$menuStyle->setWidth(35);
82+
$style = (new SelectableStyle())->setItemExtra('[DONE]');
83+
84+
$item = new SelectableItem('SOME TEXT', function () {
85+
});
86+
$item->setStyle($style);
87+
88+
self::assertEquals(
89+
[
90+
"\033[2m● SOME TEXT\033[22m [DONE]",
91+
],
92+
$renderer->render($menuStyle, $item, true, true)
93+
);
94+
}
95+
public function testWrapAndIndentText() : void
96+
{
97+
$renderer = new SelectableItemRenderer();
98+
99+
$text = 'SOME TEXT THAT IS MUCH LONGER THAN THE AVAILABLE WIDTH';
100+
101+
self::assertEquals(
102+
[
103+
'[ ] SOME TEXT THAT',
104+
' IS MUCH LONGER THAN',
105+
' THE AVAILABLE WIDTH',
106+
],
107+
$renderer->wrapAndIndentText('[ ] ', $text, 20)
108+
);
109+
}
110+
public function testLineWithExtra() : void
111+
{
112+
$renderer = new SelectableItemRenderer();
113+
$style = (new SelectableStyle())->setItemExtra('[DONE]');
114+
115+
self::assertEquals(
116+
'FIRST LINE [DONE]',
117+
$renderer->lineWithExtra('FIRST LINE', 15, $style)
118+
);
119+
}
120+
public function testEmptyString() : void
121+
{
122+
$renderer = new SelectableItemRenderer();
123+
124+
self::assertEquals(' ', $renderer->emptyString(1));
125+
self::assertEquals(' ', $renderer->emptyString(3));
126+
self::assertEquals(' ', $renderer->emptyString(5));
127+
}
128+
public function testGetAvailableTextWidthWithoutExtra() : void
129+
{
130+
$renderer = new SelectableItemRenderer();
131+
132+
$menuStyle = new MenuStyle(new MockTerminal);
133+
$menuStyle->setWidth(100);
134+
135+
$itemStyle = new SelectableStyle();
136+
137+
self::assertEquals(92, $renderer->getAvailableTextWidth($menuStyle, $itemStyle));
138+
}
139+
public function testGetAvailableTextWidthWithExtra() : void
140+
{
141+
$renderer = new SelectableItemRenderer();
142+
143+
$menuStyle = new MenuStyle(new MockTerminal);
144+
$menuStyle->setWidth(100);
145+
146+
$itemStyle = new SelectableStyle();
147+
$itemStyle->setItemExtra('[DONE]');
148+
149+
self::assertEquals(84, $renderer->getAvailableTextWidth($menuStyle, $itemStyle));
150+
}
151+
}

0 commit comments

Comments
 (0)