[ * [ * 'url' => ['/site/index'], * 'label' => 'Home', * 'icon' => 'home' * ], * [ * 'url' => ['/site/about'], * 'label' => 'About', * 'icon' => 'info-sign', * 'items' => [ * ['url' => '#', 'label' => 'Item 1'], * ['url' => '#', 'label' => 'Item 2'], * ], * ], * ], * ]); * ``` * * @author Kartik Visweswaran */ class SideNav extends \yii\widgets\Menu { /** * Panel contextual states */ const TYPE_DEFAULT = 'default'; const TYPE_PRIMARY = 'primary'; const TYPE_INFO = 'info'; const TYPE_SUCCESS = 'success'; const TYPE_DANGER = 'danger'; const TYPE_WARNING = 'warning'; /** * * @var string the menu container style. This is one of the bootstrap panel * contextual state classes. Defaults to `default`. * @see http://getbootstrap.com/components/#panels */ public $type = self::TYPE_DEFAULT; /** * * @var string prefix for the icon in [[items]]. This string will be prepended * before the icon name to get the icon CSS class. This defaults to `glyphicon glyphicon-` * for usage with glyphicons available with Bootstrap. */ public $iconPrefix = 'glyphicon glyphicon-'; /** * * @var array string/boolean the sidenav heading. This is not HTML encoded * When set to false or null, no heading container will be displayed. */ public $heading = false; /** * * @var array options for the sidenav heading */ public $headingOptions = [ ]; /** * * @var array options for the sidenav container */ public $containerOptions = [ ]; /** * * @var string indicator for a menu sub-item */ public $indItem = '» '; /** * * @var string indicator for a opened sub-menu */ public $indMenuOpen = ''; /** * * @var string indicator for a closed sub-menu */ public $indMenuClose = ''; /** * * @var array list of sidenav menu items. Each menu item should be an array of the following structure: * * - label: string, optional, specifies the menu item label. When [[encodeLabels]] is true, the label * will be HTML-encoded. If the label is not specified, an empty string will be used. * - icon: string, optional, specifies the glyphicon name to be placed before label. * - url: string or array, optional, specifies the URL of the menu item. It will be processed by [[Url::to]]. * When this is set, the actual menu item content will be generated using [[linkTemplate]]; * - visible: boolean, optional, whether this menu item is visible. Defaults to true. * - items: array, optional, specifies the sub-menu items. Its format is the same as the parent items. * - active: boolean, optional, whether this menu item is in active state (currently selected). * If a menu item is active, its CSS class will be appended with [[activeCssClass]]. * If this option is not set, the menu item will be set active automatically when the current request * is triggered by [[url]]. For more details, please refer to [[isItemActive()]]. * - template: string, optional, the template used to render the content of this menu item. * The token `{url}` will be replaced by the URL associated with this menu item, * and the token `{label}` will be replaced by the label of the menu item. * If this option is not set, [[linkTemplate]] will be used instead. * - options: array, optional, the HTML attributes for the menu item tag. * */ public $items; /** * Allowed panel stypes */ private static $_validTypes = [ self::TYPE_DEFAULT, self::TYPE_PRIMARY, self::TYPE_INFO, self::TYPE_SUCCESS, self::TYPE_DANGER, self::TYPE_WARNING ]; public function init() { parent::init (); SideNavAsset::register ( $this->getView () ); $this->activateParents = true; $this->submenuTemplate = "\n\n"; $this->linkTemplate = '{icon}{label}'; $this->labelTemplate = '{icon}{label}'; $this->markTopItems (); Html::addCssClass ( $this->options, 'nav nav-pills nav-stacked kv-sidenav' ); } /** * Renders the side navigation menu. * with the heading and panel containers */ public function run() { $heading = ''; if (isset ( $this->heading ) && $this->heading != '') { Html::addCssClass ( $this->headingOptions, 'panel-heading' ); $heading = Html::tag ( 'div', '

' . $this->heading . '

', $this->headingOptions ); } $body = Html::tag ( 'div', $this->renderMenu (), [ 'class' => 'table' ] ); $type = in_array ( $this->type, self::$_validTypes ) ? $this->type : self::TYPE_DEFAULT; Html::addCssClass ( $this->containerOptions, "panel panel-{$type}" ); echo Html::tag ( 'div', $heading . $body, $this->containerOptions ); } /** * Renders the main menu */ protected function renderMenu() { if ($this->route === null && Yii::$app->controller !== null) { $this->route = Yii::$app->controller->getRoute (); } if ($this->params === null) { $this->params = $_GET; } $items = $this->normalizeItems ( $this->items, $hasActiveChild ); $options = $this->options; $tag = ArrayHelper::remove ( $options, 'tag', 'ul' ); return Html::tag ( $tag, $this->renderItems ( $items ), $options ); } /** * Marks each topmost level item which is not a submenu */ protected function markTopItems() { $items = [ ]; foreach ( $this->items as $item ) { if (empty ( $item ['items'] )) { $item ['top'] = true; } $items [] = $item; } $this->items = $items; } /** * Renders the content of a side navigation menu item. * * @param array $item * the menu item to be rendered. Please refer to [[items]] to see what data might be in the item. * @return string the rendering result * @throws InvalidConfigException */ protected function renderItem($item) { $this->validateItems ( $item ); $template = ArrayHelper::getValue ( $item, 'template', $this->linkTemplate ); $url = Url::to ( ArrayHelper::getValue ( $item, 'url', '#' ) ); if (empty ( $item ['top'] )) { if (empty ( $item ['items'] )) { $template = str_replace ( '{icon}', $this->indItem . '{icon}', $template ); } else { $template = isset ( $item ['template'] ) ? $item ['template'] : '{icon}{label}'; $openOptions = ($item ['active']) ? [ 'class' => 'opened' ] : [ 'class' => 'opened', 'style' => 'display:none' ]; $closeOptions = ($item ['active']) ? [ 'class' => 'closed', 'style' => 'display:none' ] : [ 'class' => 'closed' ]; $indicator = Html::tag ( 'span', $this->indMenuOpen, $openOptions ) . Html::tag ( 'span', $this->indMenuClose, $closeOptions ); $template = str_replace ( '{icon}', $indicator . '{icon}', $template ); } } $icon = empty ( $item ['icon'] ) ? '' : '  '; unset ( $item ['icon'], $item ['top'] ); return strtr ( $template, [ '{url}' => $url, '{label}' => $item ['label'], '{icon}' => $icon ] ); } /** * Validates each item for a valid label and url. * * @throws InvalidConfigException */ protected function validateItems($item) { if (! isset ( $item ['label'] )) { throw new InvalidConfigException ( "The 'label' option is required." ); } } }