Skip to content

Commit e31042c

Browse files
committed
Implemented ListRenderer
1 parent a919c4f commit e31042c

File tree

7 files changed

+333
-10
lines changed

7 files changed

+333
-10
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Yii2 multiple input change log
22
==============================
33

4+
2.4.0
5+
=====
6+
7+
- Implemented `ListRenderer`
8+
49
2.3.1
510
=====
611

docs/images/list-rederer.jpg

45.9 KB
Loading

docs/images/table-renderer.jpg

31.9 KB
Loading

docs/renderers.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
##Renderers
22

3-
> Section is under development
3+
Currently widget supports two type of renderers
44

5-
Currently widget supports only `TableRenderer` which renders content in table format.
5+
###TableRenderer
6+
![Table renderer](./images/table-renderer.jpg?raw=true)
7+
8+
This renderer is enabled by default.
9+
10+
###ListRenderer
11+
![List renderer](./images/list-renderer.jpg?raw=true)
12+
13+
To enable this renderer you have to use an option `rendererClass`
14+
```php
15+
<?php
16+
echo $form->field($model, 'schedule')->widget(MultipleInput::className(), [
17+
'rendererClass' => \unclead\multipleinput\renderers\ListRenderer::className(),
18+
'max' => 4,
19+
'allowEmptyList' => true,
20+
'rowOptions' => function($model) {
21+
$options = [];
22+
23+
if ($model['priority'] > 1) {
24+
$options['class'] = 'danger';
25+
}
26+
return $options;
27+
},
28+
```

src/assets/src/css/multiple-input.css

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,43 @@
66
display: inline-block;
77
width: 80%;
88
}
9-
109
.multiple-input-list.no-buttons .multiple-input-list__input {
1110
display: block;
1211
width: 100%;
1312
}
1413
table.multiple-input-list {
1514
margin: 0;
1615
}
17-
table.multiple-input-list tbody tr > td {
16+
table.multiple-input-list.table-renderer tbody tr > td {
1817
border: 0 !important;
1918
}
20-
table.multiple-input-list tr > td:first-child {
19+
table.multiple-input-list.table-renderer tr > td:first-child {
2120
padding-left: 0;
2221
}
23-
table.multiple-input-list tr > td:last-child {
22+
table.multiple-input-list.table-renderer tr > td:last-child {
2423
padding-right: 0;
2524
}
2625
table.multiple-input-list tr > th {
2726
border-bottom: 1px solid #dddddd;
2827
}
29-
.multiple-input-list .form-group {
28+
.multiple-input-list.table-renderer .form-group {
3029
margin: 0 !important;
3130
}
32-
.multiple-input-list__item .label {
31+
.multiple-input-list.list-renderer .form-group {
32+
margin-bottom: 10px !important;
33+
}
34+
.multiple-input-list.table-renderer .multiple-input-list__item .label {
3335
display: block;
3436
font-size: 13px;
3537
}
36-
.multiple-input-list .list-cell__button {
38+
.multiple-input-list.table-renderer .list-cell__button {
3739
width: 40px;
3840
}
41+
.multiple-input-list.list-renderer .list-cell__button {
42+
width: 70px;
43+
text-align: right;
44+
padding-right: 15px;
45+
}
3946
.multiple-input-list__item .radio,
4047
.multiple-input-list__item .checkbox {
4148
margin: 7px 0 7px 0;

src/renderers/ListRenderer.php

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
<?php
2+
3+
/**
4+
* @link https://github.com/unclead/yii2-multiple-input
5+
* @copyright Copyright (c) 2014 unclead
6+
* @license https://github.com/unclead/yii2-multiple-input/blob/master/LICENSE.md
7+
*/
8+
9+
namespace unclead\multipleinput\renderers;
10+
11+
use yii\base\InvalidConfigException;
12+
use yii\db\ActiveRecordInterface;
13+
use yii\helpers\ArrayHelper;
14+
use yii\helpers\Html;
15+
use unclead\multipleinput\components\BaseColumn;
16+
17+
/**
18+
* Class ListRenderer
19+
* @package unclead\multipleinput\renderers
20+
*/
21+
class ListRenderer extends BaseRenderer
22+
{
23+
/**
24+
* @return mixed
25+
*/
26+
protected function internalRender()
27+
{
28+
$content = [];
29+
30+
$content[] = $this->renderHeader();
31+
$content[] = $this->renderBody();
32+
$content[] = $this->renderFooter();
33+
34+
$options = [];
35+
Html::addCssClass($options, 'multiple-input-list list-renderer table form-horizontal');
36+
37+
$content = Html::tag('table', implode("\n", $content), $options);
38+
39+
return Html::tag('div', $content, [
40+
'id' => $this->id,
41+
'class' => 'multiple-input'
42+
]);
43+
}
44+
45+
/**
46+
* Renders the header.
47+
*
48+
* @return string
49+
*/
50+
public function renderHeader()
51+
{
52+
if ($this->min !== 0 || !$this->isAddButtonPositionHeader()) {
53+
return '';
54+
}
55+
56+
$button = $this->isAddButtonPositionHeader() ? $this->renderAddButton() : '';
57+
58+
$content = [];
59+
$content[] = Html::tag('td', '&nbsp;');
60+
$content[] = Html::tag('td', $button, [
61+
'class' => 'list-cell__button',
62+
]);
63+
64+
return Html::tag('thead', Html::tag('tr', implode("\n", $content)));
65+
}
66+
67+
/**
68+
* Renders the footer.
69+
*
70+
* @return string
71+
*/
72+
public function renderFooter()
73+
{
74+
if (!$this->isAddButtonPositionFooter()) {
75+
return '';
76+
}
77+
78+
$cells = [];
79+
$cells[] = Html::tag('td', '&nbsp;');
80+
$cells[] = Html::tag('td', $this->renderAddButton(), [
81+
'class' => 'list-cell__button'
82+
]);
83+
84+
return Html::tag('tfoot', Html::tag('tr', implode("\n", $cells)));
85+
}
86+
87+
/**
88+
* Renders the body.
89+
*
90+
* @return string
91+
* @throws \yii\base\InvalidConfigException
92+
* @throws \yii\base\InvalidParamException
93+
*/
94+
protected function renderBody()
95+
{
96+
$rows = [];
97+
98+
if ($this->data) {
99+
$cnt = count($this->data);
100+
if ($this->min === $this->max && $cnt < $this->max) {
101+
$cnt = $this->max;
102+
}
103+
104+
$indices = array_keys($this->data);
105+
106+
for ($i = 0; $i < $cnt; $i++) {
107+
$index = ArrayHelper::getValue($indices, $i, $i);
108+
$item = ArrayHelper::getValue($this->data, $index, null);
109+
$rows[] = $this->renderRowContent($index, $item);
110+
}
111+
} elseif ($this->min > 0) {
112+
for ($i = 0; $i < $this->min; $i++) {
113+
$rows[] = $this->renderRowContent($i);
114+
}
115+
}
116+
117+
return Html::tag('tbody', implode("\n", $rows));
118+
}
119+
120+
/**
121+
* Renders the row content.
122+
*
123+
* @param int $index
124+
* @param ActiveRecordInterface|array $item
125+
* @return mixed
126+
* @throws InvalidConfigException
127+
*/
128+
private function renderRowContent($index = null, $item = null)
129+
{
130+
$elements = [];
131+
foreach ($this->columns as $column) {
132+
/* @var $column BaseColumn */
133+
$column->setModel($item);
134+
$elements[] = $this->renderCellContent($column, $index);
135+
}
136+
137+
$content = [];
138+
$content[] = Html::tag('td', implode("\n", $elements));
139+
if ($this->max !== $this->min) {
140+
$content[] = $this->renderActionColumn($index);
141+
}
142+
143+
$content = Html::tag('tr', implode("\n", $content), $this->prepareRowOptions($index, $item));
144+
145+
if ($index !== null) {
146+
$content = str_replace('{' . $this->getIndexPlaceholder() . '}', $index, $content);
147+
}
148+
149+
return $content;
150+
}
151+
152+
/**
153+
* Prepares the row options.
154+
*
155+
* @param int $index
156+
* @param ActiveRecordInterface|array $item
157+
* @return array
158+
*/
159+
protected function prepareRowOptions($index, $item)
160+
{
161+
if (is_callable($this->rowOptions)) {
162+
$options = call_user_func($this->rowOptions, $item, $index, $this->context);
163+
} else {
164+
$options = $this->rowOptions;
165+
}
166+
167+
Html::addCssClass($options, 'multiple-input-list__item');
168+
169+
return $options;
170+
}
171+
172+
/**
173+
* Renders the cell content.
174+
*
175+
* @param BaseColumn $column
176+
* @param int|null $index
177+
* @return string
178+
*/
179+
public function renderCellContent($column, $index)
180+
{
181+
$id = $column->getElementId($index);
182+
$name = $column->getElementName($index);
183+
$input = $column->renderInput($name, [
184+
'id' => $id
185+
]);
186+
187+
if ($column->isHiddenInput()) {
188+
return $input;
189+
}
190+
191+
$hasError = false;
192+
$error = '';
193+
194+
if ($index !== null) {
195+
$error = $column->getFirstError($index);
196+
$hasError = !empty($error);
197+
}
198+
199+
if ($column->enableError) {
200+
$input .= "\n" . $column->renderError($error);
201+
}
202+
203+
$wrapperOptions = [
204+
'class' => 'field-' . $id
205+
];
206+
207+
if ($hasError) {
208+
Html::addCssClass($wrapperOptions, 'has-error');
209+
}
210+
211+
$input = Html::tag('div', $input, $wrapperOptions);
212+
213+
$content = Html::beginTag('div', ['class' => 'form-group list-cell__' . $column->name]);
214+
$content .= Html::label($column->title, $id, [
215+
'class' => 'col-sm-2 control-label' . (empty($column->title) ? ' sr-only' : '')
216+
]);
217+
$content .= Html::tag('div', $input, ['class' => 'col-sm-10']);
218+
$content .= Html::endTag('div');
219+
220+
return $content;
221+
}
222+
223+
/**
224+
* Renders the action column.
225+
*
226+
* @param null|int $index
227+
* @return string
228+
* @throws \Exception
229+
*/
230+
private function renderActionColumn($index = null)
231+
{
232+
return Html::tag('td', $this->getActionButton($index), [
233+
'class' => 'list-cell__button',
234+
]);
235+
}
236+
237+
private function getActionButton($index)
238+
{
239+
if ($index === null || $this->min === 0) {
240+
return $this->renderRemoveButton();
241+
}
242+
243+
$index++;
244+
if ($index < $this->min) {
245+
return '';
246+
} elseif ($index === $this->min) {
247+
return $this->isAddButtonPositionRow() ? $this->renderAddButton() : '';
248+
} else {
249+
return $this->renderRemoveButton();
250+
}
251+
}
252+
253+
private function renderAddButton()
254+
{
255+
$options = [
256+
'class' => 'btn multiple-input-list__btn js-input-plus',
257+
];
258+
Html::addCssClass($options, $this->addButtonOptions['class']);
259+
260+
return Html::tag('div', $this->addButtonOptions['label'], $options);
261+
}
262+
263+
/**
264+
* Renders remove button.
265+
*
266+
* @return string
267+
* @throws \Exception
268+
*/
269+
private function renderRemoveButton()
270+
{
271+
$options = [
272+
'class' => 'btn multiple-input-list__btn js-input-remove',
273+
];
274+
Html::addCssClass($options, $this->removeButtonOptions['class']);
275+
276+
return Html::tag('div', $this->removeButtonOptions['label'], $options);
277+
}
278+
279+
/**
280+
* Returns template for using in js.
281+
*
282+
* @return string
283+
*/
284+
protected function prepareTemplate()
285+
{
286+
return $this->renderRowContent();
287+
}
288+
}

src/renderers/TableRenderer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ protected function internalRender()
3535
$content[] = $this->renderFooter();
3636

3737
$options = [];
38-
Html::addCssClass($options, 'multiple-input-list table table-condensed');
38+
Html::addCssClass($options, 'multiple-input-list table table-condensed table-renderer');
3939

4040
$content = Html::tag('table', implode("\n", $content), $options);
4141

0 commit comments

Comments
 (0)