From c84c84dcaf6d76f3b6c5fc4bce36e1173e042d81 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Fri, 2 Feb 2018 16:54:19 +0200 Subject: [PATCH] move source code to 'src' directory --- ActiveField.php | 423 ------------------------------------------- ActiveForm.php | 109 ----------- Alert.php | 148 --------------- BaseHtml.php | 145 --------------- BootstrapAsset.php | 24 --- BootstrapPluginAsset.php | 28 --- BootstrapThemeAsset.php | 27 --- BootstrapWidgetTrait.php | 107 ----------- Button.php | 60 ------ ButtonDropdown.php | 160 ---------------- ButtonGroup.php | 106 ----------- Carousel.php | 180 ------------------ Collapse.php | 233 ------------------------ Dropdown.php | 143 --------------- Html.php | 22 --- InputWidget.php | 19 -- Modal.php | 256 -------------------------- Nav.php | 298 ------------------------------ NavBar.php | 173 ------------------ Progress.php | 166 ----------------- Tabs.php | 319 -------------------------------- ToggleButtonGroup.php | 115 ------------ Widget.php | 26 --- composer.json | 2 +- src/ActiveField.php | 423 +++++++++++++++++++++++++++++++++++++++++++ src/ActiveForm.php | 109 +++++++++++ src/Alert.php | 148 +++++++++++++++ src/BaseHtml.php | 145 +++++++++++++++ src/BootstrapAsset.php | 24 +++ src/BootstrapPluginAsset.php | 28 +++ src/BootstrapThemeAsset.php | 27 +++ src/BootstrapWidgetTrait.php | 107 +++++++++++ src/Button.php | 60 ++++++ src/ButtonDropdown.php | 160 ++++++++++++++++ src/ButtonGroup.php | 106 +++++++++++ src/Carousel.php | 180 ++++++++++++++++++ src/Collapse.php | 233 ++++++++++++++++++++++++ src/Dropdown.php | 143 +++++++++++++++ src/Html.php | 22 +++ src/InputWidget.php | 19 ++ src/Modal.php | 256 ++++++++++++++++++++++++++ src/Nav.php | 298 ++++++++++++++++++++++++++++++ src/NavBar.php | 173 ++++++++++++++++++ src/Progress.php | 166 +++++++++++++++++ src/Tabs.php | 319 ++++++++++++++++++++++++++++++++ src/ToggleButtonGroup.php | 115 ++++++++++++ src/Widget.php | 26 +++ tests/bootstrap.php | 2 +- 48 files changed, 3289 insertions(+), 3289 deletions(-) delete mode 100644 ActiveField.php delete mode 100644 ActiveForm.php delete mode 100644 Alert.php delete mode 100644 BaseHtml.php delete mode 100644 BootstrapAsset.php delete mode 100644 BootstrapPluginAsset.php delete mode 100644 BootstrapThemeAsset.php delete mode 100644 BootstrapWidgetTrait.php delete mode 100644 Button.php delete mode 100644 ButtonDropdown.php delete mode 100644 ButtonGroup.php delete mode 100644 Carousel.php delete mode 100644 Collapse.php delete mode 100644 Dropdown.php delete mode 100644 Html.php delete mode 100644 InputWidget.php delete mode 100644 Modal.php delete mode 100644 Nav.php delete mode 100644 NavBar.php delete mode 100644 Progress.php delete mode 100644 Tabs.php delete mode 100644 ToggleButtonGroup.php delete mode 100644 Widget.php create mode 100644 src/ActiveField.php create mode 100644 src/ActiveForm.php create mode 100644 src/Alert.php create mode 100644 src/BaseHtml.php create mode 100644 src/BootstrapAsset.php create mode 100644 src/BootstrapPluginAsset.php create mode 100644 src/BootstrapThemeAsset.php create mode 100644 src/BootstrapWidgetTrait.php create mode 100644 src/Button.php create mode 100644 src/ButtonDropdown.php create mode 100644 src/ButtonGroup.php create mode 100644 src/Carousel.php create mode 100644 src/Collapse.php create mode 100644 src/Dropdown.php create mode 100644 src/Html.php create mode 100644 src/InputWidget.php create mode 100644 src/Modal.php create mode 100644 src/Nav.php create mode 100644 src/NavBar.php create mode 100644 src/Progress.php create mode 100644 src/Tabs.php create mode 100644 src/ToggleButtonGroup.php create mode 100644 src/Widget.php diff --git a/ActiveField.php b/ActiveField.php deleted file mode 100644 index 11dd10c..0000000 --- a/ActiveField.php +++ /dev/null @@ -1,423 +0,0 @@ - '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}
{input}{error}{hint}
' - * ]); - * - * // Input group - * echo $form->field($model, 'demo', [ - * 'inputTemplate' => '
@{input}
', - * ]); - * - * ActiveForm::end(); - * ``` - * - * @see \yii\bootstrap\ActiveForm - * @see http://getbootstrap.com/css/#forms - * - * @author Michael Härtl - * @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 = "
\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n{error}\n{hint}\n
"; - /** - * @var string the template for radios in default layout - */ - public $radioTemplate = "
\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n{error}\n{hint}\n
"; - /** - * @var string the template for checkboxes in horizontal layout - */ - public $horizontalCheckboxTemplate = "{beginWrapper}\n
\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n
\n{error}\n{endWrapper}\n{hint}"; - /** - * @var string the template for radio buttons in horizontal layout - */ - public $horizontalRadioTemplate = "{beginWrapper}\n
\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n
\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 '
' . Html::checkbox($name, $checked, $options) . '
'; - }; - } - 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 '
' . Html::radio($name, $checked, $options) . '
'; - }; - } - 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; - } - } -} diff --git a/ActiveForm.php b/ActiveForm.php deleted file mode 100644 index 8e935bd..0000000 --- a/ActiveForm.php +++ /dev/null @@ -1,109 +0,0 @@ - '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 - * @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); - } -} diff --git a/Alert.php b/Alert.php deleted file mode 100644 index 68ce8c0..0000000 --- a/Alert.php +++ /dev/null @@ -1,148 +0,0 @@ - [ - * '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 - * @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 '×'. - * - * 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', '×'); - 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); - } - } -} diff --git a/BaseHtml.php b/BaseHtml.php deleted file mode 100644 index 2e76f5c..0000000 --- a/BaseHtml.php +++ /dev/null @@ -1,145 +0,0 @@ - - * @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 '
' . static::radio($name, $checked, $options) . '
'; - }; - } - - 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 '
' . Html::checkbox($name, $checked, $options) . '
'; - }; - } - - 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); - } -} diff --git a/BootstrapAsset.php b/BootstrapAsset.php deleted file mode 100644 index 313a787..0000000 --- a/BootstrapAsset.php +++ /dev/null @@ -1,24 +0,0 @@ - - * @since 2.0 - */ -class BootstrapAsset extends AssetBundle -{ - public $sourcePath = '@bower/bootstrap/dist'; - public $css = [ - 'css/bootstrap.css', - ]; -} diff --git a/BootstrapPluginAsset.php b/BootstrapPluginAsset.php deleted file mode 100644 index 6af1adc..0000000 --- a/BootstrapPluginAsset.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @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', - ]; -} diff --git a/BootstrapThemeAsset.php b/BootstrapThemeAsset.php deleted file mode 100644 index 60747a9..0000000 --- a/BootstrapThemeAsset.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @since 2.0 - */ -class BootstrapThemeAsset extends AssetBundle -{ - public $sourcePath = '@bower/bootstrap/dist'; - public $css = [ - 'css/bootstrap-theme.css', - ]; - public $depends = [ - 'yii\bootstrap\BootstrapAsset', - ]; -} diff --git a/BootstrapWidgetTrait.php b/BootstrapWidgetTrait.php deleted file mode 100644 index 955feef..0000000 --- a/BootstrapWidgetTrait.php +++ /dev/null @@ -1,107 +0,0 @@ - - * @author Qiang Xue - * @author Paul Klimov - * @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(); -} diff --git a/Button.php b/Button.php deleted file mode 100644 index 7d4d495..0000000 --- a/Button.php +++ /dev/null @@ -1,60 +0,0 @@ - 'Action', - * 'options' => ['class' => 'btn-lg'], - * ]); - * ``` - * @see http://getbootstrap.com/javascript/#buttons - * @author Antonio Ramirez - * @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); - } -} diff --git a/ButtonDropdown.php b/ButtonDropdown.php deleted file mode 100644 index 310b4a0..0000000 --- a/ButtonDropdown.php +++ /dev/null @@ -1,160 +0,0 @@ - '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 - * @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' => '', - 'encodeLabel' => false, - 'options' => $this->options, - 'view' => $this->getView(), - ]); - } else { - $label .= ' '; - $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); - } -} diff --git a/ButtonGroup.php b/ButtonGroup.php deleted file mode 100644 index 832f086..0000000 --- a/ButtonGroup.php +++ /dev/null @@ -1,106 +0,0 @@ - [ - * ['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 - * @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); - } -} diff --git a/Carousel.php b/Carousel.php deleted file mode 100644 index 99fdf3a..0000000 --- a/Carousel.php +++ /dev/null @@ -1,180 +0,0 @@ - [ - * // the item contains only the image - * '', - * // equivalent to the above - * ['content' => ''], - * // the item contains both the image and the caption - * [ - * 'content' => '', - * 'caption' => '

This is title

This is the caption text

', - * 'options' => [...], - * ], - * ] - * ]); - * ``` - * - * @see http://getbootstrap.com/javascript/#carousel - * @author Antonio Ramirez - * @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 = ['‹', '›']; - /** - * @var bool whether carousel indicators (
    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' => '', - * // optional, the caption (HTML) of the slide - * 'caption' => '

    This is title

    This is the caption text

    ', - * // 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.'); - } - } -} diff --git a/Collapse.php b/Collapse.php deleted file mode 100644 index 20945b5..0000000 --- a/Collapse.php +++ /dev/null @@ -1,233 +0,0 @@ - [ - * // 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 - * @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); - } -} diff --git a/Dropdown.php b/Dropdown.php deleted file mode 100644 index de3c014..0000000 --- a/Dropdown.php +++ /dev/null @@ -1,143 +0,0 @@ - - * Label - * [ - * ['label' => 'DropdownA', 'url' => '/'], - * ['label' => 'DropdownB', 'url' => '#'], - * ], - * ]); - * ?> - * - * ``` - * @see http://getbootstrap.com/javascript/#dropdowns - * @author Antonio Ramirez - * @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 ``. - */ - 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); - } -} diff --git a/Html.php b/Html.php deleted file mode 100644 index 1941106..0000000 --- a/Html.php +++ /dev/null @@ -1,22 +0,0 @@ - - * @since 2.0.5 - */ -class Html extends BaseHtml -{ -} diff --git a/InputWidget.php b/InputWidget.php deleted file mode 100644 index 353b79b..0000000 --- a/InputWidget.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @since 2.0.6 - */ -class InputWidget extends \yii\widgets\InputWidget -{ - use BootstrapWidgetTrait; -} diff --git a/Modal.php b/Modal.php deleted file mode 100644 index 96b1264..0000000 --- a/Modal.php +++ /dev/null @@ -1,256 +0,0 @@ - '

    Hello world

    ', - * 'toggleButton' => ['label' => 'click me'], - * ]); - * - * echo 'Say hello...'; - * - * Modal::end(); - * ~~~ - * - * @see http://getbootstrap.com/javascript/#modals - * @author Antonio Ramirez - * @author Qiang Xue - * @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 '×'. - * - * 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', '×'); - 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']; - } - } - } -} diff --git a/Nav.php b/Nav.php deleted file mode 100644 index 5e18428..0000000 --- a/Nav.php +++ /dev/null @@ -1,298 +0,0 @@ - [ - * [ - * 'label' => 'Home', - * 'url' => ['site/index'], - * 'linkOptions' => [...], - * ], - * [ - * 'label' => 'Dropdown', - * 'items' => [ - * ['label' => 'Level 1 - Dropdown A', 'url' => '#'], - * '
  1. ', - * '', - * ['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 - * @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 `` 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 = ''; - } - 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; - } -} diff --git a/NavBar.php b/NavBar.php deleted file mode 100644 index fff1f87..0000000 --- a/NavBar.php +++ /dev/null @@ -1,173 +0,0 @@ - '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 - * @author Alexander Kochetov - * @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 = "{$this->screenReaderToggleText}"; - - return Html::button("{$screenReader}\n{$bar}\n{$bar}\n{$bar}", [ - 'class' => 'navbar-toggle', - 'data-toggle' => 'collapse', - 'data-target' => "#{$this->containerOptions['id']}", - ]); - } -} diff --git a/Progress.php b/Progress.php deleted file mode 100644 index 6ba6d45..0000000 --- a/Progress.php +++ /dev/null @@ -1,166 +0,0 @@ - 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 - * @author Alexander Makarov - * @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; - } -} diff --git a/Tabs.php b/Tabs.php deleted file mode 100644 index 49c3bec..0000000 --- a/Tabs.php +++ /dev/null @@ -1,319 +0,0 @@ - [ - * [ - * '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 - * @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 .= ' '; - 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) : ''; - } -} diff --git a/ToggleButtonGroup.php b/ToggleButtonGroup.php deleted file mode 100644 index 05883e6..0000000 --- a/ToggleButtonGroup.php +++ /dev/null @@ -1,115 +0,0 @@ -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 - * @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]); - } -} diff --git a/Widget.php b/Widget.php deleted file mode 100644 index 3be95f3..0000000 --- a/Widget.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @author Qiang Xue - * @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 = []; -} diff --git a/composer.json b/composer.json index 0465796..341bc94 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,7 @@ ], "autoload": { "psr-4": { - "yii\\bootstrap\\": "" + "yii\\bootstrap\\": "src" } }, "extra": { diff --git a/src/ActiveField.php b/src/ActiveField.php new file mode 100644 index 0000000..11dd10c --- /dev/null +++ b/src/ActiveField.php @@ -0,0 +1,423 @@ + '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}
    {input}{error}{hint}
    ' + * ]); + * + * // Input group + * echo $form->field($model, 'demo', [ + * 'inputTemplate' => '
    @{input}
    ', + * ]); + * + * ActiveForm::end(); + * ``` + * + * @see \yii\bootstrap\ActiveForm + * @see http://getbootstrap.com/css/#forms + * + * @author Michael Härtl + * @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 = "
    \n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n{error}\n{hint}\n
    "; + /** + * @var string the template for radios in default layout + */ + public $radioTemplate = "
    \n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n{error}\n{hint}\n
    "; + /** + * @var string the template for checkboxes in horizontal layout + */ + public $horizontalCheckboxTemplate = "{beginWrapper}\n
    \n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n
    \n{error}\n{endWrapper}\n{hint}"; + /** + * @var string the template for radio buttons in horizontal layout + */ + public $horizontalRadioTemplate = "{beginWrapper}\n
    \n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n
    \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 '
    ' . Html::checkbox($name, $checked, $options) . '
    '; + }; + } + 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 '
    ' . Html::radio($name, $checked, $options) . '
    '; + }; + } + 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; + } + } +} diff --git a/src/ActiveForm.php b/src/ActiveForm.php new file mode 100644 index 0000000..8e935bd --- /dev/null +++ b/src/ActiveForm.php @@ -0,0 +1,109 @@ + '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 + * @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); + } +} diff --git a/src/Alert.php b/src/Alert.php new file mode 100644 index 0000000..68ce8c0 --- /dev/null +++ b/src/Alert.php @@ -0,0 +1,148 @@ + [ + * '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 + * @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 '×'. + * + * 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', '×'); + 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); + } + } +} diff --git a/src/BaseHtml.php b/src/BaseHtml.php new file mode 100644 index 0000000..2e76f5c --- /dev/null +++ b/src/BaseHtml.php @@ -0,0 +1,145 @@ + + * @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 '
    ' . static::radio($name, $checked, $options) . '
    '; + }; + } + + 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 '
    ' . Html::checkbox($name, $checked, $options) . '
    '; + }; + } + + 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); + } +} diff --git a/src/BootstrapAsset.php b/src/BootstrapAsset.php new file mode 100644 index 0000000..313a787 --- /dev/null +++ b/src/BootstrapAsset.php @@ -0,0 +1,24 @@ + + * @since 2.0 + */ +class BootstrapAsset extends AssetBundle +{ + public $sourcePath = '@bower/bootstrap/dist'; + public $css = [ + 'css/bootstrap.css', + ]; +} diff --git a/src/BootstrapPluginAsset.php b/src/BootstrapPluginAsset.php new file mode 100644 index 0000000..6af1adc --- /dev/null +++ b/src/BootstrapPluginAsset.php @@ -0,0 +1,28 @@ + + * @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', + ]; +} diff --git a/src/BootstrapThemeAsset.php b/src/BootstrapThemeAsset.php new file mode 100644 index 0000000..60747a9 --- /dev/null +++ b/src/BootstrapThemeAsset.php @@ -0,0 +1,27 @@ + + * @since 2.0 + */ +class BootstrapThemeAsset extends AssetBundle +{ + public $sourcePath = '@bower/bootstrap/dist'; + public $css = [ + 'css/bootstrap-theme.css', + ]; + public $depends = [ + 'yii\bootstrap\BootstrapAsset', + ]; +} diff --git a/src/BootstrapWidgetTrait.php b/src/BootstrapWidgetTrait.php new file mode 100644 index 0000000..955feef --- /dev/null +++ b/src/BootstrapWidgetTrait.php @@ -0,0 +1,107 @@ + + * @author Qiang Xue + * @author Paul Klimov + * @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(); +} diff --git a/src/Button.php b/src/Button.php new file mode 100644 index 0000000..7d4d495 --- /dev/null +++ b/src/Button.php @@ -0,0 +1,60 @@ + 'Action', + * 'options' => ['class' => 'btn-lg'], + * ]); + * ``` + * @see http://getbootstrap.com/javascript/#buttons + * @author Antonio Ramirez + * @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); + } +} diff --git a/src/ButtonDropdown.php b/src/ButtonDropdown.php new file mode 100644 index 0000000..310b4a0 --- /dev/null +++ b/src/ButtonDropdown.php @@ -0,0 +1,160 @@ + '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 + * @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' => '', + 'encodeLabel' => false, + 'options' => $this->options, + 'view' => $this->getView(), + ]); + } else { + $label .= ' '; + $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); + } +} diff --git a/src/ButtonGroup.php b/src/ButtonGroup.php new file mode 100644 index 0000000..832f086 --- /dev/null +++ b/src/ButtonGroup.php @@ -0,0 +1,106 @@ + [ + * ['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 + * @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); + } +} diff --git a/src/Carousel.php b/src/Carousel.php new file mode 100644 index 0000000..99fdf3a --- /dev/null +++ b/src/Carousel.php @@ -0,0 +1,180 @@ + [ + * // the item contains only the image + * '', + * // equivalent to the above + * ['content' => ''], + * // the item contains both the image and the caption + * [ + * 'content' => '', + * 'caption' => '

    This is title

    This is the caption text

    ', + * 'options' => [...], + * ], + * ] + * ]); + * ``` + * + * @see http://getbootstrap.com/javascript/#carousel + * @author Antonio Ramirez + * @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 = ['‹', '›']; + /** + * @var bool whether carousel indicators (
      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' => '', + * // optional, the caption (HTML) of the slide + * 'caption' => '

      This is title

      This is the caption text

      ', + * // 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.'); + } + } +} diff --git a/src/Collapse.php b/src/Collapse.php new file mode 100644 index 0000000..20945b5 --- /dev/null +++ b/src/Collapse.php @@ -0,0 +1,233 @@ + [ + * // 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 + * @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); + } +} diff --git a/src/Dropdown.php b/src/Dropdown.php new file mode 100644 index 0000000..de3c014 --- /dev/null +++ b/src/Dropdown.php @@ -0,0 +1,143 @@ + + * Label + * [ + * ['label' => 'DropdownA', 'url' => '/'], + * ['label' => 'DropdownB', 'url' => '#'], + * ], + * ]); + * ?> + * + * ``` + * @see http://getbootstrap.com/javascript/#dropdowns + * @author Antonio Ramirez + * @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 ``. + */ + 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); + } +} diff --git a/src/Html.php b/src/Html.php new file mode 100644 index 0000000..1941106 --- /dev/null +++ b/src/Html.php @@ -0,0 +1,22 @@ + + * @since 2.0.5 + */ +class Html extends BaseHtml +{ +} diff --git a/src/InputWidget.php b/src/InputWidget.php new file mode 100644 index 0000000..353b79b --- /dev/null +++ b/src/InputWidget.php @@ -0,0 +1,19 @@ + + * @since 2.0.6 + */ +class InputWidget extends \yii\widgets\InputWidget +{ + use BootstrapWidgetTrait; +} diff --git a/src/Modal.php b/src/Modal.php new file mode 100644 index 0000000..96b1264 --- /dev/null +++ b/src/Modal.php @@ -0,0 +1,256 @@ + '

      Hello world

      ', + * 'toggleButton' => ['label' => 'click me'], + * ]); + * + * echo 'Say hello...'; + * + * Modal::end(); + * ~~~ + * + * @see http://getbootstrap.com/javascript/#modals + * @author Antonio Ramirez + * @author Qiang Xue + * @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 '×'. + * + * 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', '×'); + 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']; + } + } + } +} diff --git a/src/Nav.php b/src/Nav.php new file mode 100644 index 0000000..5e18428 --- /dev/null +++ b/src/Nav.php @@ -0,0 +1,298 @@ + [ + * [ + * 'label' => 'Home', + * 'url' => ['site/index'], + * 'linkOptions' => [...], + * ], + * [ + * 'label' => 'Dropdown', + * 'items' => [ + * ['label' => 'Level 1 - Dropdown A', 'url' => '#'], + * '
    1. ', + * '', + * ['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 + * @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 `` 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 = ''; + } + 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; + } +} diff --git a/src/NavBar.php b/src/NavBar.php new file mode 100644 index 0000000..fff1f87 --- /dev/null +++ b/src/NavBar.php @@ -0,0 +1,173 @@ + '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 + * @author Alexander Kochetov + * @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 = "{$this->screenReaderToggleText}"; + + return Html::button("{$screenReader}\n{$bar}\n{$bar}\n{$bar}", [ + 'class' => 'navbar-toggle', + 'data-toggle' => 'collapse', + 'data-target' => "#{$this->containerOptions['id']}", + ]); + } +} diff --git a/src/Progress.php b/src/Progress.php new file mode 100644 index 0000000..6ba6d45 --- /dev/null +++ b/src/Progress.php @@ -0,0 +1,166 @@ + 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 + * @author Alexander Makarov + * @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; + } +} diff --git a/src/Tabs.php b/src/Tabs.php new file mode 100644 index 0000000..49c3bec --- /dev/null +++ b/src/Tabs.php @@ -0,0 +1,319 @@ + [ + * [ + * '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 + * @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 .= ' '; + 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) : ''; + } +} diff --git a/src/ToggleButtonGroup.php b/src/ToggleButtonGroup.php new file mode 100644 index 0000000..05883e6 --- /dev/null +++ b/src/ToggleButtonGroup.php @@ -0,0 +1,115 @@ +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 + * @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]); + } +} diff --git a/src/Widget.php b/src/Widget.php new file mode 100644 index 0000000..3be95f3 --- /dev/null +++ b/src/Widget.php @@ -0,0 +1,26 @@ + + * @author Qiang Xue + * @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 = []; +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index a0c888d..40902b4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,6 +12,6 @@ require_once(__DIR__ . '/../vendor/autoload.php'); require_once(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); Yii::setAlias('@yiiunit/extensions/bootstrap', __DIR__); -Yii::setAlias('@yii/bootstrap', dirname(__DIR__)); +Yii::setAlias('@yii/bootstrap', dirname(__DIR__) . '/src'); require_once(__DIR__ . '/compatibility.php'); \ No newline at end of file