Tabs.php 8.85 KB
Newer Older
Antonio Ramirez committed
1 2 3 4 5 6 7 8 9 10
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\bootstrap;

use yii\base\InvalidConfigException;
11
use yii\helpers\ArrayHelper;
Antonio Ramirez committed
12 13 14 15 16 17 18 19
use yii\helpers\Html;

/**
 * Tabs renders a Tab bootstrap javascript component.
 *
 * For example:
 *
 * ```php
Alexander Makarov committed
20 21 22
 * echo Tabs::widget([
 *     'items' => [
 *         [
23
 *             'label' => 'One',
Antonio Ramirez committed
24
 *             'content' => 'Anim pariatur cliche...',
25
 *             'active' => true
Alexander Makarov committed
26 27
 *         ],
 *         [
28
 *             'label' => 'Two',
Antonio Ramirez committed
29
 *             'content' => 'Anim pariatur cliche...',
Alexander Makarov committed
30 31 32 33
 *             'headerOptions' => [...],
 *             'options' => ['id' => 'myveryownID'],
 *         ],
 *         [
34
 *             'label' => 'Dropdown',
Alexander Makarov committed
35 36
 *             'items' => [
 *                  [
Antonio Ramirez committed
37 38
 *                      'label' => 'DropdownA',
 *                      'content' => 'DropdownA, Anim pariatur cliche...',
Alexander Makarov committed
39 40
 *                  ],
 *                  [
Antonio Ramirez committed
41 42
 *                      'label' => 'DropdownB',
 *                      'content' => 'DropdownB, Anim pariatur cliche...',
Alexander Makarov committed
43 44 45 46 47
 *                  ],
 *             ],
 *         ],
 *     ],
 * ]);
Antonio Ramirez committed
48 49
 * ```
 *
MarsuBoss committed
50
 * @see http://getbootstrap.com/javascript/#tabs
Antonio Ramirez committed
51 52 53 54 55
 * @author Antonio Ramirez <amigo.cobos@gmail.com>
 * @since 2.0
 */
class Tabs extends Widget
{
56 57 58 59 60
    /**
     * @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.
61 62
     * - encode: boolean, optional, whether this label should be HTML-encoded. This param will override
     *   global `$this->encodeLabels` param.
63 64
     * - headerOptions: array, optional, the HTML attributes of the tab header.
     * - linkOptions: array, optional, the HTML attributes of the tab header link tags.
65
     * - content: string, optional, the content (HTML) of the tab pane.
66 67
     * - options: array, optional, the HTML attributes of the tab pane container.
     * - active: boolean, optional, whether the item tab header and pane should be visible or not.
68
     * - items: array, optional, can be used instead of `content` to specify a dropdown items
Dmitry Chernikov committed
69
     *   configuration array. Each item can hold three extra keys, besides the above ones:
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
     *     * active: boolean, 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 boolean whether the labels for header items should be HTML-encoded.
     */
    public $encodeLabels = true;
    /**
     * @var string specifies the Bootstrap tab styling.
     */
    public $navType = 'nav-tabs';
RomeroMsk committed
104
    /**
105 106 107
     * @var boolean 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
RomeroMsk committed
108 109
     */
    public $renderTabContent = true;
110

111

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    /**
     * Initializes the widget.
     */
    public function init()
    {
        parent::init();
        Html::addCssClass($this->options, 'nav ' . $this->navType);
    }

    /**
     * Renders the widget.
     */
    public function run()
    {
        echo $this->renderItems();
        $this->registerPlugin('tab');
    }

    /**
     * Renders tab items as specified on [[items]].
132
     * @return string the rendering result.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
     * @throws InvalidConfigException.
     */
    protected function renderItems()
    {
        $headers = [];
        $panes = [];

        if (!$this->hasActiveTab() && !empty($this->items)) {
            $this->items[0]['active'] = true;
        }

        foreach ($this->items as $n => $item) {
            if (!isset($item['label'])) {
                throw new InvalidConfigException("The 'label' option is required.");
            }
148
            $encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
149
            $label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
            $headerOptions = array_merge($this->headerOptions, ArrayHelper::getValue($item, 'headerOptions', []));
            $linkOptions = array_merge($this->linkOptions, ArrayHelper::getValue($item, 'linkOptions', []));

            if (isset($item['items'])) {
                $label .= ' <b class="caret"></b>';
                Html::addCssClass($headerOptions, 'dropdown');

                if ($this->renderDropdown($item['items'], $panes)) {
                    Html::addCssClass($headerOptions, 'active');
                }

                Html::addCssClass($linkOptions, 'dropdown-toggle');
                $linkOptions['data-toggle'] = 'dropdown';
                $header = Html::a($label, "#", $linkOptions) . "\n"
                    . Dropdown::widget(['items' => $item['items'], 'clientOptions' => false, 'view' => $this->getView()]);
165
            } else {
166 167 168 169 170 171 172 173 174 175
                $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', []));
                $options['id'] = ArrayHelper::getValue($options, 'id', $this->options['id'] . '-tab' . $n);

                Html::addCssClass($options, 'tab-pane');
                if (ArrayHelper::remove($item, 'active')) {
                    Html::addCssClass($options, 'active');
                    Html::addCssClass($headerOptions, 'active');
                }
                $linkOptions['data-toggle'] = 'tab';
                $header = Html::a($label, '#' . $options['id'], $linkOptions);
RomeroMsk committed
176 177 178
                if ($this->renderTabContent) {
                    $panes[] = Html::tag('div', isset($item['content']) ? $item['content'] : '', $options);
                }
179 180 181 182 183
            }

            $headers[] = Html::tag('li', $header, $headerOptions);
        }

RomeroMsk committed
184 185
        return Html::tag('ul', implode("\n", $headers), $this->options)
        . ($this->renderTabContent ? "\n" . Html::tag('div', implode("\n", $panes), ['class' => 'tab-content']) : '');
186 187 188 189 190 191 192 193
    }

    /**
     * @return boolean if there's active tab defined
     */
    protected function hasActiveTab()
    {
        foreach ($this->items as $item) {
194
            if (isset($item['active']) && $item['active'] === true) {
195 196 197 198 199 200 201 202 203 204
                return true;
            }
        }

        return false;
    }

    /**
     * Normalizes dropdown item options by removing tab specific keys `content` and `contentOptions`, and also
     * configure `panes` accordingly.
205 206 207
     * @param array $items the dropdown items configuration.
     * @param array $panes the panes reference array.
     * @return boolean whether any of the dropdown items is `active` or not.
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
     * @throws InvalidConfigException
     */
    protected function renderDropdown(&$items, &$panes)
    {
        $itemActive = false;

        foreach ($items as $n => &$item) {
            if (is_string($item)) {
                continue;
            }
            if (!isset($item['content'])) {
                throw new InvalidConfigException("The 'content' option is required.");
            }

            $content = ArrayHelper::remove($item, 'content');
            $options = ArrayHelper::remove($item, 'contentOptions', []);
            Html::addCssClass($options, '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-tab' . $n);
            $item['url'] = '#' . $options['id'];
            $item['linkOptions']['data-toggle'] = 'tab';

            $panes[] = Html::tag('div', $content, $options);

            unset($item);
        }

        return $itemActive;
    }
Qiang Xue committed
242
}