2020-12-04 16:08:11 +01:00
< ? php
/**
* Bootstrap Tabs helper
* Options :
* [ style ]
2021-01-13 14:16:52 +01:00
* - fill : Should the navigation items occupy all available space
2020-12-11 08:52:11 +01:00
* - justify : Should the navigation items be justified ( accept : [ 'center' , 'end' ])
* - pills : Should the navigation items be pills
* - vertical : Should the navigation bar be placed on the left side of the content
* - vertical - size : Specify how many boostrap ' s `cols` should be used for the navigation ( only used when `vertical` is true )
2020-12-04 16:08:11 +01:00
* - card : Should the navigation be placed in a bootstrap card
2021-01-13 14:16:52 +01:00
* - header - variant : The bootstrap variant to be used for the card header
* - body - variant : The bootstrap variant to be used for the card body
2020-12-04 16:08:11 +01:00
* - nav - class : additional class to add to the nav container
* - content - class : additional class to add to the content container
* [ data ]
* - data : contains the data for the tabs and content
* {
* 'navs' : [{ nav - item }, { nav - item }, ... ],
* 'content' : [{ nav - content }, { nav - content }, ... ]
* }
*
* # Usage:
* echo $this -> Bootstrap -> Tabs ([
* 'pills' => true ,
* 'card' => true ,
* 'data' => [
* 'navs' => [
* 'tab1' ,
* [ 'text' => 'tab2' , 'active' => true ],
* [ 'html' => '<b>tab3</b>' , 'disabled' => true ],
* ],
* 'content' => [
* 'body1' ,
* '<i>body2</i>' ,
* '~body3~'
* ]
* ]
* ]);
*/
namespace App\View\Helper ;
use Cake\View\Helper ;
2021-06-12 11:57:49 +02:00
use Cake\Utility\Hash ;
2021-03-10 09:27:17 +01:00
use Cake\Utility\Inflector ;
2020-12-04 16:08:11 +01:00
use Cake\Utility\Security ;
use InvalidArgumentException ;
class BootstrapHelper extends Helper
{
2021-06-12 11:57:49 +02:00
public $helpers = [ 'FontAwesome' ];
2020-12-04 16:08:11 +01:00
public function tabs ( $options )
{
$bsTabs = new BootstrapTabs ( $options );
return $bsTabs -> tabs ();
}
2021-02-22 15:47:30 +01:00
public function alert ( $options )
{
$bsAlert = new BoostrapAlert ( $options );
return $bsAlert -> alert ();
}
2021-02-22 16:38:55 +01:00
public function table ( $options , $data )
{
$bsTable = new BoostrapTable ( $options , $data );
return $bsTable -> table ();
}
2021-02-23 11:25:20 +01:00
public function button ( $options )
{
$bsButton = new BoostrapButton ( $options );
return $bsButton -> button ();
}
2021-03-10 09:28:33 +01:00
public function badge ( $options )
{
$bsBadge = new BoostrapBadge ( $options );
return $bsBadge -> badge ();
}
2021-03-10 09:32:18 +01:00
public function modal ( $options )
{
2021-06-12 11:57:49 +02:00
$bsModal = new BoostrapModal ( $options );
return $bsModal -> modal ();
}
public function progress ( $options )
{
$bsProgress = new BoostrapProgress ( $options );
return $bsProgress -> progress ();
}
public function collapse ( $options , $content )
{
$bsCollapse = new BoostrapCollapse ( $options , $content , $this );
return $bsCollapse -> collapse ();
}
public function progressTimeline ( $options )
{
$bsProgressTimeline = new BoostrapProgressTimeline ( $options , $this );
return $bsProgressTimeline -> progressTimeline ();
2021-03-10 09:32:18 +01:00
}
2021-02-22 15:47:30 +01:00
}
class BootstrapGeneric
{
2021-03-10 09:27:17 +01:00
public static $variants = [ 'primary' , 'secondary' , 'success' , 'danger' , 'warning' , 'info' , 'light' , 'dark' , 'white' , 'transparent' ];
2021-06-12 11:57:49 +02:00
public static $textClassByVariants = [
'primary' => 'text-white' ,
'secondary' => 'text-white' ,
'success' => 'text-white' ,
'danger' => 'text-white' ,
'warning' => 'text-black' ,
'info' => 'text-white' ,
'light' => 'text-black' ,
'dark' => 'text-white' ,
'white' => 'text-black' ,
'transparent' => 'text-black'
];
2021-02-22 15:47:30 +01:00
protected $allowedOptionValues = [];
protected $options = [];
protected 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 )));
}
}
}
2021-02-23 08:45:35 +01:00
protected static function genNode ( $node , $params = [], $content = " " )
{
return sprintf ( '<%s %s>%s</%s>' , $node , BootstrapGeneric :: genHTMLParams ( $params ), $content , $node );
}
protected static function openNode ( $node , $params = [])
2021-02-22 15:47:30 +01:00
{
return sprintf ( '<%s %s>' , $node , BootstrapGeneric :: genHTMLParams ( $params ));
}
2021-02-23 08:45:35 +01:00
protected static function closeNode ( $node )
{
return sprintf ( '</%s>' , $node );
}
2021-02-22 15:47:30 +01:00
protected static function genHTMLParams ( $params )
{
$html = '' ;
foreach ( $params as $k => $v ) {
$html .= BootstrapGeneric :: genHTMLParam ( $k , $v ) . ' ' ;
}
return $html ;
}
protected static function genHTMLParam ( $paramName , $values )
{
if ( ! is_array ( $values )) {
$values = [ $values ];
}
return sprintf ( '%s="%s"' , $paramName , implode ( ' ' , $values ));
}
2021-03-10 09:27:17 +01:00
protected static function genericCloseButton ( $dismissTarget )
{
return BootstrapGeneric :: genNode ( 'button' , [
'type' => 'button' ,
'class' => 'close' ,
'data-dismiss' => $dismissTarget ,
'arial-label' => __ ( 'Close' )
], BootstrapGeneric :: genNode ( 'span' , [
'arial-hidden' => 'true'
], '×' ));
}
2021-06-12 11:57:49 +02:00
protected static function getTextClassForVariant ( $variant )
{
return ! empty ( self :: $textClassByVariants [ $variant ]) ? self :: $textClassByVariants [ $variant ] : 'text-black' ;
}
2020-12-04 16:08:11 +01:00
}
2021-02-22 15:47:30 +01:00
class BootstrapTabs extends BootstrapGeneric
2020-12-04 16:08:11 +01:00
{
private $defaultOptions = [
2020-12-11 08:52:11 +01:00
'fill' => false ,
'justify' => false ,
2020-12-04 16:08:11 +01:00
'pills' => false ,
'vertical' => false ,
'vertical-size' => 3 ,
'card' => false ,
2021-01-13 14:16:52 +01:00
'header-variant' => 'light' ,
'body-variant' => '' ,
2020-12-04 16:08:11 +01:00
'nav-class' => [],
'nav-item-class' => [],
'content-class' => [],
'data' => [
'navs' => [],
'content' => [],
],
];
private $bsClasses = null ;
function __construct ( $options ) {
2021-02-22 15:47:30 +01:00
$this -> allowedOptionValues = [
'justify' => [ false , 'center' , 'end' ],
'body-variant' => array_merge ( BootstrapGeneric :: $variants , [ '' ]),
'header-variant' => BootstrapGeneric :: $variants ,
];
2020-12-04 16:08:11 +01:00
$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 ();
2021-02-22 15:47:30 +01:00
if ( empty ( $this -> data [ 'navs' ])) {
throw new InvalidArgumentException ( __ ( 'No navigation data provided' ));
}
2020-12-04 16:08:11 +01:00
$this -> bsClasses = [
'nav' => [],
'nav-item' => $this -> options [ 'nav-item-class' ],
];
2020-12-11 08:52:11 +01:00
if ( ! empty ( $this -> options [ 'justify' ])) {
$this -> bsClasses [ 'nav' ][] = 'justify-content-' . $this -> options [ 'justify' ];
2020-12-04 16:08:11 +01:00
}
2021-01-13 14:16:52 +01:00
if ( $this -> options [ 'vertical' ] && ! isset ( $options [ 'pills' ]) && ! isset ( $options [ 'card' ])) {
$this -> options [ 'pills' ] = true ;
$this -> options [ 'card' ] = true ;
}
2020-12-04 16:08:11 +01:00
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' ;
}
}
2020-12-11 08:52:11 +01:00
if ( $this -> options [ 'fill' ]) {
2020-12-04 16:08:11 +01:00
$this -> bsClasses [ 'nav' ][] = 'nav-fill' ;
}
2020-12-11 08:52:11 +01:00
if ( $this -> options [ 'justify' ]) {
2020-12-04 16:08:11 +01:00
$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' ];
2020-12-07 09:52:35 +01:00
2021-01-13 14:16:52 +01:00
$this -> options [ 'header-text-variant' ] = $this -> options [ 'header-variant' ] == 'light' ? 'body' : 'white' ;
$this -> options [ 'header-border-variant' ] = $this -> options [ 'header-variant' ] == 'light' ? '' : $this -> options [ 'header-variant' ];
$this -> options [ 'body-text-variant' ] = $this -> options [ 'body-variant' ] == '' ? 'body' : 'white' ;
2020-12-07 09:52:35 +01:00
if ( ! is_array ( $this -> options [ 'nav-class' ])) {
$this -> options [ 'nav-class' ] = [ $this -> options [ 'nav-class' ]];
}
if ( ! is_array ( $this -> options [ 'content-class' ])) {
$this -> options [ 'content-class' ] = [ $this -> options [ 'content-class' ]];
}
2020-12-04 16:08:11 +01:00
}
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' ]) {
2021-02-23 08:45:35 +01:00
$html .= $this -> openNode ( 'div' , [ 'class' => array_merge ([ 'card' ], [ " border- { $this -> options [ 'header-border-variant' ] } " ])]);
$html .= $this -> openNode ( 'div' , [ 'class' => array_merge ([ 'card-header' ], [ " bg- { $this -> options [ 'header-variant' ] } " , " text- { $this -> options [ 'header-text-variant' ] } " ])]);
2020-12-04 16:08:11 +01:00
}
$html .= $this -> genNav ();
if ( $this -> options [ 'card' ]) {
2021-02-23 08:45:35 +01:00
$html .= $this -> closeNode ( 'div' );
$html .= $this -> openNode ( 'div' , [ 'class' => array_merge ([ 'card-body' ], [ " bg- { $this -> options [ 'body-variant' ] } " , " text- { $this -> options [ 'body-text-variant' ] } " ])]);
2020-12-04 16:08:11 +01:00
}
$html .= $this -> genContent ();
if ( $this -> options [ 'card' ]) {
2021-02-23 08:45:35 +01:00
$html .= $this -> closeNode ( 'div' );
$html .= $this -> closeNode ( 'div' );
2020-12-04 16:08:11 +01:00
}
return $html ;
}
private function genVerticalTabs ()
{
2021-02-23 08:45:35 +01:00
$html = $this -> openNode ( 'div' , [ 'class' => array_merge ([ 'row' , ( $this -> options [ 'card' ] ? 'card flex-row' : '' )], [ " border- { $this -> options [ 'header-border-variant' ] } " ])]);
$html .= $this -> openNode ( 'div' , [ 'class' => array_merge ([ 'col-' . $this -> options [ 'vertical-size' ], ( $this -> options [ 'card' ] ? 'card-header border-right' : '' )], [ " bg- { $this -> options [ 'header-variant' ] } " , " text- { $this -> options [ 'header-text-variant' ] } " , " border- { $this -> options [ 'header-border-variant' ] } " ])]);
2020-12-04 16:08:11 +01:00
$html .= $this -> genNav ();
2021-02-23 08:45:35 +01:00
$html .= $this -> closeNode ( 'div' );
$html .= $this -> openNode ( 'div' , [ 'class' => array_merge ([ 'col-' . ( 12 - $this -> options [ 'vertical-size' ]), ( $this -> options [ 'card' ] ? 'card-body2' : '' )], [ " bg- { $this -> options [ 'body-variant' ] } " , " text- { $this -> options [ 'body-text-variant' ] } " ])]);
2020-12-04 16:08:11 +01:00
$html .= $this -> genContent ();
2021-02-23 08:45:35 +01:00
$html .= $this -> closeNode ( 'div' );
$html .= $this -> closeNode ( 'div' );
2020-12-04 16:08:11 +01:00
return $html ;
}
private function genNav ()
{
2021-02-23 08:45:35 +01:00
$html = $this -> openNode ( 'ul' , [
2020-12-04 16:08:11 +01:00
'class' => array_merge ([ 'nav' ], $this -> bsClasses [ 'nav' ], $this -> options [ 'nav-class' ]),
'role' => 'tablist' ,
2020-12-08 09:24:25 +01:00
]);
2020-12-04 16:08:11 +01:00
foreach ( $this -> data [ 'navs' ] as $navItem ) {
$html .= $this -> genNavItem ( $navItem );
}
2021-02-23 08:45:35 +01:00
$html .= $this -> closeNode ( 'ul' );
2020-12-04 16:08:11 +01:00
return $html ;
}
private function genNavItem ( $navItem )
{
2021-02-23 08:45:35 +01:00
$html = $this -> openNode ( 'li' , [
2020-12-04 16:08:11 +01:00
'class' => array_merge ([ 'nav-item' ], $this -> bsClasses [ 'nav-item' ], $this -> options [ 'nav-item-class' ]),
'role' => 'presentation' ,
2020-12-08 09:24:25 +01:00
]);
2021-02-23 08:45:35 +01:00
$html .= $this -> openNode ( 'a' , [
2020-12-04 16:08:11 +01:00
'class' => array_merge (
[ 'nav-link' ],
[ ! empty ( $navItem [ 'active' ]) ? 'active' : '' ],
[ ! empty ( $navItem [ 'disabled' ]) ? 'disabled' : '' ]
),
'data-toggle' => $this -> options [ 'pills' ] ? 'pill' : 'tab' ,
'id' => $navItem [ 'id' ] . '-tab' ,
'href' => '#' . $navItem [ 'id' ],
'aria-controls' => $navItem [ 'id' ],
'aria-selected' => ! empty ( $navItem [ 'active' ]),
'role' => 'tab' ,
2020-12-08 09:24:25 +01:00
]);
2020-12-04 16:08:11 +01:00
if ( ! empty ( $navItem [ 'html' ])) {
$html .= $navItem [ 'html' ];
} else {
$html .= h ( $navItem [ 'text' ]);
}
2021-02-23 08:45:35 +01:00
$html .= $this -> closeNode ( 'a' );
$html .= $this -> closeNode ( 'li' );
2020-12-04 16:08:11 +01:00
return $html ;
}
private function genContent ()
{
2021-02-23 08:45:35 +01:00
$html = $this -> openNode ( 'div' , [
2020-12-04 16:08:11 +01:00
'class' => array_merge ([ 'tab-content' ], $this -> options [ 'content-class' ]),
2020-12-08 09:24:25 +01:00
]);
2020-12-04 16:08:11 +01:00
foreach ( $this -> data [ 'content' ] as $i => $content ) {
$navItem = $this -> data [ 'navs' ][ $i ];
$html .= $this -> genContentItem ( $navItem , $content );
}
2021-02-23 08:45:35 +01:00
$html .= $this -> closeNode ( 'div' );
2020-12-04 16:08:11 +01:00
return $html ;
}
private function genContentItem ( $navItem , $content )
{
2021-02-23 08:45:35 +01:00
return $this -> genNode ( 'div' , [
2020-12-04 16:08:11 +01:00
'class' => array_merge ([ 'tab-pane' , 'fade' ], [ ! empty ( $navItem [ 'active' ]) ? 'show active' : '' ]),
'role' => 'tabpanel' ,
'id' => $navItem [ 'id' ],
'aria-labelledby' => $navItem [ 'id' ] . '-tab'
2021-02-23 08:45:35 +01:00
], $content );
2020-12-04 16:08:11 +01:00
}
2021-02-22 15:47:30 +01:00
}
class BoostrapAlert extends BootstrapGeneric {
private $defaultOptions = [
'text' => '' ,
'html' => null ,
'dismissible' => true ,
'variant' => 'primary' ,
'fade' => true
];
private $bsClasses = null ;
function __construct ( $options ) {
$this -> allowedOptionValues = [
'variant' => BootstrapGeneric :: $variants ,
];
$this -> processOptions ( $options );
}
2020-12-04 16:08:11 +01:00
2021-02-22 15:47:30 +01:00
private function processOptions ( $options )
{
$this -> options = array_merge ( $this -> defaultOptions , $options );
$this -> checkOptionValidity ();
}
public function alert ()
2020-12-04 16:08:11 +01:00
{
2021-02-22 15:47:30 +01:00
return $this -> genAlert ();
}
private function genAlert ()
{
2021-02-23 08:45:35 +01:00
$html = $this -> openNode ( 'div' , [
2021-02-22 15:47:30 +01:00
'class' => [
'alert' ,
" alert- { $this -> options [ 'variant' ] } " ,
$this -> options [ 'dismissible' ] ? 'alert-dismissible' : '' ,
$this -> options [ 'fade' ] ? 'fade show' : '' ,
],
'role' => " alert "
]);
$html .= $this -> genContent ();
$html .= $this -> genCloseButton ();
2021-02-23 08:45:35 +01:00
$html .= $this -> closeNode ( 'div' );
2021-02-22 15:47:30 +01:00
return $html ;
2020-12-04 16:08:11 +01:00
}
2021-02-22 15:47:30 +01:00
private function genCloseButton ()
2020-12-04 16:08:11 +01:00
{
$html = '' ;
2021-02-22 15:47:30 +01:00
if ( $this -> options [ 'dismissible' ]) {
2021-03-10 09:27:17 +01:00
$html .= $this -> genericCloseButton ( 'alert' );
2020-12-04 16:08:11 +01:00
}
return $html ;
}
2021-02-22 15:47:30 +01:00
private function genContent ()
2020-12-04 16:08:11 +01:00
{
2021-02-23 08:45:35 +01:00
return ! is_null ( $this -> options [ 'html' ]) ? $this -> options [ 'html' ] : $this -> options [ 'text' ];
2020-12-04 16:08:11 +01:00
}
}
2021-02-22 16:38:55 +01:00
class BoostrapTable extends BootstrapGeneric {
private $defaultOptions = [
'striped' => true ,
'bordered' => true ,
'borderless' => false ,
'hover' => true ,
'small' => false ,
'variant' => '' ,
'tableClass' => [],
'headerClass' => [],
'bodyClass' => [],
];
private $bsClasses = null ;
function __construct ( $options , $data ) {
$this -> allowedOptionValues = [
'variant' => array_merge ( BootstrapGeneric :: $variants , [ '' ])
];
$this -> processOptions ( $options );
$this -> fields = $data [ 'fields' ];
$this -> items = $data [ 'items' ];
$this -> caption = ! empty ( $data [ 'caption' ]) ? $data [ 'caption' ] : '' ;
}
private function processOptions ( $options )
{
$this -> options = array_merge ( $this -> defaultOptions , $options );
$this -> checkOptionValidity ();
}
public function table ()
{
return $this -> genTable ();
}
private function genTable ()
{
2021-02-23 08:45:35 +01:00
$html = $this -> openNode ( 'table' , [
2021-02-22 16:38:55 +01:00
'class' => [
'table' ,
" table- { $this -> options [ 'variant' ] } " ,
$this -> options [ 'striped' ] ? 'table-striped' : '' ,
$this -> options [ 'bordered' ] ? 'table-bordered' : '' ,
$this -> options [ 'borderless' ] ? 'table-borderless' : '' ,
$this -> options [ 'hover' ] ? 'table-hover' : '' ,
$this -> options [ 'small' ] ? 'table-sm' : '' ,
! empty ( $this -> options [ 'variant' ]) ? " table- { $this -> options [ 'variant' ] } " : '' ,
2021-03-10 09:27:17 +01:00
! empty ( $this -> options [ 'tableClass' ]) ? ( is_array ( $this -> options [ 'tableClass' ]) ? implode ( ' ' , $this -> options [ 'tableClass' ]) : $this -> options [ 'tableClass' ]) : ''
2021-02-22 16:38:55 +01:00
],
]);
$html .= $this -> genCaption ();
$html .= $this -> genHeader ();
$html .= $this -> genBody ();
2021-02-23 08:45:35 +01:00
$html .= $this -> closeNode ( 'table' );
2021-02-22 16:38:55 +01:00
return $html ;
}
private function genHeader ()
{
2021-02-23 08:45:35 +01:00
$head = $this -> openNode ( 'thead' , [
2021-02-22 16:38:55 +01:00
'class' => [
! empty ( $this -> options [ 'headerClass' ]) ? $this -> options [ 'headerClass' ] : ''
],
]);
2021-02-23 08:45:35 +01:00
$head .= $this -> openNode ( 'tr' );
2021-02-22 16:38:55 +01:00
foreach ( $this -> fields as $i => $field ) {
if ( is_array ( $field )) {
2021-03-10 09:27:17 +01:00
if ( ! empty ( $field [ 'labelHtml' ])) {
$label = $field [ 'labelHtml' ];
} else {
$label = ! empty ( $field [ 'label' ]) ? $field [ 'label' ] : Inflector :: humanize ( $field [ 'key' ]);
$label = h ( $label );
}
2021-02-22 16:38:55 +01:00
} else {
$label = Inflector :: humanize ( $field );
2021-03-10 09:27:17 +01:00
$label = h ( $label );
2021-02-22 16:38:55 +01:00
}
2021-03-10 09:27:17 +01:00
$head .= $this -> genNode ( 'th' , [], $label );
2021-02-22 16:38:55 +01:00
}
2021-02-23 08:45:35 +01:00
$head .= $this -> closeNode ( 'tr' );
$head .= $this -> closeNode ( 'thead' );
2021-02-22 16:38:55 +01:00
return $head ;
}
private function genBody ()
{
2021-02-23 08:45:35 +01:00
$body = $this -> openNode ( 'tbody' , [
2021-02-22 16:38:55 +01:00
'class' => [
2021-03-10 09:27:17 +01:00
! empty ( $this -> options [ 'bodyClass' ]) ? ( is_array ( $this -> options [ 'bodyClass' ]) ? implode ( ' ' , $this -> options [ 'bodyClass' ]) : $this -> options [ 'bodyClass' ]) : ''
2021-02-22 16:38:55 +01:00
],
]);
foreach ( $this -> items as $i => $row ) {
2021-02-23 11:42:26 +01:00
$body .= $this -> genRow ( $row );
}
$body .= $this -> closeNode ( 'tbody' );
return $body ;
}
private function genRow ( $row )
{
$html = $this -> openNode ( 'tr' ,[
'class' => [
! empty ( $row [ '_rowVariant' ]) ? " table- { $row [ '_rowVariant' ] } " : ''
]
]);
if ( array_keys ( $row ) !== range ( 0 , count ( $row ) - 1 )) { // associative array
foreach ( $this -> fields as $i => $field ) {
if ( is_array ( $field )) {
$key = $field [ 'key' ];
} else {
$key = $field ;
2021-02-22 16:38:55 +01:00
}
2021-06-12 11:57:49 +02:00
$cellValue = Hash :: get ( $row , $key );
2021-02-23 11:42:26 +01:00
$html .= $this -> genCell ( $cellValue , $field , $row );
}
} else { // indexed array
foreach ( $row as $cellValue ) {
$html .= $this -> genCell ( $cellValue , $field , $row );
2021-02-22 16:38:55 +01:00
}
}
2021-02-23 11:42:26 +01:00
$html .= $this -> closeNode ( 'tr' );
return $html ;
}
private function genCell ( $value , $field = [], $row = [])
{
if ( isset ( $field [ 'formatter' ])) {
$cellContent = $field [ 'formatter' ]( $value , $row );
} else {
$cellContent = h ( $value );
}
return $this -> genNode ( 'td' , [
'class' => [
! empty ( $row [ '_cellVariant' ]) ? " bg- { $row [ '_cellVariant' ] } " : ''
]
], $cellContent );
2021-02-22 16:38:55 +01:00
}
private function genCaption ()
{
2021-02-23 08:45:35 +01:00
return $this -> genNode ( 'caption' , [], h ( $this -> caption ));
2021-02-22 16:38:55 +01:00
}
2021-02-23 08:45:35 +01:00
}
2021-02-23 11:25:20 +01:00
class BoostrapButton extends BootstrapGeneric {
private $defaultOptions = [
'id' => '' ,
'text' => '' ,
'html' => null ,
'variant' => 'primary' ,
'outline' => false ,
'size' => '' ,
'block' => false ,
'icon' => null ,
'class' => [],
'type' => 'button' ,
'nodeType' => 'button' ,
2021-03-10 09:32:00 +01:00
'params' => [],
'badge' => false
2021-02-23 11:25:20 +01:00
];
private $bsClasses = [];
function __construct ( $options ) {
$this -> allowedOptionValues = [
'variant' => BootstrapGeneric :: $variants ,
'size' => [ '' , 'sm' , 'lg' ],
'type' => [ 'button' , 'submit' , 'reset' ]
];
2021-03-10 09:27:17 +01:00
if ( empty ( $options [ 'class' ])) {
$options [ 'class' ] = '' ;
}
2021-02-23 11:25:20 +01:00
$options [ 'class' ] = ! is_array ( $options [ 'class' ]) ? [ $options [ 'class' ]] : $options [ 'class' ];
$this -> processOptions ( $options );
}
private function processOptions ( $options )
{
$this -> options = array_merge ( $this -> defaultOptions , $options );
$this -> checkOptionValidity ();
$this -> bsClasses [] = 'btn' ;
if ( $this -> options [ 'outline' ]) {
$this -> bsClasses [] = " btn-outline- { $this -> options [ 'variant' ] } " ;
} else {
$this -> bsClasses [] = " btn- { $this -> options [ 'variant' ] } " ;
}
if ( ! empty ( $this -> options [ 'size' ])) {
$this -> bsClasses [] = " btn- $this->options ['size'] " ;
}
if ( $this -> options [ 'block' ]) {
$this -> bsClasses [] = 'btn-block' ;
}
}
public function button ()
{
return $this -> genButton ();
}
private function genButton ()
{
$html = $this -> openNode ( $this -> options [ 'nodeType' ], array_merge ( $this -> options [ 'params' ], [
'class' => array_merge ( $this -> options [ 'class' ], $this -> bsClasses ),
'role' => " alert " ,
'type' => $this -> options [ 'type' ]
]));
$html .= $this -> genIcon ();
$html .= $this -> genContent ();
2021-03-10 09:32:00 +01:00
if ( ! empty ( $this -> options [ 'badge' ])) {
$bsBadge = new BoostrapBadge ( $this -> options [ 'badge' ]);
$html .= $bsBadge -> badge ();
}
2021-02-23 11:25:20 +01:00
$html .= $this -> closeNode ( $this -> options [ 'nodeType' ]);
return $html ;
}
private function genIcon ()
{
return $this -> genNode ( 'span' , [
'class' => [ 'mr-1' , " fa fa- { $this -> options [ 'icon' ] } " ],
]);
}
private function genContent ()
{
return ! is_null ( $this -> options [ 'html' ]) ? $this -> options [ 'html' ] : $this -> options [ 'text' ];
}
2021-03-10 09:28:33 +01:00
}
class BoostrapBadge extends BootstrapGeneric {
private $defaultOptions = [
'text' => '' ,
'variant' => 'primary' ,
'pill' => false ,
'title' => ''
];
function __construct ( $options ) {
$this -> allowedOptionValues = [
'variant' => BootstrapGeneric :: $variants ,
];
$this -> processOptions ( $options );
}
private function processOptions ( $options )
{
$this -> options = array_merge ( $this -> defaultOptions , $options );
$this -> checkOptionValidity ();
}
public function badge ()
{
return $this -> genBadge ();
}
private function genBadge ()
{
$html = $this -> genNode ( 'span' , [
'class' => [
'badge' ,
" badge- { $this -> options [ 'variant' ] } " ,
$this -> options [ 'pill' ] ? 'badge-pill' : '' ,
],
'title' => $this -> options [ 'title' ]
], h ( $this -> options [ 'text' ]));
return $html ;
}
}
2021-03-10 09:32:18 +01:00
class BoostrapModal extends BootstrapGeneric {
private $defaultOptions = [
'size' => '' ,
'centered' => true ,
'scrollable' => true ,
'backdropStatic' => false ,
'title' => '' ,
'titleHtml' => false ,
'body' => '' ,
'bodyHtml' => false ,
'footerHtml' => false ,
'confirmText' => 'Confirm' ,
'cancelText' => 'Cancel' ,
'modalClass' => [ '' ],
'headerClass' => [ '' ],
'bodyClass' => [ '' ],
'footerClass' => [ '' ],
'type' => 'ok-only' ,
'variant' => '' ,
2021-03-18 14:01:14 +01:00
'confirmFunction' => '' , // Will be called with the following arguments confirmFunction(modalObject, tmpApi)
2021-03-10 09:32:18 +01:00
'cancelFunction' => ''
];
private $bsClasses = null ;
function __construct ( $options ) {
$this -> allowedOptionValues = [
'size' => [ 'sm' , 'lg' , 'xl' , '' ],
2021-06-14 13:03:58 +02:00
'type' => [ 'ok-only' , 'confirm' , 'confirm-success' , 'confirm-warning' , 'confirm-danger' , 'custom' ],
2021-03-10 09:32:18 +01:00
'variant' => array_merge ( BootstrapGeneric :: $variants , [ '' ]),
];
$this -> processOptions ( $options );
}
private function processOptions ( $options )
{
$this -> options = array_merge ( $this -> defaultOptions , $options );
$this -> checkOptionValidity ();
}
public function modal ()
{
return $this -> genModal ();
}
private function genModal ()
{
$dialog = $this -> openNode ( 'div' , [
'class' => array_merge (
[ 'modal-dialog' , ( ! empty ( $this -> options [ 'size' ])) ? " modal- { $this -> options [ 'size' ] } " : '' ],
$this -> options [ 'modalClass' ]
),
]);
$content = $this -> openNode ( 'div' , [
'class' => [ 'modal-content' ],
]);
$header = $this -> genHeader ();
$body = $this -> genBody ();
$footer = $this -> genFooter ();
$closedDiv = $this -> closeNode ( 'div' );
$html = " { $dialog } { $content } { $header } { $body } { $footer } { $closedDiv } { $closedDiv } " ;
return $html ;
}
private function genHeader ()
{
$header = $this -> openNode ( 'div' , [ 'class' => array_merge ([ 'modal-header' ], $this -> options [ 'headerClass' ])]);
if ( ! empty ( $this -> options [ 'titleHtml' ])) {
$header .= $this -> options [ 'titleHtml' ];
} else {
$header .= $this -> genNode ( 'h5' , [ 'class' => [ 'modal-title' ]], h ( $this -> options [ 'title' ]));
}
if ( empty ( $this -> options [ 'backdropStatic' ])) {
$header .= $this -> genericCloseButton ( 'modal' );
}
$header .= $this -> closeNode ( 'div' );
return $header ;
}
private function genBody ()
{
$body = $this -> openNode ( 'div' , [ 'class' => array_merge ([ 'modal-body' ], $this -> options [ 'bodyClass' ])]);
if ( ! empty ( $this -> options [ 'bodyHtml' ])) {
$body .= $this -> options [ 'bodyHtml' ];
} else {
$body .= h ( $this -> options [ 'body' ]);
}
$body .= $this -> closeNode ( 'div' );
return $body ;
}
private function genFooter ()
{
2021-06-14 13:03:58 +02:00
$footer = $this -> openNode ( 'div' , [
'class' => array_merge ([ 'modal-footer' ], $this -> options [ 'footerClass' ]),
'data-custom-footer' => $this -> options [ 'type' ] == 'custom'
]);
2021-03-10 09:32:18 +01:00
if ( ! empty ( $this -> options [ 'footerHtml' ])) {
$footer .= $this -> options [ 'footerHtml' ];
} else {
$footer .= $this -> getFooterBasedOnType ();
}
$footer .= $this -> closeNode ( 'div' );
return $footer ;
}
private function getFooterBasedOnType () {
if ( $this -> options [ 'type' ] == 'ok-only' ) {
return $this -> getFooterOkOnly ();
} else if ( str_contains ( $this -> options [ 'type' ], 'confirm' )) {
return $this -> getFooterConfirm ();
2021-06-14 13:03:58 +02:00
} else if ( $this -> options [ 'type' ] == 'custom' ) {
return $this -> getFooterCustom ();
2021-03-10 09:32:18 +01:00
} else {
return $this -> getFooterOkOnly ();
}
}
private function getFooterOkOnly ()
{
return ( new BoostrapButton ([
'variant' => 'primary' ,
'text' => __ ( 'Ok' ),
'params' => [
2021-03-15 22:47:13 +01:00
'data-dismiss' => $this -> options [ 'confirmFunction' ] ? '' : 'modal' ,
2021-03-10 09:32:18 +01:00
'onclick' => $this -> options [ 'confirmFunction' ]
]
])) -> button ();
}
private function getFooterConfirm ()
{
if ( $this -> options [ 'type' ] == 'confirm' ) {
$variant = 'primary' ;
} else {
$variant = explode ( '-' , $this -> options [ 'type' ])[ 1 ];
}
$buttonCancel = ( new BoostrapButton ([
'variant' => 'secondary' ,
'text' => h ( $this -> options [ 'cancelText' ]),
'params' => [
'data-dismiss' => 'modal' ,
'onclick' => $this -> options [ 'cancelFunction' ]
]
])) -> button ();
$buttonConfirm = ( new BoostrapButton ([
'variant' => $variant ,
'text' => h ( $this -> options [ 'confirmText' ]),
2021-03-18 14:01:14 +01:00
'class' => 'modal-confirm-button' ,
2021-03-10 09:32:18 +01:00
'params' => [
2021-03-15 22:47:13 +01:00
'data-dismiss' => $this -> options [ 'confirmFunction' ] ? '' : 'modal' ,
2021-03-18 14:01:14 +01:00
'data-confirmFunction' => sprintf ( '%s' , $this -> options [ 'confirmFunction' ])
2021-03-10 09:32:18 +01:00
]
])) -> button ();
return $buttonCancel . $buttonConfirm ;
}
2021-06-14 13:03:58 +02:00
private function getFooterCustom ()
{
$buttons = [];
foreach ( $this -> options [ 'footerButtons' ] as $buttonConfig ) {
$buttons [] = ( new BoostrapButton ([
'variant' => h ( $buttonConfig [ 'variant' ] ? ? 'primary' ),
'text' => h ( $buttonConfig [ 'text' ]),
'class' => 'modal-confirm-button' ,
'params' => [
'data-dismiss' => ! empty ( $buttonConfig [ 'clickFunction' ]) ? '' : 'modal' ,
'data-clickFunction' => sprintf ( '%s' , $buttonConfig [ 'clickFunction' ])
]
])) -> button ();
}
return implode ( '' , $buttons );
}
2021-03-10 09:32:18 +01:00
}
2021-06-12 11:57:49 +02:00
class BoostrapProgress extends BootstrapGeneric {
private $defaultOptions = [
'value' => 0 ,
'total' => 100 ,
'text' => '' ,
'title' => '' ,
'variant' => 'primary' ,
'height' => '' ,
'striped' => false ,
'animated' => false ,
'label' => true
];
function __construct ( $options ) {
$this -> allowedOptionValues = [
'variant' => BootstrapGeneric :: $variants ,
];
$this -> processOptions ( $options );
}
private function processOptions ( $options )
{
$this -> options = array_merge ( $this -> defaultOptions , $options );
$this -> checkOptionValidity ();
}
public function progress ()
{
return $this -> genProgress ();
}
private function genProgress ()
{
$percentage = round ( 100 * $this -> options [ 'value' ] / $this -> options [ 'total' ]);
$heightStyle = ! empty ( $this -> options [ 'height' ]) ? sprintf ( 'height: %s;' , h ( $this -> options [ 'height' ])) : '' ;
$widthStyle = sprintf ( 'width: %s%%;' , $percentage );
$label = $this -> options [ 'label' ] ? " { $percentage } % " : '' ;
$pb = $this -> genNode ( 'div' , [
'class' => [
'progress-bar' ,
" bg- { $this -> options [ 'variant' ] } " ,
$this -> options [ 'striped' ] ? 'progress-bar-striped' : '' ,
$this -> options [ 'animated' ] ? 'progress-bar-animated' : '' ,
],
'role' => " progressbar " ,
'aria-valuemin' => " 0 " , 'aria-valuemax' => " 100 " , 'aria-valuenow' => $percentage ,
'style' => " ${ widthStyle } " ,
'title' => $this -> options [ 'title' ]
], $label );
$container = $this -> genNode ( 'div' , [
'class' => [
'progress' ,
],
'style' => " ${ heightStyle } " ,
'title' => h ( $this -> options [ 'title' ]),
], $pb );
return $container ;
}
}
class BoostrapCollapse extends BootstrapGeneric {
private $defaultOptions = [
'text' => '' ,
'open' => false ,
];
function __construct ( $options , $content , $btHelper ) {
$this -> allowedOptionValues = [];
$this -> processOptions ( $options );
$this -> content = $content ;
$this -> btHelper = $btHelper ;
}
private function processOptions ( $options )
{
$this -> options = array_merge ( $this -> defaultOptions , $options );
$this -> checkOptionValidity ();
}
public function collapse ()
{
return $this -> genCollapse ();
}
private function genControl ()
{
$html = $this -> genNode ( 'a' , [
'class' => [ 'text-decoration-none' ],
'data-toggle' => 'collapse' ,
'href' => '#collapseExample' ,
'role' => 'button' ,
'aria-expanded' => 'false' ,
'aria-controls' => 'collapseExample' ,
], h ( $this -> options [ 'title' ]));
return $html ;
}
private function genContent ()
{
$content = $this -> genNode ( 'div' , [
'class' => 'card' ,
], $this -> content );
$container = $this -> genNode ( 'div' , [
'class' => [ 'collapse' , $this -> options [ 'open' ] ? 'show' : '' ],
'id' => 'collapseExample' ,
], $content );
return $container ;
}
private function genCollapse ()
{
$html = $this -> genControl ();
$html .= $this -> genContent ();
return $html ;
}
}
class BoostrapProgressTimeline extends BootstrapGeneric {
private $defaultOptions = [
'steps' => [],
'selected' => 0 ,
'variant' => 'info' ,
'variantInactive' => 'secondary' ,
];
function __construct ( $options , $btHelper ) {
$this -> allowedOptionValues = [
'variant' => BootstrapGeneric :: $variants ,
'variantInactive' => BootstrapGeneric :: $variants ,
];
$this -> processOptions ( $options );
$this -> btHelper = $btHelper ;
}
private function processOptions ( $options )
{
$this -> options = array_merge ( $this -> defaultOptions , $options );
$this -> checkOptionValidity ();
}
public function progressTimeline ()
{
return $this -> genProgressTimeline ();
}
private function getStepIcon ( $step , $i , $nodeActive , $lineActive )
{
$icon = $this -> genNode ( 'b' , [
'class' => [
! empty ( $step [ 'icon' ]) ? h ( $this -> btHelper -> FontAwesome -> getClass ( $step [ 'icon' ])) : '' ,
$this -> getTextClassForVariant ( $this -> options [ 'variant' ])
],
], empty ( $step [ 'icon' ]) ? h ( $i + 1 ) : '' );
$iconContainer = $this -> genNode ( 'span' , [
'class' => [
'd-flex' , 'align-items-center' , 'justify-content-center' ,
'rounded-circle' ,
$nodeActive ? " bg- { $this -> options [ 'variant' ] } " : " bg- { $this -> options [ 'variantInactive' ] } "
],
'style' => 'width:50px; height:50px'
], $icon );
$li = $this -> genNode ( 'li' , [
'class' => [
'd-flex' , 'flex-column' ,
$nodeActive ? 'progress-active' : 'progress-inactive' ,
],
], $iconContainer );
$html = $li . $this -> getHorizontalLine ( $i , $nodeActive , $lineActive );
return $html ;
}
private function getHorizontalLine ( $i , $nodeActive , $lineActive )
{
$stepCount = count ( $this -> options [ 'steps' ]);
if ( $i == $stepCount - 1 ) {
return '' ;
}
$progressBar = ( new BoostrapProgress ([
'label' => false ,
'value' => $nodeActive ? ( $lineActive ? 100 : 50 ) : 0 ,
'height' => '2px' ,
'variant' => $this -> options [ 'variant' ]
])) -> progress ();
$line = $this -> genNode ( 'span' , [
'class' => [
'progress-line' ,
'flex-grow-1' , 'align-self-center' ,
$lineActive ? " bg- { $this -> options [ 'variant' ] } " : ''
],
], $progressBar );
return $line ;
}
private function getStepText ( $step , $isActive )
{
return $this -> genNode ( 'li' , [
'class' => [
'text-center' ,
'font-weight-bold' ,
$isActive ? 'progress-active' : 'progress-inactive' ,
],
], h ( $step [ 'text' ] ? ? '' ));
}
private function genProgressTimeline ()
{
$iconLis = '' ;
$textLis = '' ;
foreach ( $this -> options [ 'steps' ] as $i => $step ) {
$nodeActive = $i <= $this -> options [ 'selected' ];
$lineActive = $i < $this -> options [ 'selected' ];
$iconLis .= $this -> getStepIcon ( $step , $i , $nodeActive , $lineActive );
$textLis .= $this -> getStepText ( $step , $nodeActive );
}
$ulIcons = $this -> genNode ( 'ul' , [
'class' => [
'd-flex' , 'justify-content-around' ,
],
], $iconLis );
$ulText = $this -> genNode ( 'ul' , [
'class' => [
'd-flex' , 'justify-content-between' ,
],
], $textLis );
$html = $this -> genNode ( 'div' , [
2021-06-18 09:22:04 +02:00
'class' => [ 'progress-timeline' , 'mw-75' , 'mx-auto' ]
2021-06-12 11:57:49 +02:00
], $ulIcons . $ulText );
return $html ;
}
}