src/html/Field.php
<?php
namespace vps\tools\html;
use Yii;
use vps\tools\helpers\ArrayHelper;
use vps\tools\helpers\Html;
use yii\bootstrap\Collapse;
/**
* @inheritdoc
*/
class Field extends \yii\bootstrap\ActiveField
{
/***
* @inheritdoc
*/
public $options = [ 'class' => 'form-group row' ];
/**
* Renders [datetimepicker](https://github.com/Eonasdan/bootstrap-datetimepicker) input.
*
* @param bool $dateOnly Whether to show only datepicker without time.
* @param array $options
* @return $this
*/
public function datetimepicker ($dateOnly = false, $options = [])
{
$options = array_merge($this->inputOptions, $options);
$this->adjustLabelFor($options);
$options[ 'id' ] = $this->attribute;
$this->parts[ '{input}' ] = Html::activeHiddenInput($this->model, $this->attribute, $options);
$this->parts[ '{input}' ] .= Html::tag('div', '<span class="input-group-addon"><i class="fa fa-calendar"></i></span>' . Html::textInput('', '', [ 'class' => 'form-control', 'tabindex' => '-1' ]), [ 'class' => 'input-group date' . ( $dateOnly ? '' : 'time' ) . 'picker', 'id' => $this->attribute . '-picker' ]);
return $this;
}
/**
* Generates hidden input inside hidden form-group.
*
* @param array $options
* @return $this
*/
public function hidden ($options = [])
{
$this->options[ 'class' ] = ( isset($this->options[ 'class' ]) ? $this->options[ 'class' ] . ' ' : '' ) . 'hide';
$this->parts[ '{input}' ] = Html::activeHiddenInput($this->model, $this->attribute, $options);
return $this;
}
/**
* Prepares data for [Jasny file input plugin](http://www.jasny.net/bootstrap/javascript/#fileinput).
*
* @param null $path Path to the image.
* @param array $options
* @return $this
*/
public function image ($path = null, $options = [])
{
/** @var boolean $fileLoaded - файл уже загружен */
$fileLoaded = ArrayHelper::remove($options, 'fileLoaded', false);
/** @var text $removeText - текст для кнопки "Remove" */
$removeText = ArrayHelper::remove($options, 'removeText', 'Remove');
/** @var text $removeKey - текст для кнопки "Remove" */
$removeKey = ArrayHelper::remove($options, 'removeKey', 0);
/** @var text $removeKey - текст для кнопки "Remove" */
$deleteUrl = ArrayHelper::remove($options, 'deleteUrl', '');
$options = array_merge($this->inputOptions, $options);
$this->adjustLabelFor($options);
$this->parts[ '{input}' ] = Html::tag('div',
Html::tag('div',
Html::tag('div',
$path ? Html::img($path . '?' . time()) : '',
[ 'class' => 'fileinput-preview thumbnail', 'data-trigger' => 'fileinput' ]
)
. Html::tag('div',
Html::tag('div',
Html::tag('span', Yii::tr('Select image'), [ 'class' => 'fileinput-new' ])
. Html::tag('span', Yii::tr('Change'), [ 'class' => 'fileinput-exists' ])
. Html::activeFileInput($this->model, $this->attribute),
[ 'class' => 'btn btn-default btn-file' ]
)
. Html::a(Yii::tr($removeText), '#', [
'class' => 'btn btn-default fileinput-exists delete-loadedfile-ajax',
'data-dismiss' => 'fileinput',
'data-delete-key' => $removeKey,
'data-delete-url' => $deleteUrl,
])
),
[
'class' => 'fileinput' . ( $fileLoaded ? ' fileinput-exists' : ' fileinput-new' ),
'data-provides' => 'fileinput'
]
),
[ 'class' => 'image' ]
);
$this->enableLabel = false;
return $this;
}
/**
* This function overrides error output. All errors are displayed.
*
* @inheritdoc
*/
public function render ($content = null)
{
// Custom error output.
$errors = $this->model->getErrors($this->attribute);
if (count($errors) == 0)
$this->parts[ '{error}' ] = '';
else
{
$class = isset($this->errorOptions[ 'class' ]) ? ' class="' . $this->errorOptions[ 'class' ] . '"' : '';
$this->parts[ '{error}' ] = '<div' . $class . '><ul class="list-unstyled">';
foreach ($errors as $e)
{
$this->parts[ '{error}' ] .= '<li>' . $e . '</li>';
}
$this->parts[ '{error}' ] .= '</ul></div>';
}
return parent::render($content);
}
/**
* Prepares data to be shown as [bootstrap select plugin](https://github.com/silviomoreto/bootstrap-select).
*
* @param array $models Array of models to be inserted in option tag.
* @param array $listOptions Options for 'option' tag. The following options are
* * value: string, attribute name to use for `<option>` value, default is 'id';
* * label: string, attribute name to put for `<option>` text;
* * data-content: string, attribute name to put in 'data-content' attribute for
* `<option>`, will overwrite label if set;
* * title: string, attribute name to put for title attribute for `<option>`.
* @param array $options
* @return $this
*/
public function select ($models, $listOptions, $options = [])
{
$value = isset($listOptions[ 'value' ]) ? $listOptions[ 'value' ] : 'id';
$label = isset($listOptions[ 'label' ]) ? $listOptions[ 'label' ] : false;
$content = isset($listOptions[ 'data-content' ]) ? $listOptions[ 'data-content' ] : false;
$title = isset($listOptions[ 'title' ]) ? $listOptions[ 'title' ] : false;
$items = [];
foreach ($models as $model)
$items[ $model->$value ] = $label ? $model->$label : '';
// $options[ 'options' ] = [];
if (isset($options[ 'selected' ]))
{
foreach ($options[ 'selected' ] as $id)
$options[ 'options' ][ $id ][ 'selected' ] = 'selected';
}
if ($title)
foreach ($models as $model)
$options[ 'options' ][ $model->$value ][ 'title' ] = $model->$title;
if ($content)
{
preg_match_all('/\{([\w]+)\}/', $content, $matches);
foreach ($models as $model)
{
$tr = [];
foreach ($matches[ 0 ] as $i => $match)
{
$var = $matches[ 1 ][ $i ];
$tr[ $match ] = $model->$var;
}
$options[ 'options' ][ $model->$value ][ 'data-content' ] = strtr($content, $tr);
}
}
return $this->dropDownList($items, $options);
}
/**
* Renders [sortable lists](https://github.com/rubaxa/Sortable) for selecting multiple data with order. It
* contains left and right blocks with draggable items between them.
*
* @param array $left Items for left block (selected items).
* @param array $right Items for right block.
* @param array $options
* @return $this
* @throws \yii\base\InvalidConfigException
*/
public function sortable ($left, $right, $options = [])
{
$options = array_merge($this->inputOptions, $options);
$this->adjustLabelFor($options);
$options[ 'value' ] = '';
$html = Html::activeHiddenInput($this->model, $this->attribute, $options);
$leftBlock = Html::listGroupOrder($left, [
'class' => 'sortable list-group-sm' . ( count($left) > 0 ? '' : ' empty' ),
'orderClass' => 'info'
]);
if (key($right) === 0)
{
$rightBlock = Html::listGroupOrder($right, [ 'class' => 'sortable list-group-sm', 'title' => 'guid' ]);
}
else
{
$collapse = new Collapse;
$items = [];
foreach ($right as $label => $data)
{
$items[] = [
'label' => $label,
'content' => Html::listGroupOrder($data, [ 'class' => 'sortable list-group-sm', 'title' => 'guid' ]),
'options' => [ 'class' => 'panel-sortable' ]
];
}
$collapse->items = $items;
$rightBlock = $collapse->renderItems();
}
$this->parts[ '{input}' ] = $html . '<div class="row sortable-' . $this->attribute . '"><div class="col-md-6 sortable-left">' . $leftBlock . '</div><div class="col-md-6 sortable-right">' . $rightBlock . '</div></div>';
return $this;
}
/**
* Renders submit button.
*
* @param string $text
* @param array $options
* @return $this
*/
public function submit ($text, $options = [])
{
$this->label(false);
$this->parts[ '{input}' ] = Html::submitButton($text, [ 'class' => 'btn btn-lg btn-primary', 'name' => 's-' . $this->attribute ]);
return $this;
}
/**
* Renders [tagsinput](https://github.com/bootstrap-tagsinput/bootstrap-tagsinput).
*
* @param array $options
* @return $this
*/
public function tags ($options = [])
{
$options[ 'data-role' ] = 'tagsinput';
$this->adjustLabelFor($options);
if (!isset($options[ 'id' ]))
$options[ 'id' ] = $this->getInputId();
$model = $this->model;
$attribute = $this->attribute;
$data = $model->$attribute;
$value = '';
if (is_array($data))
{
if (count($data) > 0)
{
if (is_object($data[ 0 ]))
$value = implode(',', ArrayHelper::objectsAttribute($data, 'title'));
else
$value = implode(',', $data);
}
}
else
$value = $data;
$this->parts[ '{input}' ] = Html::textInput(Html::getInputName($model, $attribute), $value, $options);
return $this;
}
/**
* Added translation for label title.
*
* @inheritdoc
*/
protected function renderLabelParts ($label = null, $options = [])
{
parent::renderLabelParts($label, $options);
if (!empty($this->parts[ '{labelTitle}' ]))
$this->parts[ '{labelTitle}' ] = Yii::tr($this->parts[ '{labelTitle}' ]);
}
}