Файл: cobisja/BootHelp/src/Dropdown.php
Строк: 524
<?php
/**
 * BootHelp - PHP Helpers for Bootstrap
 *
 * (The MIT License)
 *
 * Copyright (c) 2015 Jorge Cobis <jcobis@gmail.com / http://twitter.com/cobisja>.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
namespace cobisjaBootHelp;
use cobisjaBootHelpBase;
use cobisjaBootHelpHelpersContentTag;
use cobisjaBootHelpHelpersLinkTo;
/**
 * Generates an HTML block tag that follows the Bootstrap documentation
 * on how to display <strong>Dropdown</strong> component.
 *
 * See {@link http://getbootstrap.com/components/#dropdowns} for more information.
 */
class Dropdown extends Base
{
    /**
     * Initializes the Dropdown instance.
     *
     * @param string  $caption Dropdown caption.
     * @param array   $options options to build the Dropdown.
     * @param closure $block closure to build the Dropdown content.
     */
    public function __construct($caption, $options = [], $block = null)
    {
        if (is_callable($options)) {
            $block = $options;
            $options = [];
        }
        $options['id'] = isset($options['id']) ? $options['id'] : 'label-dropdown-' . (string)(mt_rand(1, pow(10, 10)));
        $options['caption'] = $caption . Base::SPACE;
        $options['div_class'] = $this->dropdownDivClass($options);
        $options['button_class'] = $this->dropdownButtonClass($options);
        $options['list_class'] = $this->dropdownListClass($options);
        Base::setDropdownLink(true);
        $yield = is_callable($block) ? call_user_func($block) : null;
        if ((isset($options['into_navbar']) && $options['into_navbar']) || ('' !== Base::getNavbarId())) {
            $dropdown = $this->buildStandardDropdownIntoNavbar($options, $yield);
        } elseif (isset($options['into_nav']) && $options['into_nav'] || Base::getNavLink()) {
            $dropdown = $this->buildStandardDropdownIntoNav($options, $yield);
        } else {
            $dropdown = isset($options['split']) && $options['split'] ?
                $this->buildSplitDropdown($options, $yield) : $this->buildStandardDropdown($options, $yield);
        }
        
        Base::setDropdownLink(false);
        $this->setHtmlObject($dropdown->getHtmlObject());
    }
    /**
     * Builds a Standard Dropdown.
     *
     * @param array $options Dropdown's options.
     * @param mixed $yield Dropdown's content.
     * 
     * @return ContentTag Instance that represents a Dropdown,
     * in this case a button that triggers an unordered html list.
     */
    private function buildStandardDropdown($options, $yield)
    {
        return
            new ContentTag('div', ['class'=>$options['div_class']], function () use ($options, $yield) {
                return [
                    new ContentTag(
                        'button',
                        ['class'=>'dropdown-toggle ' . $options['button_class'],
                        'type'=>'button',
                        'id'=>$options['id'],
                        'data-toggle'=>'dropdown'],
                        function () use ($options) {
                            return [$options['caption'] , new ContentTag('span', '', ['class'=>'caret'])];
                        }
                    ),
                    new ContentTag(
                        'ul',
                        $yield,
                        ['class'=>$options['list_class'],
                        'role'=>'menu',
                        'aria-labelledby'=>$options['id']]
                    )
                ];
            });
    }
    /**
     * Builds a standard Dropdown that is rendered within a Navbar, so the Button
     * is not going to be generated instead a LinkTo is generated to trigger the
     * dropdown menu.
     *
     * @param array $options Dropdown's options.
     * @param mixed $yield Dropdown's content.
     * 
     * @return ContentTag a ContentTag instance that represents a Dropdown within a NavBar.
     */
    private function buildStandardDropdownIntoNavbar($options, $yield)
    {
        return
            new ContentTag('div', ['class'=>$options['div_class']], function () use ($options, $yield) {
                return new ContentTag('ul', ['class'=>'nav navbar-nav'], function () use ($options, $yield) {
                    return $this->buildStandardDropdownIntoNav($options, $yield);
                });
            });
    }
    /**
     * Builds a standard Dropdown that is rendered within a Nav, so the Button
     * is not going to be generated instead a LinkTo is generated to trigger the
     * dropdown menu.
     *
     * @param array $options Dropdown's options.
     * @param mixed $yield Dropdown's content.
     * 
     * @return ContentTag a ContentTag instance that represents a Dropdown within a Nav.
     */
    private function buildStandardDropdownIntoNav($options, $yield)
    {
        return new ContentTag('li', ['class'=>'dropdown'], function () use ($options, $yield) {
            $nav_link_status = Base::getNavLink();
            $dropdown_link_status = Base::getDropdownLink();
            Base::setDropdownLink(false);
            Base::setNavLink(false);
            
            $link =  new LinkTo(
                ['href'=>'#', 'class'=>'dropdown-toggle', 'data-toggle'=>'dropdown'],
                function () use ($options) {
                    return [
                        $options['caption'],
                        new ContentTag('span', '', ['class'=>'caret'])
                    ];
                }
            );
            
            Base::setNavLink($nav_link_status);
            Base::setDropdownLink($dropdown_link_status);
            
            return [
                $link,
                new ContentTag(
                    'ul',
                    $yield,
                    ['class'=>$options['list_class'], 'role'=>'menu', 'aria-labelledby'=>$options['id']]
                )
            ];
        });
    }
    /**
     * Builds a Split Dropdown.
     *
     * @param array $options Dropdown's options.
     * @param mixed $yield Dropdown's content.
     * 
     * @return ContentTag a Contentag instance that represents a Split Dropdown, 
     * in this case 2 buttons, one of them triggers an unordered html list.
     */
    private function buildSplitDropdown($options, $yield)
    {
        return
            new ContentTag(
                'div',
                ['class'=>$options['div_class']],
                function () use ($options, $yield) {
                    return [
                        new ContentTag(
                            'button',
                            $options['caption'],
                            ['type'=>'button', 'class'=>$options['button_class']]
                        ),
                        new ContentTag(
                            'button',
                            [
                                'class'=>'dropdown-toggle ' . $options['button_class'],
                                'type'=>'button', 'id'=>$options['id'],
                                'data-toggle'=>'dropdown'
                            ],
                            function () {
                                return [
                                    new ContentTag('span', '', ['class'=>'caret']),
                                    new ContentTag('span', 'Toggle Dropdown', ['class'=>'sr-only'])
                                ];
                            }
                        ),
                        new ContentTag(
                            'ul',
                            $yield,
                            ['class'=>$options['list_class'],
                            'role'=>'menu',
                            'aria-labelledby'=>$options['id']]
                        )
                    ];
                }
            );
    }
    /**
     * Returns the class for the div that contains the Dropdown.
     *
     * @param array $options Div's class options.
     * 
     * @return string Div's class.
     */
    private function dropdownDivClass($options = [])
    {
        $group = (isset($options['groupable']) && $options['groupable']) ||
                 (isset($options['split']) && $options['split'] ) ||
                 (isset($options['align']) && 'right' === $options['align'] )? 'btn-group' : null;
        $direction = isset($options['direction']) && $options['direction'] === 'up' ? 'dropup' : 'dropdown';
        return join(Base::SPACE, array_filter([$group, $direction], 'strlen'));
    }
    /**
     * Returns the class related with alignment.
     *
     * @param array $options alignment options.
     * 
     * @return string alignment class.
     */
    private function dropdownListClass($options = [])
    {
        $align = isset($options['align']) && $options['align'] === 'right' ? 'dropdown-menu-right' : null;
        return  join(Base::SPACE, array_filter(['dropdown-menu', $align], 'strlen'));
    }
    /**
     * Returns the class information associated to the Button.
     *
     * @param array $options class options information.
     * 
     * @return string button's class.
     */
    private function dropdownButtonClass($options = [])
    {
        $base_options = [
            'context' => null,
            'size' => '',
        ];
        $valid_contexts = [ 'primary', 'success', 'info', 'warning', 'danger', 'link' ];
        $this->setOptions($base_options, $options);
        $context = $this->contextFor($options['context'], ['valid' => $valid_contexts]);
        $button_class = isset($options['button']['class']) ? $options['button']['class'] : null;
        switch ($options['size']) {
            case 'lg':
            case 'large':
                $size = 'btn-lg';
                break;
            case 'sm':
            case 'small':
                $size = 'btn-sm';
                break;
            case 'xs':
            case 'extra_small':
                $size = 'btn-xs';
                break;
            default:
                $size = null;
        }
        unset($options['context']);
        unset($options['size']);
        return join(Base::SPACE, array_filter([$button_class, 'btn', "btn-$context", $size], 'strlen'));
    }
}