diff --git a/src/View/Helper/BootstrapHelper.php b/src/View/Helper/BootstrapHelper.php new file mode 100644 index 0000000..b44c0f4 --- /dev/null +++ b/src/View/Helper/BootstrapHelper.php @@ -0,0 +1,298 @@ +Bootstrap->Tabs([ + * 'pills' => true, + * 'card' => true, + * 'data' => [ + * 'navs' => [ + * 'tab1', + * ['text' => 'tab2', 'active' => true], + * ['html' => 'tab3', 'disabled' => true], + * ], + * 'content' => [ + * 'body1', + * 'body2', + * '~body3~' + * ] + * ] + * ]); + */ + +namespace App\View\Helper; + +use Cake\View\Helper; +use Cake\Utility\Security; +use InvalidArgumentException; + +class BootstrapHelper extends Helper +{ + public function tabs($options) + { + $bsTabs = new BootstrapTabs($options); + return $bsTabs->tabs(); + } +} + +class BootstrapTabs extends Helper +{ + private $defaultOptions = [ + 'nav-fill' => false, + 'nav-justify' => false, + 'pills' => false, + 'vertical' => false, + 'vertical-size' => 3, + 'card' => false, + 'nav-class' => [], + 'nav-item-class' => [], + 'content-class' => [], + 'data' => [ + 'navs' => [], + 'content' => [], + ], + ]; + + private $allowedOptionValues = [ + 'nav-justify' => [false, 'center', 'end'], + ]; + + private $options = null; + private $bsClasses = null; + + function __construct($options) { + $this->processOptions($options); + } + + public function tabs() + { + return $this->genTabs(); + } + + private function processOptions($options) + { + $this->options = array_merge($this->defaultOptions, $options); + $this->data = $this->options['data']; + $this->checkOptionValidity(); + $this->bsClasses = [ + 'nav' => [], + 'nav-item' => $this->options['nav-item-class'], + + ]; + + if (!empty($this->options['nav-justify'])) { + $this->bsClasses['nav'][] = 'justify-content-' . $this->options['nav-justify']; + } + + if ($this->options['pills']) { + $this->bsClasses['nav'][] = 'nav-pills'; + if ($this->options['vertical']) { + $this->bsClasses['nav'][] = 'flex-column'; + } + if ($this->options['card']) { + $this->bsClasses['nav'][] = 'card-header-pills'; + } + } else { + $this->bsClasses['nav'][] = 'nav-tabs'; + if ($this->options['card']) { + $this->bsClasses['nav'][] = 'card-header-tabs'; + } + } + + if ($this->options['nav-fill']) { + $this->bsClasses['nav'][] = 'nav-fill'; + } + if ($this->options['nav-justify']) { + $this->bsClasses['nav'][] = 'nav-justify'; + } + + $activeTab = 0; + foreach ($this->data['navs'] as $i => $nav) { + if (!is_array($nav)) { + $this->data['navs'][$i] = ['text' => $nav]; + } + if (!isset($this->data['navs'][$i]['id'])) { + $this->data['navs'][$i]['id'] = 't-' . Security::randomString(8); + } + if (!empty($nav['active'])) { + $activeTab = $i; + } + } + $this->data['navs'][$activeTab]['active'] = true; + + $this->options['vertical-size'] = $this->options['vertical-size'] < 0 || $this->options['vertical-size'] > 11 ? 3 : $this->options['vertical-size']; + } + + private function checkOptionValidity() + { + foreach ($this->allowedOptionValues as $option => $values) { + if (!isset($this->options[$option])) { + throw new InvalidArgumentException(__('Option `{0}` should have a value', $option)); + } + if (!in_array($this->options[$option], $values)) { + throw new InvalidArgumentException(__('Option `{0}` is not a valid option for `{1}`. Accepted values: {2}', json_encode($this->options[$option]), $option, json_encode($values))); + } + } + if (empty($this->data['navs'])) { + throw new InvalidArgumentException(__('No navigation data provided')); + } + if ($this->options['card'] && $this->options['vertical']) { + throw new InvalidArgumentException(__('`card` option can only be used on horizontal mode')); + } + } + + private function genTabs() + { + $html = ''; + if ($this->options['vertical']) { + $html .= $this->genVerticalTabs(); + } else { + $html .= $this->genHorizontalTabs(); + } + return $html; + } + + private function genHorizontalTabs() + { + $html = ''; + if ($this->options['card']) { + $html .= $this->genNode('div', ['class' => ['card']]); + $html .= $this->genNode('div', ['class' => ['card-header']]); + } + $html .= $this->genNav(); + if ($this->options['card']) { + $html .= ''; + $html .= $this->genNode('div', ['class' => ['card-body']]); + } + $html .= $this->genContent(); + if ($this->options['card']) { + $html .= ''; + } + return $html; + } + + private function genVerticalTabs() + { + $html = sprintf('