This commit is contained in:
2020-02-01 16:47:12 +07:00
commit 4c619ad6e6
16739 changed files with 3329179 additions and 0 deletions

View File

@@ -0,0 +1,423 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\helpers\ArrayHelper;
/**
* A Bootstrap 3 enhanced version of [[\yii\widgets\ActiveField]].
*
* This class adds some useful features to [[\yii\widgets\ActiveField|ActiveField]] to render all
* sorts of Bootstrap 3 form fields in different form layouts:
*
* - [[inputTemplate]] is an optional template to render complex inputs, for example input groups
* - [[horizontalCssClasses]] defines the CSS grid classes to add to label, wrapper, error and hint
* in horizontal forms
* - [[inline]]/[[inline()]] is used to render inline [[checkboxList()]] and [[radioList()]]
* - [[enableError]] can be set to `false` to disable to the error
* - [[enableLabel]] can be set to `false` to disable to the label
* - [[label()]] can be used with a `bool` argument to enable/disable the label
*
* There are also some new placeholders that you can use in the [[template]] configuration:
*
* - `{beginLabel}`: the opening label tag
* - `{labelTitle}`: the label title for use with `{beginLabel}`/`{endLabel}`
* - `{endLabel}`: the closing label tag
* - `{beginWrapper}`: the opening wrapper tag
* - `{endWrapper}`: the closing wrapper tag
*
* The wrapper tag is only used for some layouts and form elements.
*
* Note that some elements use slightly different defaults for [[template]] and other options.
* You may want to override those predefined templates for checkboxes, radio buttons, checkboxLists
* and radioLists in the [[\yii\widgets\ActiveForm::fieldConfig|fieldConfig]] of the
* [[\yii\widgets\ActiveForm]]:
*
* - [[checkboxTemplate]] the template for checkboxes in default layout
* - [[radioTemplate]] the template for radio buttons in default layout
* - [[horizontalCheckboxTemplate]] the template for checkboxes in horizontal layout
* - [[horizontalRadioTemplate]] the template for radio buttons in horizontal layout
* - [[inlineCheckboxListTemplate]] the template for inline checkboxLists
* - [[inlineRadioListTemplate]] the template for inline radioLists
*
* Example:
*
* ```php
* use yii\bootstrap\ActiveForm;
*
* $form = ActiveForm::begin(['layout' => 'horizontal']);
*
* // Form field without label
* echo $form->field($model, 'demo', [
* 'inputOptions' => [
* 'placeholder' => $model->getAttributeLabel('demo'),
* ],
* ])->label(false);
*
* // Inline radio list
* echo $form->field($model, 'demo')->inline()->radioList($items);
*
* // Control sizing in horizontal mode
* echo $form->field($model, 'demo', [
* 'horizontalCssClasses' => [
* 'wrapper' => 'col-sm-2',
* ]
* ]);
*
* // With 'default' layout you would use 'template' to size a specific field:
* echo $form->field($model, 'demo', [
* 'template' => '{label} <div class="row"><div class="col-sm-4">{input}{error}{hint}</div></div>'
* ]);
*
* // Input group
* echo $form->field($model, 'demo', [
* 'inputTemplate' => '<div class="input-group"><span class="input-group-addon">@</span>{input}</div>',
* ]);
*
* ActiveForm::end();
* ```
*
* @see \yii\bootstrap\ActiveForm
* @see http://getbootstrap.com/css/#forms
*
* @author Michael Härtl <haertl.mike@gmail.com>
* @since 2.0
*/
class ActiveField extends \yii\widgets\ActiveField
{
/**
* @var bool whether to render [[checkboxList()]] and [[radioList()]] inline.
*/
public $inline = false;
/**
* @var string|null optional template to render the `{input}` placeholder content
*/
public $inputTemplate;
/**
* @var array options for the wrapper tag, used in the `{beginWrapper}` placeholder
*/
public $wrapperOptions = [];
/**
* @var null|array CSS grid classes for horizontal layout. This must be an array with these keys:
* - 'offset' the offset grid class to append to the wrapper if no label is rendered
* - 'label' the label grid class
* - 'wrapper' the wrapper grid class
* - 'error' the error grid class
* - 'hint' the hint grid class
*/
public $horizontalCssClasses = [];
/**
* @var string the template for checkboxes in default layout
*/
public $checkboxTemplate = "<div class=\"checkbox\">\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n{error}\n{hint}\n</div>";
/**
* @var string the template for radios in default layout
*/
public $radioTemplate = "<div class=\"radio\">\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n{error}\n{hint}\n</div>";
/**
* @var string the template for checkboxes in horizontal layout
*/
public $horizontalCheckboxTemplate = "{beginWrapper}\n<div class=\"checkbox\">\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n</div>\n{error}\n{endWrapper}\n{hint}";
/**
* @var string the template for radio buttons in horizontal layout
*/
public $horizontalRadioTemplate = "{beginWrapper}\n<div class=\"radio\">\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n</div>\n{error}\n{endWrapper}\n{hint}";
/**
* @var string the template for inline checkboxLists
*/
public $inlineCheckboxListTemplate = "{label}\n{beginWrapper}\n{input}\n{error}\n{endWrapper}\n{hint}";
/**
* @var string the template for inline radioLists
*/
public $inlineRadioListTemplate = "{label}\n{beginWrapper}\n{input}\n{error}\n{endWrapper}\n{hint}";
/**
* @var bool whether to render the error. Default is `true` except for layout `inline`.
*/
public $enableError = true;
/**
* @var bool whether to render the label. Default is `true`.
*/
public $enableLabel = true;
/**
* {@inheritdoc}
*/
public function __construct($config = [])
{
$layoutConfig = $this->createLayoutConfig($config);
$config = ArrayHelper::merge($layoutConfig, $config);
parent::__construct($config);
}
/**
* {@inheritdoc}
*/
public function render($content = null)
{
if ($content === null) {
if (!isset($this->parts['{beginWrapper}'])) {
$options = $this->wrapperOptions;
$tag = ArrayHelper::remove($options, 'tag', 'div');
$this->parts['{beginWrapper}'] = Html::beginTag($tag, $options);
$this->parts['{endWrapper}'] = Html::endTag($tag);
}
if ($this->enableLabel === false) {
$this->parts['{label}'] = '';
$this->parts['{beginLabel}'] = '';
$this->parts['{labelTitle}'] = '';
$this->parts['{endLabel}'] = '';
} elseif (!isset($this->parts['{beginLabel}'])) {
$this->renderLabelParts();
}
if ($this->enableError === false) {
$this->parts['{error}'] = '';
}
if ($this->inputTemplate) {
$input = isset($this->parts['{input}']) ?
$this->parts['{input}'] : Html::activeTextInput($this->model, $this->attribute, $this->inputOptions);
$this->parts['{input}'] = strtr($this->inputTemplate, ['{input}' => $input]);
}
}
return parent::render($content);
}
/**
* {@inheritdoc}
*/
public function checkbox($options = [], $enclosedByLabel = true)
{
if ($enclosedByLabel) {
if (!isset($options['template'])) {
$this->template = $this->form->layout === 'horizontal' ?
$this->horizontalCheckboxTemplate : $this->checkboxTemplate;
} else {
$this->template = $options['template'];
unset($options['template']);
}
if (isset($options['label'])) {
$this->parts['{labelTitle}'] = $options['label'];
}
if ($this->form->layout === 'horizontal') {
Html::addCssClass($this->wrapperOptions, $this->horizontalCssClasses['offset']);
}
$this->labelOptions['class'] = null;
}
return parent::checkbox($options, false);
}
/**
* {@inheritdoc}
*/
public function radio($options = [], $enclosedByLabel = true)
{
if ($enclosedByLabel) {
if (!isset($options['template'])) {
$this->template = $this->form->layout === 'horizontal' ?
$this->horizontalRadioTemplate : $this->radioTemplate;
} else {
$this->template = $options['template'];
unset($options['template']);
}
if (isset($options['label'])) {
$this->parts['{labelTitle}'] = $options['label'];
}
if ($this->form->layout === 'horizontal') {
Html::addCssClass($this->wrapperOptions, $this->horizontalCssClasses['offset']);
}
$this->labelOptions['class'] = null;
}
return parent::radio($options, false);
}
/**
* {@inheritdoc}
*/
public function checkboxList($items, $options = [])
{
if ($this->inline) {
if (!isset($options['template'])) {
$this->template = $this->inlineCheckboxListTemplate;
} else {
$this->template = $options['template'];
unset($options['template']);
}
if (!isset($options['itemOptions'])) {
$options['itemOptions'] = [
'labelOptions' => ['class' => 'checkbox-inline'],
];
}
} elseif (!isset($options['item'])) {
$itemOptions = isset($options['itemOptions']) ? $options['itemOptions'] : [];
$encode = ArrayHelper::getValue($options, 'encode', true);
$options['item'] = function ($index, $label, $name, $checked, $value) use ($itemOptions, $encode) {
$options = array_merge([
'label' => $encode ? Html::encode($label) : $label,
'value' => $value
], $itemOptions);
return '<div class="checkbox">' . Html::checkbox($name, $checked, $options) . '</div>';
};
}
parent::checkboxList($items, $options);
return $this;
}
/**
* {@inheritdoc}
*/
public function radioList($items, $options = [])
{
if ($this->inline) {
if (!isset($options['template'])) {
$this->template = $this->inlineRadioListTemplate;
} else {
$this->template = $options['template'];
unset($options['template']);
}
if (!isset($options['itemOptions'])) {
$options['itemOptions'] = [
'labelOptions' => ['class' => 'radio-inline'],
];
}
} elseif (!isset($options['item'])) {
$itemOptions = isset($options['itemOptions']) ? $options['itemOptions'] : [];
$encode = ArrayHelper::getValue($options, 'encode', true);
$options['item'] = function ($index, $label, $name, $checked, $value) use ($itemOptions, $encode) {
$options = array_merge([
'label' => $encode ? Html::encode($label) : $label,
'value' => $value
], $itemOptions);
return '<div class="radio">' . Html::radio($name, $checked, $options) . '</div>';
};
}
parent::radioList($items, $options);
return $this;
}
/**
* Renders Bootstrap static form control.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. There are also a special options:
*
* - encode: bool, whether value should be HTML-encoded or not.
*
* @return $this the field object itself
* @since 2.0.5
* @see http://getbootstrap.com/css/#forms-controls-static
*/
public function staticControl($options = [])
{
$this->adjustLabelFor($options);
$this->parts['{input}'] = Html::activeStaticControl($this->model, $this->attribute, $options);
return $this;
}
/**
* {@inheritdoc}
*/
public function label($label = null, $options = [])
{
if (is_bool($label)) {
$this->enableLabel = $label;
if ($label === false && $this->form->layout === 'horizontal') {
Html::addCssClass($this->wrapperOptions, $this->horizontalCssClasses['offset']);
}
} else {
$this->enableLabel = true;
$this->renderLabelParts($label, $options);
parent::label($label, $options);
}
return $this;
}
/**
* @param bool $value whether to render a inline list
* @return $this the field object itself
* Make sure you call this method before [[checkboxList()]] or [[radioList()]] to have any effect.
*/
public function inline($value = true)
{
$this->inline = (bool) $value;
return $this;
}
/**
* @param array $instanceConfig the configuration passed to this instance's constructor
* @return array the layout specific default configuration for this instance
*/
protected function createLayoutConfig($instanceConfig)
{
$config = [
'hintOptions' => [
'tag' => 'p',
'class' => 'help-block',
],
'errorOptions' => [
'tag' => 'p',
'class' => 'help-block help-block-error',
],
'inputOptions' => [
'class' => 'form-control',
],
];
$layout = $instanceConfig['form']->layout;
if ($layout === 'horizontal') {
$config['template'] = "{label}\n{beginWrapper}\n{input}\n{error}\n{endWrapper}\n{hint}";
$cssClasses = array_merge([
'offset' => 'col-sm-offset-3',
'label' => 'col-sm-3',
'wrapper' => 'col-sm-6',
'error' => '',
'hint' => 'col-sm-3',
], $this->horizontalCssClasses);
if (isset($instanceConfig['horizontalCssClasses'])) {
$cssClasses = ArrayHelper::merge($cssClasses, $instanceConfig['horizontalCssClasses']);
}
$config['horizontalCssClasses'] = $cssClasses;
$config['wrapperOptions'] = ['class' => $cssClasses['wrapper']];
$config['labelOptions'] = ['class' => 'control-label ' . $cssClasses['label']];
$config['errorOptions']['class'] = 'help-block help-block-error ' . $cssClasses['error'];
$config['hintOptions']['class'] = 'help-block ' . $cssClasses['hint'];
} elseif ($layout === 'inline') {
$config['labelOptions'] = ['class' => 'sr-only'];
$config['enableError'] = false;
}
return $config;
}
/**
* @param string|null $label the label or null to use model label
* @param array $options the tag options
*/
protected function renderLabelParts($label = null, $options = [])
{
$options = array_merge($this->labelOptions, $options);
if ($label === null) {
if (isset($options['label'])) {
$label = $options['label'];
unset($options['label']);
} else {
$attribute = Html::getAttributeName($this->attribute);
$label = Html::encode($this->model->getAttributeLabel($attribute));
}
}
if (!isset($options['for'])) {
$options['for'] = Html::getInputId($this->model, $this->attribute);
}
$this->parts['{beginLabel}'] = Html::beginTag('label', $options);
$this->parts['{endLabel}'] = Html::endTag('label');
if (!isset($this->parts['{labelTitle}'])) {
$this->parts['{labelTitle}'] = $label;
}
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use Yii;
use yii\base\InvalidConfigException;
/**
* A Bootstrap 3 enhanced version of [[\yii\widgets\ActiveForm]].
*
* This class mainly adds the [[layout]] property to choose a Bootstrap 3 form layout.
* So for example to render a horizontal form you would:
*
* ```php
* use yii\bootstrap\ActiveForm;
*
* $form = ActiveForm::begin(['layout' => 'horizontal'])
* ```
*
* This will set default values for the [[ActiveField]]
* to render horizontal form fields. In particular the [[ActiveField::template|template]]
* is set to `{label} {beginWrapper} {input} {error} {endWrapper} {hint}` and the
* [[ActiveField::horizontalCssClasses|horizontalCssClasses]] are set to:
*
* ```php
* [
* 'offset' => 'col-sm-offset-3',
* 'label' => 'col-sm-3',
* 'wrapper' => 'col-sm-6',
* 'error' => '',
* 'hint' => 'col-sm-3',
* ]
* ```
*
* To get a different column layout in horizontal mode you can modify those options
* through [[fieldConfig]]:
*
* ```php
* $form = ActiveForm::begin([
* 'layout' => 'horizontal',
* 'fieldConfig' => [
* 'template' => "{label}\n{beginWrapper}\n{input}\n{hint}\n{error}\n{endWrapper}",
* 'horizontalCssClasses' => [
* 'label' => 'col-sm-4',
* 'offset' => 'col-sm-offset-4',
* 'wrapper' => 'col-sm-8',
* 'error' => '',
* 'hint' => '',
* ],
* ],
* ]);
* ```
*
* @see ActiveField for details on the [[fieldConfig]] options
* @see http://getbootstrap.com/css/#forms
*
* @author Michael Härtl <haertl.mike@gmail.com>
* @since 2.0
*/
class ActiveForm extends \yii\widgets\ActiveForm
{
/**
* @var string the default field class name when calling [[field()]] to create a new field.
* @see fieldConfig
*/
public $fieldClass = 'yii\bootstrap\ActiveField';
/**
* @var array HTML attributes for the form tag. Default is `[]`.
*/
public $options = [];
/**
* @var string the form layout. Either 'default', 'horizontal' or 'inline'.
* By choosing a layout, an appropriate default field configuration is applied. This will
* render the form fields with slightly different markup for each layout. You can
* override these defaults through [[fieldConfig]].
* @see \yii\bootstrap\ActiveField for details on Bootstrap 3 field configuration
*/
public $layout = 'default';
/**
* {@inheritdoc}
*/
public function init()
{
if (!in_array($this->layout, ['default', 'horizontal', 'inline'])) {
throw new InvalidConfigException('Invalid layout type: ' . $this->layout);
}
if ($this->layout !== 'default') {
Html::addCssClass($this->options, 'form-' . $this->layout);
}
parent::init();
}
/**
* {@inheritdoc}
* @return ActiveField the created ActiveField object
*/
public function field($model, $attribute, $options = [])
{
return parent::field($model, $attribute, $options);
}
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use Yii;
use yii\helpers\ArrayHelper;
/**
* Alert renders an alert bootstrap component.
*
* For example,
*
* ```php
* echo Alert::widget([
* 'options' => [
* 'class' => 'alert-info',
* ],
* 'body' => 'Say hello...',
* ]);
* ```
*
* The following example will show the content enclosed between the [[begin()]]
* and [[end()]] calls within the alert box:
*
* ```php
* Alert::begin([
* 'options' => [
* 'class' => 'alert-warning',
* ],
* ]);
*
* echo 'Say hello...';
*
* Alert::end();
* ```
*
* @see http://getbootstrap.com/components/#alerts
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class Alert extends Widget
{
/**
* @var string the body content in the alert component. Note that anything between
* the [[begin()]] and [[end()]] calls of the Alert widget will also be treated
* as the body content, and will be rendered before this.
*/
public $body;
/**
* @var array|false the options for rendering the close button tag.
* The close button is displayed in the header of the modal window. Clicking
* on the button will hide the modal window. If this is false, no close button will be rendered.
*
* The following special options are supported:
*
* - tag: string, the tag name of the button. Defaults to 'button'.
* - label: string, the label of the button. Defaults to '&times;'.
*
* The rest of the options will be rendered as the HTML attributes of the button tag.
* Please refer to the [Alert documentation](http://getbootstrap.com/components/#alerts)
* for the supported HTML attributes.
*/
public $closeButton = [];
/**
* Initializes the widget.
*/
public function init()
{
parent::init();
$this->initOptions();
echo Html::beginTag('div', $this->options) . "\n";
echo $this->renderBodyBegin() . "\n";
}
/**
* Renders the widget.
*/
public function run()
{
echo "\n" . $this->renderBodyEnd();
echo "\n" . Html::endTag('div');
$this->registerPlugin('alert');
}
/**
* Renders the close button if any before rendering the content.
* @return string the rendering result
*/
protected function renderBodyBegin()
{
return $this->renderCloseButton();
}
/**
* Renders the alert body (if any).
* @return string the rendering result
*/
protected function renderBodyEnd()
{
return $this->body . "\n";
}
/**
* Renders the close button.
* @return string the rendering result
*/
protected function renderCloseButton()
{
if (($closeButton = $this->closeButton) !== false) {
$tag = ArrayHelper::remove($closeButton, 'tag', 'button');
$label = ArrayHelper::remove($closeButton, 'label', '&times;');
if ($tag === 'button' && !isset($closeButton['type'])) {
$closeButton['type'] = 'button';
}
return Html::tag($tag, $label, $closeButton);
} else {
return null;
}
}
/**
* Initializes the widget options.
* This method sets the default values for various options.
*/
protected function initOptions()
{
Html::addCssClass($this->options, ['alert', 'fade', 'in']);
if ($this->closeButton !== false) {
$this->closeButton = array_merge([
'data-dismiss' => 'alert',
'aria-hidden' => 'true',
'class' => 'close',
], $this->closeButton);
}
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\helpers\ArrayHelper;
/**
* BaseHtml provides concrete implementation for [[Html]].
*
* Do not use BaseHtml. Use [[Html]] instead.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0.5
*/
class BaseHtml extends \yii\helpers\Html
{
/**
* Composes icon HTML for bootstrap Glyphicons.
* @param string $name icon short name, for example: 'star'
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. There are also a special options:
*
* - tag: string, tag to be rendered, by default 'span' is used.
* - prefix: string, prefix which should be used to compose tag class, by default 'glyphicon glyphicon-' is used.
*
* @return string icon HTML.
* @see http://getbootstrap.com/components/#glyphicons
*/
public static function icon($name, $options = [])
{
$tag = ArrayHelper::remove($options, 'tag', 'span');
$classPrefix = ArrayHelper::remove($options, 'prefix', 'glyphicon glyphicon-');
static::addCssClass($options, $classPrefix . $name);
return static::tag($tag, '', $options);
}
/**
* Renders Bootstrap static form control.
*
* By default value will be HTML-encoded using [[encode()]], you may control this behavior
* via 'encode' option.
* @param string $value static control value.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. There are also a special options:
*
* - encode: bool, whether value should be HTML-encoded or not.
*
* @return string generated HTML
* @see http://getbootstrap.com/css/#forms-controls-static
*/
public static function staticControl($value, $options = [])
{
static::addCssClass($options, 'form-control-static');
$value = (string) $value;
if (isset($options['encode'])) {
$encode = $options['encode'];
unset($options['encode']);
} else {
$encode = true;
}
return static::tag('p', $encode ? static::encode($value) : $value, $options);
}
/**
* Generates a Bootstrap static form control for the given model attribute.
* @param \yii\base\Model $model the model object.
* @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
* about attribute expression.
* @param array $options the tag options in terms of name-value pairs. See [[staticControl()]] for details.
* @return string generated HTML
* @see staticControl()
*/
public static function activeStaticControl($model, $attribute, $options = [])
{
if (isset($options['value'])) {
$value = $options['value'];
unset($options['value']);
} else {
$value = static::getAttributeValue($model, $attribute);
}
return static::staticControl($value, $options);
}
/**
* {@inheritdoc}
* @since 2.0.8
*/
public static function radioList($name, $selection = null, $items = [], $options = [])
{
if (!isset($options['item'])) {
$itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
$encode = ArrayHelper::getValue($options, 'encode', true);
$options['item'] = function ($index, $label, $name, $checked, $value) use ($itemOptions, $encode) {
$options = array_merge([
'label' => $encode ? static::encode($label) : $label,
'value' => $value
], $itemOptions);
return '<div class="radio">' . static::radio($name, $checked, $options) . '</div>';
};
}
return parent::radioList($name, $selection, $items, $options);
}
/**
* {@inheritdoc}
* @since 2.0.8
*/
public static function checkboxList($name, $selection = null, $items = [], $options = [])
{
if (!isset($options['item'])) {
$itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
$encode = ArrayHelper::getValue($options, 'encode', true);
$options['item'] = function ($index, $label, $name, $checked, $value) use ($itemOptions, $encode) {
$options = array_merge([
'label' => $encode ? static::encode($label) : $label,
'value' => $value
], $itemOptions);
return '<div class="checkbox">' . Html::checkbox($name, $checked, $options) . '</div>';
};
}
return parent::checkboxList($name, $selection, $items, $options);
}
/**
* {@inheritdoc}
* @since 2.0.8
*/
public static function error($model, $attribute, $options = [])
{
if (!array_key_exists('tag', $options)) {
$options['tag'] = 'p';
}
if (!array_key_exists('class', $options)) {
$options['class'] = 'help-block help-block-error';
}
return parent::error($model, $attribute, $options);
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\web\AssetBundle;
/**
* Asset bundle for the Twitter bootstrap css files.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class BootstrapAsset extends AssetBundle
{
public $sourcePath = '@bower/bootstrap/dist';
public $css = [
'css/bootstrap.css',
];
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\web\AssetBundle;
/**
* Asset bundle for the Twitter bootstrap javascript files.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class BootstrapPluginAsset extends AssetBundle
{
public $sourcePath = '@bower/bootstrap/dist';
public $js = [
'js/bootstrap.js',
];
public $depends = [
'yii\web\JqueryAsset',
'yii\bootstrap\BootstrapAsset',
];
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\web\AssetBundle;
/**
* Asset bundle for the Twitter bootstrap default theme.
*
* @author Alexander Makarov <sam@rmcreative.ru>
* @since 2.0
*/
class BootstrapThemeAsset extends AssetBundle
{
public $sourcePath = '@bower/bootstrap/dist';
public $css = [
'css/bootstrap-theme.css',
];
public $depends = [
'yii\bootstrap\BootstrapAsset',
];
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use Yii;
use yii\helpers\Json;
/**
* BootstrapWidgetTrait is the trait, which provides basic for all bootstrap widgets features.
*
* Note: class, which uses this trait must declare public field named `options` with the array default value:
*
* ```php
* class MyWidget extends \yii\base\Widget
* {
* use BootstrapWidgetTrait;
*
* public $options = [];
* }
* ```
*
* This field is not present in the trait in order to avoid possible PHP Fatal error on definition conflict.
*
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0.6
*/
trait BootstrapWidgetTrait
{
/**
* @var array the options for the underlying Bootstrap JS plugin.
* Please refer to the corresponding Bootstrap plugin Web page for possible options.
* For example, [this page](http://getbootstrap.com/javascript/#modals) shows
* how to use the "Modal" plugin and the supported options (e.g. "remote").
*/
public $clientOptions = [];
/**
* @var array the event handlers for the underlying Bootstrap JS plugin.
* Please refer to the corresponding Bootstrap plugin Web page for possible events.
* For example, [this page](http://getbootstrap.com/javascript/#modals) shows
* how to use the "Modal" plugin and the supported events (e.g. "shown").
*/
public $clientEvents = [];
/**
* Initializes the widget.
* This method will register the bootstrap asset bundle. If you override this method,
* make sure you call the parent implementation first.
*/
public function init()
{
parent::init();
if (!isset($this->options['id'])) {
$this->options['id'] = $this->getId();
}
}
/**
* Registers a specific Bootstrap plugin and the related events
* @param string $name the name of the Bootstrap plugin
*/
protected function registerPlugin($name)
{
$view = $this->getView();
BootstrapPluginAsset::register($view);
$id = $this->options['id'];
if ($this->clientOptions !== false) {
$options = empty($this->clientOptions) ? '' : Json::htmlEncode($this->clientOptions);
$js = "jQuery('#$id').$name($options);";
$view->registerJs($js);
}
$this->registerClientEvents();
}
/**
* Registers JS event handlers that are listed in [[clientEvents]].
* @since 2.0.2
*/
protected function registerClientEvents()
{
if (!empty($this->clientEvents)) {
$id = $this->options['id'];
$js = [];
foreach ($this->clientEvents as $event => $handler) {
$js[] = "jQuery('#$id').on('$event', $handler);";
}
$this->getView()->registerJs(implode("\n", $js));
}
}
/**
* @return \yii\web\View the view object that can be used to render views or view files.
* @see \yii\base\Widget::getView()
*/
abstract function getView();
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
/**
* Button renders a bootstrap button.
*
* For example,
*
* ```php
* echo Button::widget([
* 'label' => 'Action',
* 'options' => ['class' => 'btn-lg'],
* ]);
* ```
* @see http://getbootstrap.com/javascript/#buttons
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class Button extends Widget
{
/**
* @var string the tag to use to render the button
*/
public $tagName = 'button';
/**
* @var string the button label
*/
public $label = 'Button';
/**
* @var bool whether the label should be HTML-encoded.
*/
public $encodeLabel = true;
/**
* Initializes the widget.
* If you override this method, make sure you call the parent implementation first.
*/
public function init()
{
parent::init();
$this->clientOptions = false;
Html::addCssClass($this->options, ['widget' => 'btn']);
}
/**
* Renders the widget.
*/
public function run()
{
$this->registerPlugin('button');
return Html::tag($this->tagName, $this->encodeLabel ? Html::encode($this->label) : $this->label, $this->options);
}
}

View File

@@ -0,0 +1,160 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\helpers\ArrayHelper;
use yii\helpers\Url;
/**
* ButtonDropdown renders a group or split button dropdown bootstrap component.
*
* For example,
*
* ```php
* // a button group using Dropdown widget
* echo ButtonDropdown::widget([
* 'label' => 'Action',
* 'dropdown' => [
* 'items' => [
* ['label' => 'DropdownA', 'url' => '/'],
* ['label' => 'DropdownB', 'url' => '#'],
* ],
* ],
* ]);
* ```
* @see http://getbootstrap.com/javascript/#buttons
* @see http://getbootstrap.com/components/#btn-dropdowns
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class ButtonDropdown extends Widget
{
/**
* @var string the button label
*/
public $label = 'Button';
/**
* @var array the HTML attributes for the container tag. The following special options are recognized:
*
* - tag: string, defaults to "div", the name of the container tag.
*
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
* @since 2.0.1
*/
public $containerOptions = [];
/**
* @var array the HTML attributes of the button.
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $options = [];
/**
* @var array the configuration array for [[Dropdown]].
*/
public $dropdown = [];
/**
* @var bool whether to display a group of split-styled button group.
*/
public $split = false;
/**
* @var string the tag to use to render the button
*/
public $tagName = 'button';
/**
* @var bool whether the label should be HTML-encoded.
*/
public $encodeLabel = true;
/**
* @var string name of a class to use for rendering dropdowns withing this widget. Defaults to [[Dropdown]].
* @since 2.0.7
*/
public $dropdownClass = 'yii\bootstrap\Dropdown';
/**
* Renders the widget.
*/
public function run()
{
// @todo use [[options]] instead of [[containerOptions]] and introduce [[buttonOptions]] before 2.1 release
Html::addCssClass($this->containerOptions, ['widget' => 'btn-group']);
$options = $this->containerOptions;
$tag = ArrayHelper::remove($options, 'tag', 'div');
$this->registerPlugin('dropdown');
return implode("\n", [
Html::beginTag($tag, $options),
$this->renderButton(),
$this->renderDropdown(),
Html::endTag($tag)
]);
}
/**
* Generates the button dropdown.
* @return string the rendering result.
*/
protected function renderButton()
{
Html::addCssClass($this->options, ['widget' => 'btn']);
$label = $this->label;
if ($this->encodeLabel) {
$label = Html::encode($label);
}
if ($this->split) {
$options = $this->options;
$this->options['data-toggle'] = 'dropdown';
Html::addCssClass($this->options, ['toggle' => 'dropdown-toggle']);
unset($options['id']);
$splitButton = Button::widget([
'label' => '<span class="caret"></span>',
'encodeLabel' => false,
'options' => $this->options,
'view' => $this->getView(),
]);
} else {
$label .= ' <span class="caret"></span>';
$options = $this->options;
Html::addCssClass($options, ['toggle' => 'dropdown-toggle']);
$options['data-toggle'] = 'dropdown';
$splitButton = '';
}
if (isset($options['href'])) {
if (is_array($options['href'])) {
$options['href'] = Url::to($options['href']);
}
} else {
if ($this->tagName === 'a') {
$options['href'] = '#';
}
}
return Button::widget([
'tagName' => $this->tagName,
'label' => $label,
'options' => $options,
'encodeLabel' => false,
'view' => $this->getView(),
]) . "\n" . $splitButton;
}
/**
* Generates the dropdown menu.
* @return string the rendering result.
*/
protected function renderDropdown()
{
$config = $this->dropdown;
$config['clientOptions'] = false;
$config['view'] = $this->getView();
/** @var Widget $dropdownClass */
$dropdownClass = $this->dropdownClass;
return $dropdownClass::widget($config);
}
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\helpers\ArrayHelper;
/**
* ButtonGroup renders a button group bootstrap component.
*
* For example,
*
* ```php
* // a button group with items configuration
* echo ButtonGroup::widget([
* 'buttons' => [
* ['label' => 'A'],
* ['label' => 'B'],
* ['label' => 'C', 'visible' => false],
* ]
* ]);
*
* // button group with an item as a string
* echo ButtonGroup::widget([
* 'buttons' => [
* Button::widget(['label' => 'A']),
* ['label' => 'B'],
* ]
* ]);
* ```
*
* Pressing on the button should be handled via JavaScript. See the following for details:
*
* @see http://getbootstrap.com/javascript/#buttons
* @see http://getbootstrap.com/components/#btn-groups
*
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class ButtonGroup extends Widget
{
/**
* @var array list of buttons. Each array element represents a single button
* which can be specified as a string or an array of the following structure:
*
* - label: string, required, the button label.
* - options: array, optional, the HTML attributes of the button.
* - visible: bool, optional, whether this button is visible. Defaults to true.
*/
public $buttons = [];
/**
* @var bool whether to HTML-encode the button labels.
*/
public $encodeLabels = true;
/**
* Initializes the widget.
* If you override this method, make sure you call the parent implementation first.
*/
public function init()
{
parent::init();
Html::addCssClass($this->options, ['widget' => 'btn-group']);
}
/**
* Renders the widget.
*/
public function run()
{
BootstrapAsset::register($this->getView());
return Html::tag('div', $this->renderButtons(), $this->options);
}
/**
* Generates the buttons that compound the group as specified on [[buttons]].
* @return string the rendering result.
*/
protected function renderButtons()
{
$buttons = [];
foreach ($this->buttons as $button) {
if (is_array($button)) {
$visible = ArrayHelper::remove($button, 'visible', true);
if ($visible === false) {
continue;
}
$button['view'] = $this->getView();
if (!isset($button['encodeLabel'])) {
$button['encodeLabel'] = $this->encodeLabels;
}
$buttons[] = Button::widget($button);
} else {
$buttons[] = $button;
}
}
return implode("\n", $buttons);
}
}

View File

@@ -0,0 +1,180 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
/**
* Carousel renders a carousel bootstrap javascript component.
*
* For example:
*
* ```php
* echo Carousel::widget([
* 'items' => [
* // the item contains only the image
* '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-01.jpg"/>',
* // equivalent to the above
* ['content' => '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-02.jpg"/>'],
* // the item contains both the image and the caption
* [
* 'content' => '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-03.jpg"/>',
* 'caption' => '<h4>This is title</h4><p>This is the caption text</p>',
* 'options' => [...],
* ],
* ]
* ]);
* ```
*
* @see http://getbootstrap.com/javascript/#carousel
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class Carousel extends Widget
{
/**
* @var array|bool the labels for the previous and the next control buttons.
* If false, it means the previous and the next control buttons should not be displayed.
*/
public $controls = ['&lsaquo;', '&rsaquo;'];
/**
* @var bool whether carousel indicators (<ol> tag with anchors to items) should be displayed or not.
*/
public $showIndicators = true;
/**
* @var array list of slides in the carousel. Each array element represents a single
* slide with the following structure:
*
* ```php
* [
* // required, slide content (HTML), such as an image tag
* 'content' => '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-01.jpg"/>',
* // optional, the caption (HTML) of the slide
* 'caption' => '<h4>This is title</h4><p>This is the caption text</p>',
* // optional the HTML attributes of the slide container
* 'options' => [],
* ]
* ```
*/
public $items = [];
/**
* Initializes the widget.
*/
public function init()
{
parent::init();
Html::addCssClass($this->options, ['widget' => 'carousel']);
}
/**
* Renders the widget.
*/
public function run()
{
$this->registerPlugin('carousel');
return implode("\n", [
Html::beginTag('div', $this->options),
$this->renderIndicators(),
$this->renderItems(),
$this->renderControls(),
Html::endTag('div')
]) . "\n";
}
/**
* Renders carousel indicators.
* @return string the rendering result
*/
public function renderIndicators()
{
if ($this->showIndicators === false) {
return '';
}
$indicators = [];
for ($i = 0, $count = count($this->items); $i < $count; $i++) {
$options = ['data-target' => '#' . $this->options['id'], 'data-slide-to' => $i];
if ($i === 0) {
Html::addCssClass($options, 'active');
}
$indicators[] = Html::tag('li', '', $options);
}
return Html::tag('ol', implode("\n", $indicators), ['class' => 'carousel-indicators']);
}
/**
* Renders carousel items as specified on [[items]].
* @return string the rendering result
*/
public function renderItems()
{
$items = [];
for ($i = 0, $count = count($this->items); $i < $count; $i++) {
$items[] = $this->renderItem($this->items[$i], $i);
}
return Html::tag('div', implode("\n", $items), ['class' => 'carousel-inner']);
}
/**
* Renders a single carousel item
* @param string|array $item a single item from [[items]]
* @param int $index the item index as the first item should be set to `active`
* @return string the rendering result
* @throws InvalidConfigException if the item is invalid
*/
public function renderItem($item, $index)
{
if (is_string($item)) {
$content = $item;
$caption = null;
$options = [];
} elseif (isset($item['content'])) {
$content = $item['content'];
$caption = ArrayHelper::getValue($item, 'caption');
if ($caption !== null) {
$caption = Html::tag('div', $caption, ['class' => 'carousel-caption']);
}
$options = ArrayHelper::getValue($item, 'options', []);
} else {
throw new InvalidConfigException('The "content" option is required.');
}
Html::addCssClass($options, ['widget' => 'item']);
if ($index === 0) {
Html::addCssClass($options, 'active');
}
return Html::tag('div', $content . "\n" . $caption, $options);
}
/**
* Renders previous and next control buttons.
* @throws InvalidConfigException if [[controls]] is invalid.
*/
public function renderControls()
{
if (isset($this->controls[0], $this->controls[1])) {
return Html::a($this->controls[0], '#' . $this->options['id'], [
'class' => 'left carousel-control',
'data-slide' => 'prev',
]) . "\n"
. Html::a($this->controls[1], '#' . $this->options['id'], [
'class' => 'right carousel-control',
'data-slide' => 'next',
]);
} elseif ($this->controls === false) {
return '';
} else {
throw new InvalidConfigException('The "controls" property must be either false or an array of two elements.');
}
}
}

View File

@@ -0,0 +1,233 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
/**
* Collapse renders an accordion bootstrap javascript component.
*
* For example:
*
* ```php
* echo Collapse::widget([
* 'items' => [
* // equivalent to the above
* [
* 'label' => 'Collapsible Group Item #1',
* 'content' => 'Anim pariatur cliche...',
* // open its content by default
* 'contentOptions' => ['class' => 'in']
* ],
* // another group item
* [
* 'label' => 'Collapsible Group Item #1',
* 'content' => 'Anim pariatur cliche...',
* 'contentOptions' => [...],
* 'options' => [...],
* ],
* // if you want to swap out .panel-body with .list-group, you may use the following
* [
* 'label' => 'Collapsible Group Item #1',
* 'content' => [
* 'Anim pariatur cliche...',
* 'Anim pariatur cliche...'
* ],
* 'contentOptions' => [...],
* 'options' => [...],
* 'footer' => 'Footer' // the footer label in list-group
* ],
* ]
* ]);
* ```
*
* @see http://getbootstrap.com/javascript/#collapse
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class Collapse extends Widget
{
/**
* @var array list of groups in the collapse widget. Each array element represents a single
* group with the following structure:
*
* - label: string, required, the group header label.
* - encode: bool, optional, whether this label should be HTML-encoded. This param will override
* global `$this->encodeLabels` param.
* - content: array|string|object, required, the content (HTML) of the group
* - options: array, optional, the HTML attributes of the group
* - contentOptions: optional, the HTML attributes of the group's content
*
* Since version 2.0.7 you may also specify this property as key-value pairs, where the key refers to the
* `label` and the value refers to `content`. If value is a string it is interpreted as label. If it is
* an array, it is interpreted as explained above.
*
* For example:
*
* ```php
* echo Collapse::widget([
* 'items' => [
* 'Introduction' => 'This is the first collapsable menu',
* 'Second panel' => [
* 'content' => 'This is the second collapsable menu',
* ],
* [
* 'label' => 'Third panel',
* 'content' => 'This is the third collapsable menu',
* ],
* ]
* ])
* ```
*/
public $items = [];
/**
* @var bool whether the labels for header items should be HTML-encoded.
*/
public $encodeLabels = true;
/**
* @var bool whether to close other items if an item is opened. Defaults to `true` which causes an
* accordion effect. Set this to `false` to allow keeping multiple items open at once.
* @since 2.0.7
*/
public $autoCloseItems = true;
/**
* @var string the HTML options for the item toggle tag. Key 'tag' might be used here for the tag name specification.
* For example:
*
* ```php
* [
* 'tag' => 'div',
* 'class' => 'custom-toggle',
* ]
* ```
*
* @since 2.0.8
*/
public $itemToggleOptions = [];
/**
* Initializes the widget.
*/
public function init()
{
parent::init();
Html::addCssClass($this->options, ['widget' => 'panel-group']);
}
/**
* Renders the widget.
*/
public function run()
{
$this->registerPlugin('collapse');
return implode("\n", [
Html::beginTag('div', $this->options),
$this->renderItems(),
Html::endTag('div')
]) . "\n";
}
/**
* Renders collapsible items as specified on [[items]].
* @throws InvalidConfigException if label isn't specified
* @return string the rendering result
*/
public function renderItems()
{
$items = [];
$index = 0;
foreach ($this->items as $key => $item) {
if (!is_array($item)) {
$item = ['content' => $item];
}
if (!array_key_exists('label', $item)) {
if (is_int($key)) {
throw new InvalidConfigException("The 'label' option is required.");
} else {
$item['label'] = $key;
}
}
$header = $item['label'];
$options = ArrayHelper::getValue($item, 'options', []);
Html::addCssClass($options, ['panel' => 'panel', 'widget' => 'panel-default']);
$items[] = Html::tag('div', $this->renderItem($header, $item, ++$index), $options);
}
return implode("\n", $items);
}
/**
* Renders a single collapsible item group
* @param string $header a label of the item group [[items]]
* @param array $item a single item from [[items]]
* @param int $index the item index as each item group content must have an id
* @return string the rendering result
* @throws InvalidConfigException
*/
public function renderItem($header, $item, $index)
{
if (array_key_exists('content', $item)) {
$id = $this->options['id'] . '-collapse' . $index;
$options = ArrayHelper::getValue($item, 'contentOptions', []);
$options['id'] = $id;
Html::addCssClass($options, ['widget' => 'panel-collapse', 'collapse' => 'collapse']);
$encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
if ($encodeLabel) {
$header = Html::encode($header);
}
$itemToggleOptions = array_merge([
'tag' => 'a',
'data-toggle' => 'collapse',
], $this->itemToggleOptions);
Html::addCssClass($itemToggleOptions, ['widget' => 'collapse-toggle']);
if ($this->autoCloseItems) {
$itemToggleOptions['data-parent'] = '#' . $this->options['id'];
}
$itemToggleTag = ArrayHelper::remove($itemToggleOptions, 'tag', 'a');
if ($itemToggleTag === 'a') {
$headerToggle = Html::a($header, '#' . $id, $itemToggleOptions) . "\n";
} else {
$itemToggleOptions['data-target'] = '#' . $id;
$headerToggle = Html::tag($itemToggleTag, $header, $itemToggleOptions) . "\n";
}
$header = Html::tag('h4', $headerToggle, ['class' => 'panel-title']);
if (is_string($item['content']) || is_numeric($item['content']) || is_object($item['content'])) {
$content = Html::tag('div', $item['content'], ['class' => 'panel-body']) . "\n";
} elseif (is_array($item['content'])) {
$content = Html::ul($item['content'], [
'class' => 'list-group',
'itemOptions' => [
'class' => 'list-group-item'
],
'encode' => false,
]) . "\n";
if (isset($item['footer'])) {
$content .= Html::tag('div', $item['footer'], ['class' => 'panel-footer']) . "\n";
}
} else {
throw new InvalidConfigException('The "content" option should be a string, array or object.');
}
} else {
throw new InvalidConfigException('The "content" option is required.');
}
$group = [];
$group[] = Html::tag('div', $header, ['class' => 'panel-heading']);
$group[] = Html::tag('div', $content, $options);
return implode("\n", $group);
}
}

View File

@@ -0,0 +1,143 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
/**
* Dropdown renders a Bootstrap dropdown menu component.
*
* For example,
*
* ```php
* <div class="dropdown">
* <a href="#" data-toggle="dropdown" class="dropdown-toggle">Label <b class="caret"></b></a>
* <?php
* echo Dropdown::widget([
* 'items' => [
* ['label' => 'DropdownA', 'url' => '/'],
* ['label' => 'DropdownB', 'url' => '#'],
* ],
* ]);
* ?>
* </div>
* ```
* @see http://getbootstrap.com/javascript/#dropdowns
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class Dropdown extends Widget
{
/**
* @var array list of menu items in the dropdown. Each array element can be either an HTML string,
* or an array representing a single menu with the following structure:
*
* - label: string, required, the label of the item link.
* - encode: bool, optional, whether to HTML-encode item label.
* - url: string|array, optional, the URL of the item link. This will be processed by [[\yii\helpers\Url::to()]].
* If not set, the item will be treated as a menu header when the item has no sub-menu.
* - visible: bool, optional, whether this menu item is visible. Defaults to true.
* - linkOptions: array, optional, the HTML attributes of the item link.
* - options: array, optional, the HTML attributes of the item.
* - items: array, optional, the submenu items. The structure is the same as this property.
* Note that Bootstrap doesn't support dropdown submenu. You have to add your own CSS styles to support it.
* - submenuOptions: array, optional, the HTML attributes for sub-menu container tag. If specified it will be
* merged with [[submenuOptions]].
*
* To insert divider use `<li role="presentation" class="divider"></li>`.
*/
public $items = [];
/**
* @var bool whether the labels for header items should be HTML-encoded.
*/
public $encodeLabels = true;
/**
* @var array|null the HTML attributes for sub-menu container tags.
* If not set - [[options]] value will be used for it.
* @since 2.0.5
*/
public $submenuOptions;
/**
* Initializes the widget.
* If you override this method, make sure you call the parent implementation first.
*/
public function init()
{
if ($this->submenuOptions === null) {
// copying of [[options]] kept for BC
// @todo separate [[submenuOptions]] from [[options]] completely before 2.1 release
$this->submenuOptions = $this->options;
unset($this->submenuOptions['id']);
}
parent::init();
Html::addCssClass($this->options, ['widget' => 'dropdown-menu']);
}
/**
* Renders the widget.
*/
public function run()
{
BootstrapPluginAsset::register($this->getView());
$this->registerClientEvents();
return $this->renderItems($this->items, $this->options);
}
/**
* Renders menu items.
* @param array $items the menu items to be rendered
* @param array $options the container HTML attributes
* @return string the rendering result.
* @throws InvalidConfigException if the label option is not specified in one of the items.
*/
protected function renderItems($items, $options = [])
{
$lines = [];
foreach ($items as $item) {
if (is_string($item)) {
$lines[] = $item;
continue;
}
if (isset($item['visible']) && !$item['visible']) {
continue;
}
if (!array_key_exists('label', $item)) {
throw new InvalidConfigException("The 'label' option is required.");
}
$encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
$label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
$itemOptions = ArrayHelper::getValue($item, 'options', []);
$linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
$linkOptions['tabindex'] = '-1';
$url = array_key_exists('url', $item) ? $item['url'] : null;
if (empty($item['items'])) {
if ($url === null) {
$content = $label;
Html::addCssClass($itemOptions, ['widget' => 'dropdown-header']);
} else {
$content = Html::a($label, $url, $linkOptions);
}
} else {
$submenuOptions = $this->submenuOptions;
if (isset($item['submenuOptions'])) {
$submenuOptions = array_merge($submenuOptions, $item['submenuOptions']);
}
$content = Html::a($label, $url === null ? '#' : $url, $linkOptions)
. $this->renderItems($item['items'], $submenuOptions);
Html::addCssClass($itemOptions, ['widget' => 'dropdown-submenu']);
}
$lines[] = Html::tag('li', $content, $itemOptions);
}
return Html::tag('ul', implode("\n", $lines), $options);
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
/**
* Html is an enhanced version of [[\yii\helpers\Html]] helper class dedicated to the Bootstrap needs.
* This class inherits all functionality available at [[\yii\helpers\Html]] and can be used as substitute.
*
* Attention: do not confuse [[\yii\bootstrap\Html]] and [[\yii\helpers\Html]], be careful in which class
* you are using inside your views.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0.5
*/
class Html extends BaseHtml
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
/**
* InputWidget is an adjusted for bootstrap needs version of [[\yii\widgets\InputWidget]].
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0.6
*/
class InputWidget extends \yii\widgets\InputWidget
{
use BootstrapWidgetTrait;
}

View File

@@ -0,0 +1,256 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use Yii;
use yii\helpers\ArrayHelper;
/**
* Modal renders a modal window that can be toggled by clicking on a button.
*
* The following example will show the content enclosed between the [[begin()]]
* and [[end()]] calls within the modal window:
*
* ~~~php
* Modal::begin([
* 'header' => '<h2>Hello world</h2>',
* 'toggleButton' => ['label' => 'click me'],
* ]);
*
* echo 'Say hello...';
*
* Modal::end();
* ~~~
*
* @see http://getbootstrap.com/javascript/#modals
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Modal extends Widget
{
const SIZE_LARGE = "modal-lg";
const SIZE_SMALL = "modal-sm";
const SIZE_DEFAULT = "";
/**
* @var string the header content in the modal window.
*/
public $header;
/**
* @var string additional header options
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
* @since 2.0.1
*/
public $headerOptions;
/**
* @var array body options
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
* @since 2.0.7
*/
public $bodyOptions = ['class' => 'modal-body'];
/**
* @var string the footer content in the modal window.
*/
public $footer;
/**
* @var string additional footer options
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
* @since 2.0.1
*/
public $footerOptions;
/**
* @var string the modal size. Can be [[SIZE_LARGE]] or [[SIZE_SMALL]], or empty for default.
*/
public $size;
/**
* @var array|false the options for rendering the close button tag.
* The close button is displayed in the header of the modal window. Clicking
* on the button will hide the modal window. If this is false, no close button will be rendered.
*
* The following special options are supported:
*
* - tag: string, the tag name of the button. Defaults to 'button'.
* - label: string, the label of the button. Defaults to '&times;'.
*
* The rest of the options will be rendered as the HTML attributes of the button tag.
* Please refer to the [Modal plugin help](http://getbootstrap.com/javascript/#modals)
* for the supported HTML attributes.
*/
public $closeButton = [];
/**
* @var array the options for rendering the toggle button tag.
* The toggle button is used to toggle the visibility of the modal window.
* If this property is false, no toggle button will be rendered.
*
* The following special options are supported:
*
* - tag: string, the tag name of the button. Defaults to 'button'.
* - label: string, the label of the button. Defaults to 'Show'.
*
* The rest of the options will be rendered as the HTML attributes of the button tag.
* Please refer to the [Modal plugin help](http://getbootstrap.com/javascript/#modals)
* for the supported HTML attributes.
*/
public $toggleButton = false;
/**
* Initializes the widget.
*/
public function init()
{
parent::init();
$this->initOptions();
echo $this->renderToggleButton() . "\n";
echo Html::beginTag('div', $this->options) . "\n";
echo Html::beginTag('div', ['class' => 'modal-dialog ' . $this->size]) . "\n";
echo Html::beginTag('div', ['class' => 'modal-content']) . "\n";
echo $this->renderHeader() . "\n";
echo $this->renderBodyBegin() . "\n";
}
/**
* Renders the widget.
*/
public function run()
{
echo "\n" . $this->renderBodyEnd();
echo "\n" . $this->renderFooter();
echo "\n" . Html::endTag('div'); // modal-content
echo "\n" . Html::endTag('div'); // modal-dialog
echo "\n" . Html::endTag('div');
$this->registerPlugin('modal');
}
/**
* Renders the header HTML markup of the modal
* @return string the rendering result
*/
protected function renderHeader()
{
$button = $this->renderCloseButton();
if ($button !== null) {
$this->header = $button . "\n" . $this->header;
}
if ($this->header !== null) {
Html::addCssClass($this->headerOptions, ['widget' => 'modal-header']);
return Html::tag('div', "\n" . $this->header . "\n", $this->headerOptions);
} else {
return null;
}
}
/**
* Renders the opening tag of the modal body.
* @return string the rendering result
*/
protected function renderBodyBegin()
{
return Html::beginTag('div', $this->bodyOptions);
}
/**
* Renders the closing tag of the modal body.
* @return string the rendering result
*/
protected function renderBodyEnd()
{
return Html::endTag('div');
}
/**
* Renders the HTML markup for the footer of the modal
* @return string the rendering result
*/
protected function renderFooter()
{
if ($this->footer !== null) {
Html::addCssClass($this->footerOptions, ['widget' => 'modal-footer']);
return Html::tag('div', "\n" . $this->footer . "\n", $this->footerOptions);
} else {
return null;
}
}
/**
* Renders the toggle button.
* @return string the rendering result
*/
protected function renderToggleButton()
{
if (($toggleButton = $this->toggleButton) !== false) {
$tag = ArrayHelper::remove($toggleButton, 'tag', 'button');
$label = ArrayHelper::remove($toggleButton, 'label', 'Show');
if ($tag === 'button' && !isset($toggleButton['type'])) {
$toggleButton['type'] = 'button';
}
return Html::tag($tag, $label, $toggleButton);
} else {
return null;
}
}
/**
* Renders the close button.
* @return string the rendering result
*/
protected function renderCloseButton()
{
if (($closeButton = $this->closeButton) !== false) {
$tag = ArrayHelper::remove($closeButton, 'tag', 'button');
$label = ArrayHelper::remove($closeButton, 'label', '&times;');
if ($tag === 'button' && !isset($closeButton['type'])) {
$closeButton['type'] = 'button';
}
return Html::tag($tag, $label, $closeButton);
} else {
return null;
}
}
/**
* Initializes the widget options.
* This method sets the default values for various options.
*/
protected function initOptions()
{
$this->options = array_merge([
'class' => 'fade',
'role' => 'dialog',
'tabindex' => -1,
], $this->options);
Html::addCssClass($this->options, ['widget' => 'modal']);
if ($this->clientOptions !== false) {
$this->clientOptions = array_merge(['show' => false], $this->clientOptions);
}
if ($this->closeButton !== false) {
$this->closeButton = array_merge([
'data-dismiss' => 'modal',
'aria-hidden' => 'true',
'class' => 'close',
], $this->closeButton);
}
if ($this->toggleButton !== false) {
$this->toggleButton = array_merge([
'data-toggle' => 'modal',
], $this->toggleButton);
if (!isset($this->toggleButton['data-target']) && !isset($this->toggleButton['href'])) {
$this->toggleButton['data-target'] = '#' . $this->options['id'];
}
}
}
}

View File

@@ -0,0 +1,298 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use Yii;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
/**
* Nav renders a nav HTML component.
*
* For example:
*
* ```php
* echo Nav::widget([
* 'items' => [
* [
* 'label' => 'Home',
* 'url' => ['site/index'],
* 'linkOptions' => [...],
* ],
* [
* 'label' => 'Dropdown',
* 'items' => [
* ['label' => 'Level 1 - Dropdown A', 'url' => '#'],
* '<li class="divider"></li>',
* '<li class="dropdown-header">Dropdown Header</li>',
* ['label' => 'Level 1 - Dropdown B', 'url' => '#'],
* ],
* ],
* [
* 'label' => 'Login',
* 'url' => ['site/login'],
* 'visible' => Yii::$app->user->isGuest
* ],
* ],
* 'options' => ['class' =>'nav-pills'], // set this to nav-tab to get tab-styled navigation
* ]);
* ```
*
* Note: Multilevel dropdowns beyond Level 1 are not supported in Bootstrap 3.
*
* @see http://getbootstrap.com/components/#dropdowns
* @see http://getbootstrap.com/components/#nav
*
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class Nav extends Widget
{
/**
* @var array list of items in the nav widget. Each array element represents a single
* menu item which can be either a string or an array with the following structure:
*
* - label: string, required, the nav item label.
* - url: optional, the item's URL. Defaults to "#".
* - visible: bool, optional, whether this menu item is visible. Defaults to true.
* - linkOptions: array, optional, the HTML attributes of the item's link.
* - options: array, optional, the HTML attributes of the item container (LI).
* - active: bool, optional, whether the item should be on active state or not.
* - dropDownOptions: array, optional, the HTML options that will passed to the [[Dropdown]] widget.
* - items: array|string, optional, the configuration array for creating a [[Dropdown]] widget,
* or a string representing the dropdown menu. Note that Bootstrap does not support sub-dropdown menus.
* - encode: bool, optional, whether the label will be HTML-encoded. If set, supersedes the $encodeLabels option for only this item.
*
* If a menu item is a string, it will be rendered directly without HTML encoding.
*/
public $items = [];
/**
* @var bool whether the nav items labels should be HTML-encoded.
*/
public $encodeLabels = true;
/**
* @var bool whether to automatically activate items according to whether their route setting
* matches the currently requested route.
* @see isItemActive
*/
public $activateItems = true;
/**
* @var bool whether to activate parent menu items when one of the corresponding child menu items is active.
*/
public $activateParents = false;
/**
* @var string the route used to determine if a menu item is active or not.
* If not set, it will use the route of the current request.
* @see params
* @see isItemActive
*/
public $route;
/**
* @var array the parameters used to determine if a menu item is active or not.
* If not set, it will use `$_GET`.
* @see route
* @see isItemActive
*/
public $params;
/**
* @var string this property allows you to customize the HTML which is used to generate the drop down caret symbol,
* which is displayed next to the button text to indicate the drop down functionality.
* Defaults to `null` which means `<span class="caret"></span>` will be used. To disable the caret, set this property to be an empty string.
*/
public $dropDownCaret;
/**
* @var string name of a class to use for rendering dropdowns within this widget. Defaults to [[Dropdown]].
* @since 2.0.7
*/
public $dropdownClass = 'yii\bootstrap\Dropdown';
/**
* Initializes the widget.
*/
public function init()
{
parent::init();
if ($this->route === null && Yii::$app->controller !== null) {
$this->route = Yii::$app->controller->getRoute();
}
if ($this->params === null) {
$this->params = Yii::$app->request->getQueryParams();
}
if ($this->dropDownCaret === null) {
$this->dropDownCaret = '<span class="caret"></span>';
}
Html::addCssClass($this->options, ['widget' => 'nav']);
}
/**
* Renders the widget.
*/
public function run()
{
BootstrapAsset::register($this->getView());
return $this->renderItems();
}
/**
* Renders widget items.
*/
public function renderItems()
{
$items = [];
foreach ($this->items as $i => $item) {
if (isset($item['visible']) && !$item['visible']) {
continue;
}
$items[] = $this->renderItem($item);
}
return Html::tag('ul', implode("\n", $items), $this->options);
}
/**
* Renders a widget's item.
* @param string|array $item the item to render.
* @return string the rendering result.
* @throws InvalidConfigException
*/
public function renderItem($item)
{
if (is_string($item)) {
return $item;
}
if (!isset($item['label'])) {
throw new InvalidConfigException("The 'label' option is required.");
}
$encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
$label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
$options = ArrayHelper::getValue($item, 'options', []);
$items = ArrayHelper::getValue($item, 'items');
$url = ArrayHelper::getValue($item, 'url', '#');
$linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
if (isset($item['active'])) {
$active = ArrayHelper::remove($item, 'active', false);
} else {
$active = $this->isItemActive($item);
}
if (empty($items)) {
$items = '';
} else {
$linkOptions['data-toggle'] = 'dropdown';
Html::addCssClass($options, ['widget' => 'dropdown']);
Html::addCssClass($linkOptions, ['widget' => 'dropdown-toggle']);
if ($this->dropDownCaret !== '') {
$label .= ' ' . $this->dropDownCaret;
}
if (is_array($items)) {
$items = $this->isChildActive($items, $active);
$items = $this->renderDropdown($items, $item);
}
}
if ($active) {
Html::addCssClass($options, 'active');
}
return Html::tag('li', Html::a($label, $url, $linkOptions) . $items, $options);
}
/**
* Renders the given items as a dropdown.
* This method is called to create sub-menus.
* @param array $items the given items. Please refer to [[Dropdown::items]] for the array structure.
* @param array $parentItem the parent item information. Please refer to [[items]] for the structure of this array.
* @return string the rendering result.
* @since 2.0.1
*/
protected function renderDropdown($items, $parentItem)
{
/** @var Widget $dropdownClass */
$dropdownClass = $this->dropdownClass;
return $dropdownClass::widget([
'options' => ArrayHelper::getValue($parentItem, 'dropDownOptions', []),
'items' => $items,
'encodeLabels' => $this->encodeLabels,
'clientOptions' => false,
'view' => $this->getView(),
]);
}
/**
* Check to see if a child item is active optionally activating the parent.
* @param array $items @see items
* @param bool $active should the parent be active too
* @return array @see items
*/
protected function isChildActive($items, &$active)
{
foreach ($items as $i => $child) {
if (is_array($child) && !ArrayHelper::getValue($child, 'visible', true)) {
continue;
}
if (ArrayHelper::remove($items[$i], 'active', false) || $this->isItemActive($child)) {
Html::addCssClass($items[$i]['options'], 'active');
if ($this->activateParents) {
$active = true;
}
}
$childItems = ArrayHelper::getValue($child, 'items');
if (is_array($childItems)) {
$activeParent = false;
$items[$i]['items'] = $this->isChildActive($childItems, $activeParent);
if ($activeParent) {
Html::addCssClass($items[$i]['options'], 'active');
$active = true;
}
}
}
return $items;
}
/**
* Checks whether a menu item is active.
* This is done by checking if [[route]] and [[params]] match that specified in the `url` option of the menu item.
* When the `url` option of a menu item is specified in terms of an array, its first element is treated
* as the route for the item and the rest of the elements are the associated parameters.
* Only when its route and parameters match [[route]] and [[params]], respectively, will a menu item
* be considered active.
* @param array $item the menu item to be checked
* @return bool whether the menu item is active
*/
protected function isItemActive($item)
{
if (!$this->activateItems) {
return false;
}
if (isset($item['url']) && is_array($item['url']) && isset($item['url'][0])) {
$route = $item['url'][0];
if ($route[0] !== '/' && Yii::$app->controller) {
$route = Yii::$app->controller->module->getUniqueId() . '/' . $route;
}
if (ltrim($route, '/') !== $this->route) {
return false;
}
unset($item['url']['#']);
if (count($item['url']) > 1) {
$params = $item['url'];
unset($params[0]);
foreach ($params as $name => $value) {
if ($value !== null && (!isset($this->params[$name]) || $this->params[$name] != $value)) {
return false;
}
}
}
return true;
}
return false;
}
}

View File

@@ -0,0 +1,173 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use Yii;
use yii\helpers\ArrayHelper;
/**
* NavBar renders a navbar HTML component.
*
* Any content enclosed between the [[begin()]] and [[end()]] calls of NavBar
* is treated as the content of the navbar. You may use widgets such as [[Nav]]
* or [[\yii\widgets\Menu]] to build up such content. For example,
*
* ```php
* use yii\bootstrap\NavBar;
* use yii\bootstrap\Nav;
*
* NavBar::begin(['brandLabel' => 'NavBar Test']);
* echo Nav::widget([
* 'items' => [
* ['label' => 'Home', 'url' => ['/site/index']],
* ['label' => 'About', 'url' => ['/site/about']],
* ],
* 'options' => ['class' => 'navbar-nav'],
* ]);
* NavBar::end();
* ```
*
* @see https://getbootstrap.com/docs/3.3/components/#navbar
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @author Alexander Kochetov <creocoder@gmail.com>
* @since 2.0
*/
class NavBar extends Widget
{
/**
* @var array the HTML attributes for the widget container tag. The following special options are recognized:
*
* - tag: string, defaults to "nav", the name of the container tag.
*
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $options = [];
/**
* @var array the HTML attributes for the container tag. The following special options are recognized:
*
* - tag: string, defaults to "div", the name of the container tag.
*
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $containerOptions = [];
/**
* @var string|bool the text of the brand or false if it's not used. Note that this is not HTML-encoded.
* @see https://getbootstrap.com/docs/3.3/components/#navbar
*/
public $brandLabel = false;
/**
* @var string|bool src of the brand image or false if it's not used. Note that this param will override `$this->brandLabel` param.
* @see https://getbootstrap.com/docs/3.3/components/#navbar
* @since 2.0.8
*/
public $brandImage = false;
/**
* @var array|string|bool $url the URL for the brand's hyperlink tag. This parameter will be processed by [[\yii\helpers\Url::to()]]
* and will be used for the "href" attribute of the brand link. Default value is false that means
* [[\yii\web\Application::homeUrl]] will be used.
* You may set it to `null` if you want to have no link at all.
*/
public $brandUrl = false;
/**
* @var array the HTML attributes of the brand link.
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $brandOptions = [];
/**
* @var string HTML content to be added in navbar-header div, for example, mobile search form.
* @since 2.0.8
*/
public $headerContent;
/**
* @var string text to show for screen readers for the button to toggle the navbar.
*/
public $screenReaderToggleText = 'Toggle navigation';
/**
* @var bool whether the navbar content should be included in an inner div container which by default
* adds left and right padding. Set this to false for a 100% width navbar.
*/
public $renderInnerContainer = true;
/**
* @var array the HTML attributes of the inner container.
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $innerContainerOptions = [];
/**
* Initializes the widget.
*/
public function init()
{
parent::init();
$this->clientOptions = false;
if (empty($this->options['class'])) {
Html::addCssClass($this->options, ['navbar', 'navbar-default']);
} else {
Html::addCssClass($this->options, ['widget' => 'navbar']);
}
$options = $this->options;
$tag = ArrayHelper::remove($options, 'tag', 'nav');
echo Html::beginTag($tag, $options);
if ($this->renderInnerContainer) {
if (!isset($this->innerContainerOptions['class'])) {
Html::addCssClass($this->innerContainerOptions, 'container');
}
echo Html::beginTag('div', $this->innerContainerOptions);
}
echo Html::beginTag('div', ['class' => 'navbar-header']);
if (!isset($this->containerOptions['id'])) {
$this->containerOptions['id'] = "{$this->options['id']}-collapse";
}
echo $this->renderToggleButton();
if ($this->brandImage !== false) {
$this->brandLabel = Html::img($this->brandImage);
}
if ($this->brandLabel !== false) {
Html::addCssClass($this->brandOptions, ['widget' => 'navbar-brand']);
echo Html::a($this->brandLabel, $this->brandUrl === false ? Yii::$app->homeUrl : $this->brandUrl, $this->brandOptions);
}
echo $this->headerContent;
echo Html::endTag('div');
Html::addCssClass($this->containerOptions, ['collapse' => 'collapse', 'widget' => 'navbar-collapse']);
$options = $this->containerOptions;
$tag = ArrayHelper::remove($options, 'tag', 'div');
echo Html::beginTag($tag, $options);
}
/**
* Renders the widget.
*/
public function run()
{
$tag = ArrayHelper::remove($this->containerOptions, 'tag', 'div');
echo Html::endTag($tag);
if ($this->renderInnerContainer) {
echo Html::endTag('div');
}
$tag = ArrayHelper::remove($this->options, 'tag', 'nav');
echo Html::endTag($tag);
BootstrapPluginAsset::register($this->getView());
}
/**
* Renders collapsible toggle button.
* @return string the rendering toggle button.
*/
protected function renderToggleButton()
{
$bar = Html::tag('span', '', ['class' => 'icon-bar']);
$screenReader = "<span class=\"sr-only\">{$this->screenReaderToggleText}</span>";
return Html::button("{$screenReader}\n{$bar}\n{$bar}\n{$bar}", [
'class' => 'navbar-toggle',
'data-toggle' => 'collapse',
'data-target' => "#{$this->containerOptions['id']}",
]);
}
}

View File

@@ -0,0 +1,166 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
/**
* Progress renders a bootstrap progress bar component.
*
* For example,
*
* ```php
* // default with label
* echo Progress::widget([
* 'percent' => 60,
* 'label' => 'test',
* ]);
*
* // styled
* echo Progress::widget([
* 'percent' => 65,
* 'barOptions' => ['class' => 'progress-bar-danger']
* ]);
*
* // striped
* echo Progress::widget([
* 'percent' => 70,
* 'barOptions' => ['class' => 'progress-bar-warning'],
* 'options' => ['class' => 'progress-striped']
* ]);
*
* // striped animated
* echo Progress::widget([
* 'percent' => 70,
* 'barOptions' => ['class' => 'progress-bar-success'],
* 'options' => ['class' => 'active progress-striped']
* ]);
*
* // stacked bars
* echo Progress::widget([
* 'bars' => [
* ['percent' => 30, 'options' => ['class' => 'progress-bar-danger']],
* ['percent' => 30, 'label' => 'test', 'options' => ['class' => 'progress-bar-success']],
* ['percent' => 35, 'options' => ['class' => 'progress-bar-warning']],
* ]
* ]);
* ```
* @see http://getbootstrap.com/components/#progress
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @author Alexander Makarov <sam@rmcreative.ru>
* @since 2.0
*/
class Progress extends Widget
{
/**
* @var string the button label.
*/
public $label;
/**
* @var int the amount of progress as a percentage.
*/
public $percent = 0;
/**
* @var array the HTML attributes of the bar.
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $barOptions = [];
/**
* @var array a set of bars that are stacked together to form a single progress bar.
* Each bar is an array of the following structure:
*
* ```php
* [
* // required, the amount of progress as a percentage.
* 'percent' => 30,
* // optional, the label to be displayed on the bar
* 'label' => '30%',
* // optional, array, additional HTML attributes for the bar tag
* 'options' => [],
* ]
* ```
*/
public $bars;
/**
* Initializes the widget.
* If you override this method, make sure you call the parent implementation first.
*/
public function init()
{
parent::init();
Html::addCssClass($this->options, ['widget' => 'progress']);
}
/**
* Renders the widget.
*/
public function run()
{
BootstrapAsset::register($this->getView());
return implode("\n", [
Html::beginTag('div', $this->options),
$this->renderProgress(),
Html::endTag('div')
]) . "\n";
}
/**
* Renders the progress.
* @return string the rendering result.
* @throws InvalidConfigException if the "percent" option is not set in a stacked progress bar.
*/
protected function renderProgress()
{
if (empty($this->bars)) {
return $this->renderBar($this->percent, $this->label, $this->barOptions);
}
$bars = [];
foreach ($this->bars as $bar) {
$label = ArrayHelper::getValue($bar, 'label', '');
if (!isset($bar['percent'])) {
throw new InvalidConfigException("The 'percent' option is required.");
}
$options = ArrayHelper::getValue($bar, 'options', []);
$bars[] = $this->renderBar($bar['percent'], $label, $options);
}
return implode("\n", $bars);
}
/**
* Generates a bar
* @param int $percent the percentage of the bar
* @param string $label, optional, the label to display at the bar
* @param array $options the HTML attributes of the bar
* @return string the rendering result.
*/
protected function renderBar($percent, $label = '', $options = [])
{
$defaultOptions = [
'role' => 'progressbar',
'aria-valuenow' => $percent,
'aria-valuemin' => 0,
'aria-valuemax' => 100,
'style' => "width:{$percent}%",
];
$options = array_merge($defaultOptions, $options);
Html::addCssClass($options, ['widget' => 'progress-bar']);
$out = Html::beginTag('div', $options);
$out .= $label;
$out .= Html::tag('span', \Yii::t('yii', '{percent}% Complete', ['percent' => $percent]), [
'class' => 'sr-only'
]);
$out .= Html::endTag('div');
return $out;
}
}

View File

@@ -0,0 +1,319 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
/**
* Tabs renders a Tab bootstrap javascript component.
*
* For example:
*
* ```php
* echo Tabs::widget([
* 'items' => [
* [
* 'label' => 'One',
* 'content' => 'Anim pariatur cliche...',
* 'active' => true
* ],
* [
* 'label' => 'Two',
* 'content' => 'Anim pariatur cliche...',
* 'headerOptions' => [...],
* 'options' => ['id' => 'myveryownID'],
* ],
* [
* 'label' => 'Example',
* 'url' => 'http://www.example.com',
* ],
* [
* 'label' => 'Dropdown',
* 'items' => [
* [
* 'label' => 'DropdownA',
* 'content' => 'DropdownA, Anim pariatur cliche...',
* ],
* [
* 'label' => 'DropdownB',
* 'content' => 'DropdownB, Anim pariatur cliche...',
* ],
* [
* 'label' => 'External Link',
* 'url' => 'http://www.example.com',
* ],
* ],
* ],
* ],
* ]);
* ```
*
* @see http://getbootstrap.com/javascript/#tabs
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
class Tabs extends Widget
{
/**
* @var array list of tabs in the tabs widget. Each array element represents a single
* tab with the following structure:
*
* - label: string, required, the tab header label.
* - encode: bool, optional, whether this label should be HTML-encoded. This param will override
* global `$this->encodeLabels` param.
* - headerOptions: array, optional, the HTML attributes of the tab header.
* - linkOptions: array, optional, the HTML attributes of the tab header link tags.
* - content: string, optional, the content (HTML) of the tab pane.
* - url: string, optional, an external URL. When this is specified, clicking on this tab will bring
* the browser to this URL. This option is available since version 2.0.4.
* - options: array, optional, the HTML attributes of the tab pane container.
* - active: bool, optional, whether this item tab header and pane should be active. If no item is marked as
* 'active' explicitly - the first one will be activated.
* - visible: bool, optional, whether the item tab header and pane should be visible or not. Defaults to true.
* - items: array, optional, can be used instead of `content` to specify a dropdown items
* configuration array. Each item can hold three extra keys, besides the above ones:
* * active: bool, optional, whether the item tab header and pane should be visible or not.
* * content: string, required if `items` is not set. The content (HTML) of the tab pane.
* * contentOptions: optional, array, the HTML attributes of the tab content container.
*/
public $items = [];
/**
* @var array list of HTML attributes for the item container tags. This will be overwritten
* by the "options" set in individual [[items]]. The following special options are recognized:
*
* - tag: string, defaults to "div", the tag name of the item container tags.
*
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $itemOptions = [];
/**
* @var array list of HTML attributes for the header container tags. This will be overwritten
* by the "headerOptions" set in individual [[items]].
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $headerOptions = [];
/**
* @var array list of HTML attributes for the tab header link tags. This will be overwritten
* by the "linkOptions" set in individual [[items]].
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $linkOptions = [];
/**
* @var bool whether the labels for header items should be HTML-encoded.
*/
public $encodeLabels = true;
/**
* @var string specifies the Bootstrap tab styling.
*/
public $navType = 'nav-tabs';
/**
* @var bool whether to render the `tab-content` container and its content. You may set this property
* to be false so that you can manually render `tab-content` yourself in case your tab contents are complex.
* @since 2.0.1
*/
public $renderTabContent = true;
/**
* @var array list of HTML attributes for the `tab-content` container. This will always contain the CSS class `tab-content`.
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
* @since 2.0.7
*/
public $tabContentOptions = [];
/**
* @var string name of a class to use for rendering dropdowns withing this widget. Defaults to [[Dropdown]].
* @since 2.0.7
*/
public $dropdownClass = 'yii\bootstrap\Dropdown';
/**
* Initializes the widget.
*/
public function init()
{
parent::init();
Html::addCssClass($this->options, ['widget' => 'nav', $this->navType]);
Html::addCssClass($this->tabContentOptions, 'tab-content');
}
/**
* Renders the widget.
*/
public function run()
{
$this->registerPlugin('tab');
return $this->renderItems();
}
/**
* Renders tab items as specified on [[items]].
* @return string the rendering result.
* @throws InvalidConfigException.
*/
protected function renderItems()
{
$headers = [];
$panes = [];
if (!$this->hasActiveTab()) {
$this->activateFirstVisibleTab();
}
foreach ($this->items as $n => $item) {
if (!ArrayHelper::remove($item, 'visible', true)) {
continue;
}
if (!array_key_exists('label', $item)) {
throw new InvalidConfigException("The 'label' option is required.");
}
$encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
$label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
$headerOptions = array_merge($this->headerOptions, ArrayHelper::getValue($item, 'headerOptions', []));
$linkOptions = array_merge($this->linkOptions, ArrayHelper::getValue($item, 'linkOptions', []));
if (isset($item['items'])) {
$label .= ' <b class="caret"></b>';
Html::addCssClass($headerOptions, ['widget' => 'dropdown']);
if ($this->renderDropdown($n, $item['items'], $panes)) {
Html::addCssClass($headerOptions, 'active');
}
Html::addCssClass($linkOptions, ['widget' => 'dropdown-toggle']);
if (!isset($linkOptions['data-toggle'])) {
$linkOptions['data-toggle'] = 'dropdown';
}
/** @var Widget $dropdownClass */
$dropdownClass = $this->dropdownClass;
$header = Html::a($label, "#", $linkOptions) . "\n"
. $dropdownClass::widget(['items' => $item['items'], 'clientOptions' => false, 'view' => $this->getView()]);
} else {
$options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', []));
$options['id'] = ArrayHelper::getValue($options, 'id', $this->options['id'] . '-tab' . $n);
Html::addCssClass($options, ['widget' => 'tab-pane']);
if (ArrayHelper::remove($item, 'active')) {
Html::addCssClass($options, 'active');
Html::addCssClass($headerOptions, 'active');
}
if (isset($item['url'])) {
$header = Html::a($label, $item['url'], $linkOptions);
} else {
if (!isset($linkOptions['data-toggle'])) {
$linkOptions['data-toggle'] = 'tab';
}
$header = Html::a($label, '#' . $options['id'], $linkOptions);
}
if ($this->renderTabContent) {
$tag = ArrayHelper::remove($options, 'tag', 'div');
$panes[] = Html::tag($tag, isset($item['content']) ? $item['content'] : '', $options);
}
}
$headers[] = Html::tag('li', $header, $headerOptions);
}
return Html::tag('ul', implode("\n", $headers), $this->options) . $this->renderPanes($panes);
}
/**
* @return bool if there's active tab defined
*/
protected function hasActiveTab()
{
foreach ($this->items as $item) {
if (isset($item['active']) && $item['active'] === true) {
return true;
}
}
return false;
}
/**
* Sets the first visible tab as active.
*
* This method activates the first tab that is visible and
* not explicitly set to inactive (`'active' => false`).
* @since 2.0.7
*/
protected function activateFirstVisibleTab()
{
foreach ($this->items as $i => $item) {
$active = ArrayHelper::getValue($item, 'active', null);
$visible = ArrayHelper::getValue($item, 'visible', true);
if ($visible && $active !== false) {
$this->items[$i]['active'] = true;
return;
}
}
}
/**
* Normalizes dropdown item options by removing tab specific keys `content` and `contentOptions`, and also
* configure `panes` accordingly.
* @param string $itemNumber number of the item
* @param array $items the dropdown items configuration.
* @param array $panes the panes reference array.
* @return bool whether any of the dropdown items is `active` or not.
* @throws InvalidConfigException
*/
protected function renderDropdown($itemNumber, &$items, &$panes)
{
$itemActive = false;
foreach ($items as $n => &$item) {
if (is_string($item)) {
continue;
}
if (isset($item['visible']) && !$item['visible']) {
continue;
}
if (!(array_key_exists('content', $item) xor array_key_exists('url', $item))) {
throw new InvalidConfigException("Either the 'content' or the 'url' option is required, but only one can be set.");
}
if (array_key_exists('url', $item)) {
continue;
}
$content = ArrayHelper::remove($item, 'content');
$options = ArrayHelper::remove($item, 'contentOptions', []);
Html::addCssClass($options, ['widget' => 'tab-pane']);
if (ArrayHelper::remove($item, 'active')) {
Html::addCssClass($options, 'active');
Html::addCssClass($item['options'], 'active');
$itemActive = true;
}
$options['id'] = ArrayHelper::getValue($options, 'id', $this->options['id'] . '-dd' . $itemNumber . '-tab' . $n);
$item['url'] = '#' . $options['id'];
if (!isset($item['linkOptions']['data-toggle'])) {
$item['linkOptions']['data-toggle'] = 'tab';
}
$panes[] = Html::tag('div', $content, $options);
unset($item);
}
return $itemActive;
}
/**
* Renders tab panes.
*
* @param array $panes
* @return string the rendering result.
* @since 2.0.7
*/
public function renderPanes($panes)
{
return $this->renderTabContent ? "\n" . Html::tag('div', implode("\n", $panes), $this->tabContentOptions) : '';
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\base\InvalidConfigException;
/**
* ToggleButtonGroup allows rendering form inputs Checkbox/Radio toggle button groups.
*
* You can use this widget in an [[yii\bootstrap\ActiveForm|ActiveForm]] using the [[yii\widgets\ActiveField::widget()|widget()]]
* method, for example like this:
*
* ```php
* <?= $form->field($model, 'item_id')->widget(\yii\bootstrap\ToggleButtonGroup::classname(), [
* // configure additional widget properties here
* ]) ?>
* ```
*
* @see http://getbootstrap.com/javascript/#buttons-checkbox-radio
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0.6
*/
class ToggleButtonGroup extends InputWidget
{
/**
* @var string input type, can be:
* - 'checkbox'
* - 'radio'
*/
public $type;
/**
* @var array the data item used to generate the checkboxes.
* The array values are the labels, while the array keys are the corresponding checkbox or radio values.
*/
public $items = [];
/**
* @var array, the HTML attributes for the label (button) tag.
* @see Html::checkbox()
* @see Html::radio()
*/
public $labelOptions = [];
/**
* @var bool whether the items labels should be HTML-encoded.
*/
public $encodeLabels = true;
/**
* {@inheritdoc}
*/
public function init()
{
parent::init();
$this->registerPlugin('button');
Html::addCssClass($this->options, 'btn-group');
$this->options['data-toggle'] = 'buttons';
}
/**
* {@inheritdoc}
*/
public function run()
{
if (!isset($this->options['item'])) {
$this->options['item'] = [$this, 'renderItem'];
}
switch ($this->type) {
case 'checkbox':
if ($this->hasModel()) {
return Html::activeCheckboxList($this->model, $this->attribute, $this->items, $this->options);
} else {
return Html::checkboxList($this->name, $this->value, $this->items, $this->options);
}
case 'radio':
if ($this->hasModel()) {
return Html::activeRadioList($this->model, $this->attribute, $this->items, $this->options);
} else {
return Html::radioList($this->name, $this->value, $this->items, $this->options);
}
default:
throw new InvalidConfigException("Unsupported type '{$this->type}'");
}
}
/**
* Default callback for checkbox/radio list item rendering.
* @param int $index item index.
* @param string $label item label.
* @param string $name input name.
* @param bool $checked whether value is checked or not.
* @param string $value input value.
* @return string generated HTML.
* @see Html::checkbox()
* @see Html::radio()
*/
public function renderItem($index, $label, $name, $checked, $value)
{
$labelOptions = $this->labelOptions;
Html::addCssClass($labelOptions, 'btn');
if ($checked) {
Html::addCssClass($labelOptions, 'active');
}
$type = $this->type;
if ($this->encodeLabels) {
$label = Html::encode($label);
}
return Html::$type($name, $checked, ['label' => $label, 'labelOptions' => $labelOptions, 'value' => $value]);
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
/**
* \yii\bootstrap\Widget is the base class for all bootstrap widgets.
*
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Widget extends \yii\base\Widget
{
use BootstrapWidgetTrait;
/**
* @var array the HTML attributes for the widget container tag.
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
*/
public $options = [];
}