├── .gitignore ├── ColorBlueAsset.php ├── ColorBrownAsset.php ├── ColorDefaultAsset.php ├── ColorGrayAsset.php ├── ColorLightAsset.php ├── ColorPurpleAsset.php ├── CoreAsset.php ├── FontAsset.php ├── LICENSE.md ├── Metronic.php ├── README.md ├── assets └── .empty ├── composer.json ├── helpers ├── Html.php └── Layout.php ├── layouts └── main.php └── widgets ├── Accordion.php ├── ActiveField.php ├── ActiveForm.php ├── Alert.php ├── Badge.php ├── Breadcrumbs.php ├── Button.php ├── ButtonDropdown.php ├── ButtonGroup.php ├── CheckboxList.php ├── DatePicker.php ├── DatePickerAsset.php ├── DateRangePicker.php ├── DateRangePickerAsset.php ├── Decorator.php ├── DetailView.php ├── Dropdown.php ├── DropdownContent.php ├── GridView.php ├── HorizontalMenu.php ├── InputWidget.php ├── IonRangeSlider.php ├── IonRangeSliderAsset.php ├── Link.php ├── Menu.php ├── Modal.php ├── ModalAsset.php ├── MultiSelect.php ├── MultiSelectAsset.php ├── Nav.php ├── NavBar.php ├── Note.php ├── Notification.php ├── NotificationAsset.php ├── Portlet.php ├── Select2.php ├── Select2Asset.php ├── Spinner.php ├── SpinnerAsset.php ├── Tabs.php ├── TextInputWidget.php └── Widget.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /ColorBlueAsset.php: -------------------------------------------------------------------------------- 1 | [ 16 | 'plugins/respond.min.js' => 'if lt IE 9', 17 | 'plugins/excanvas.min.js' => 'if lt IE 9', 18 | ], 19 | ]; 20 | public $js = [ 21 | 'plugins/respond.min.js', 22 | 'plugins/excanvas.min.js', 23 | 'plugins/jquery-migrate-1.2.1.min.js', 24 | 'plugins/bootstrap-hover-dropdown/bootstrap-hover-dropdown.min.js', 25 | 'plugins/jquery-slimscroll/jquery.slimscroll.min.js', 26 | 'plugins/jquery.blockui.min.js', 27 | 'plugins/jquery.cokie.min.js', 28 | 'plugins/uniform/jquery.uniform.min.js', 29 | 'scripts/core/metronic.js', 30 | 'scripts/core/app.js', 31 | 'scripts/custom/custom.js', 32 | ]; 33 | 34 | public $css = [ 35 | 'plugins/uniform/css/uniform.default.css', 36 | 'css/style-metronic.css', 37 | 'css/style.css', 38 | 'css/style-responsive.css', 39 | 'css/custom.css', 40 | ]; 41 | public $depends = [ 42 | 'icron\metronic\FontAsset', 43 | 'yii\bootstrap\BootstrapPluginAsset', 44 | 'yii\web\JqueryAsset', 45 | ]; 46 | } 47 | -------------------------------------------------------------------------------- /FontAsset.php: -------------------------------------------------------------------------------- 1 | {static::$componentName}; 90 | } 91 | 92 | /** 93 | * Get base url to metronic assets 94 | * @param $view View 95 | * @return string 96 | */ 97 | public static function getAssetsUrl($view) 98 | { 99 | if(static::$assetsBundle === null){ 100 | static::$assetsBundle = static::registerThemeAsset($view); 101 | } 102 | 103 | return (static::$assetsBundle instanceof AssetBundle) ? static::$assetsBundle->baseUrl : ''; 104 | } 105 | 106 | /** 107 | * Register Theme Asset 108 | * @param $view View 109 | * @return AssetBundle 110 | */ 111 | public static function registerThemeAsset($view) 112 | { 113 | /** @var AssetBundle $themeAsset */ 114 | $themeAsset = 'icron\metronic\Color' . ucfirst(static::getComponent()->color) . 'Asset'; 115 | static::$assetsBundle = $themeAsset::register($view); 116 | 117 | return static::$assetsBundle; 118 | } 119 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Yii2Metronic 2 | ====================== 3 | Yii2Metronic is a collection of Yii2 components(widgets) based on responsive and multipurpose admin theme 4 | called Metronic (v2.0). Powered with Twitter Bootstrap 3.1.0 Framework. 5 | 6 | Metronic can be used for any type of web applications: custom admin panels, admin dashboards, CMS, CRM, SAAS and websites: business, corporate, portfolio, blog. 7 | Metronic has a sleek, clean and intuitive metro style design which makes your next project look awesome and yet user 8 | friendly. Metronic has a huge collection of plugins and UI components and works seamlessly on all major web browsers, 9 | tablets and phones. 10 | 11 | More than 30 widgets for Yii2 Metronic. 12 | 13 | Basic configuration: 14 | ``` 15 | 'components' => [ 16 | 'metronic' => [ 17 | 'class' => 'icron\metronic\Metronic', 18 | 'color' => 'default', 19 | 'layoutOption' => \icron\metronic\Metronic::LAYOUT_FLUID, 20 | 'headerOption' => 'fixed', 21 | ], 22 | ], 23 | 'preload' => ['metronic'], 24 | ``` 25 | 26 | Some examples: 27 | #### Menu 28 | Metronic menu displays a multi-level menu using nested HTML lists. 29 | 30 | The main property of Menu is [[items]], which specifies the possible items in the menu. 31 | A menu item can contain sub-items which specify the sub-menu under that menu item. 32 | Menu checks the current route and request parameters to toggle certain menu items 33 | with active state. 34 | Note that Menu only renders the HTML tags about the menu. It does do any styling. 35 | You are responsible to provide CSS styles to make it look like a real menu. 36 | 37 | The following example shows how to use Menu: 38 | 39 | ```php 40 | echo Menu::widget([ 41 | 'items' => [ 42 | // Important: you need to specify url as 'controller/action', 43 | // not just as 'controller' even if default action is used. 44 | [ 45 | 'icon' => '', 46 | 'label' => 'Home', 47 | 'url' => ['site/index'] 48 | ], 49 | // 'Products' menu item will be selected as long as the route is 'product/index' 50 | ['label' => 'Products', 'url' => ['product/index'], 'items' => [ 51 | ['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']], 52 | ['label' => 'Most Popular', 'url' => ['product/index', 'tag' => 'popular']], 53 | ]], 54 | ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest], 55 | ], 56 | 'search' => [ 57 | // required, whether search box is visible. Defaults to 'true'. 58 | 'visible' => true, 59 | // optional, the configuration array for [[ActiveForm]]. 60 | 'form' => [], 61 | // optional, input options with default values 62 | 'input' => [ 63 | 'name' => 'search', 64 | 'value' => '', 65 | 'options' => [ 66 | 'placeholder' => 'Search...', 67 | ] 68 | ], 69 | ] 70 | ]); 71 | ``` 72 | #### Horizontal Menu 73 | Horizontal Menu displays a multi-level menu using nested HTML lists. 74 | 75 | The main property of Menu is [[items]], which specifies the possible items in the menu. 76 | A menu item can contain sub-items which specify the sub-menu under that menu item. 77 | Menu checks the current route and request parameters to toggle certain menu items 78 | with active state. 79 | Note that Menu only renders the HTML tags about the menu. It does do any styling. 80 | You are responsible to provide CSS styles to make it look like a real menu. 81 | Supports multiple operating modes: classic, mega, and full mega(see [[HorizontalMenu::type]]). 82 | 83 | The following example shows how to use Menu: 84 | 85 | ```php 86 | // Classic menu with search box 87 | echo HorizontalMenu::widget([ 88 | 'items' => [ 89 | // Important: you need to specify url as 'controller/action', 90 | // not just as 'controller' even if default action is used. 91 | ['label' => 'Home', 'url' => ['site/index']], 92 | // 'Products' menu item will be selected as long as the route is 'product/index' 93 | ['label' => 'Products', 'url' => ['product/index'], 'items' => [ 94 | ['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']], 95 | ['label' => 'Most Popular', 'url' => ['product/index', 'tag' => 'popular']], 96 | ]], 97 | ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest], 98 | ], 99 | 'search' => [ 100 | // required, whether search box is visible. Defaults to 'true'. 101 | 'visible' => true, 102 | // optional, the configuration array for [[ActiveForm]]. 103 | 'form' => [], 104 | // optional, input options with default values 105 | 'input' => [ 106 | 'name' => 'search', 107 | 'value' => '', 108 | 'options' => [ 109 | 'placeholder' => 'Search...', 110 | ] 111 | ], 112 | ]); 113 | 114 | // Mega Menu without search box 115 | echo HorizontalMenu::widget([ 116 | 'items' => [ 117 | ['label' => 'Home', 'url' => ['site/index']], 118 | [ 119 | 'label' => 'Mega Menu', 120 | 'type' => HorizontalMenu::ITEM_TYPE_FULL_MEGA, 121 | //optional, HTML text for last column 122 | 'text' => 'Other HTML text', 123 | 'items' => [ 124 | [ 125 | 'label' => 'Column 1', // First column title 126 | 'items' => [ 127 | ['label' => 'Column 1 Item 1'], 128 | ['label' => 'Column 1 Item 2'], 129 | ] 130 | ], 131 | [ 132 | 'label' => 'Column 2', // Second column title 133 | 'items' => [ 134 | ['label' => 'Column 2 Item 1'], 135 | ['label' => 'Column 2 Item 2'], 136 | ] 137 | ], 138 | ] 139 | ], 140 | ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest], 141 | ], 142 | ]); 143 | ``` 144 | #### Nav 145 | 146 | Nav renders a nav HTML component. 147 | For example: 148 | 149 | ```php 150 | echo Nav::widget([ 151 | 'items' => [ 152 | [ 153 | 'icon' => 'fa fa-warning', 154 | 'badge' => Badge::widget(['label' => 'New', 'round' => false]), 155 | 'label' => 'Home', 156 | 'url' => ['site/index'], 157 | 'linkOptions' => [...], 158 | ], 159 | [ 160 | 'label' => 'Dropdown', 161 | 'items' => [ 162 | ['label' => 'Level 1 - Dropdown A', 'url' => '#'], 163 | '
  • ', 164 | '', 165 | ['label' => 'Level 1 - Dropdown B', 'url' => '#'], 166 | ], 167 | ], 168 | ], 169 | ]); 170 | ``` 171 | 172 | **Note**: *Multilevel dropdowns beyond Level 1 are not supported in Bootstrap 3.* 173 | 174 | #### DateRangePicker 175 | DateRangePicker renders dateRangePicker widget. 176 | 177 | There are two modes of operation of the widget are 'input' and 'advance'. 178 | Mode 'input' renders input HTML element and mode 'advance' renders span HTML element. 179 | Widget renders a hidden field with the model name that this widget is associated with 180 | and the current value of the selected date. 181 | 182 | For example, if [[model]] and [[attribute]] are not set: 183 | ```php 184 | DateRangePicker::widget([ 185 | 'mode' => DateRangePicker::MODE_ADVANCE, 186 | 'labelDateFormat' => 'MMMM D, YYYY', 187 | 'type' => DateRangePicker::TYPE_BLUE, 188 | 'clientOptions' => [ 189 | 'format' => 'YYYY-MM-DD', 190 | 'ranges' => new \yii\web\JsExpression("{ 191 | 'Today': [moment(), moment()], 192 | 'Yesterday': [moment().subtract('days', 1), moment().subtract('days', 1)], 193 | 'Last 7 Days': [moment().subtract('days', 6), moment()], 194 | 'Last 30 Days': [moment().subtract('days', 29), moment()], 195 | 'This Month': [moment().startOf('month'), moment().endOf('month')], 196 | 'Last Month': [moment().subtract('month', 1).startOf('month'), moment().subtract('month', 1).endOf('month')] 197 | }"), 198 | ], 199 | 'name' => 'date', 200 | 'icon' => 'fa fa-calendar', 201 | 'value' => '2014-02-15 - 2014-02-18', 202 | ]); 203 | ``` 204 | #### Portlet 205 | 206 | Portlet renders a metronic portlet. 207 | Any content enclosed between the [[begin()]] and [[end()]] calls of Portlet 208 | is treated as the content of the portlet. 209 | For example, 210 | 211 | ```php 212 | // Simple portlet 213 | Portlet::begin([ 214 | 'icon' => 'fa fa-bell-o', 215 | 'title' => 'Title Portlet', 216 | ]); 217 | echo 'Body portlet'; 218 | Portlet::end(); 219 | 220 | // Portlet with tools, actions, scroller, events and remote content 221 | Portlet::begin([ 222 | 'title' => 'Extended Portlet', 223 | 'scroller' => [ 224 | 'height' => 150, 225 | 'footer' => ['label' => 'Show all', 'url' => '#'], 226 | ], 227 | 'clientOptions' => [ 228 | 'loadSuccess' => new \yii\web\JsExpression('function(){ console.log("load success"); }'), 229 | 'remote' => '/?r=site/about', 230 | ], 231 | 'clientEvents' => [ 232 | 'close.mr.portlet' => 'function(e) { console.log("portlet closed"); e.preventDefault(); }' 233 | ], 234 | 'tools' => [ 235 | Portlet::TOOL_RELOAD, 236 | Portlet::TOOL_MINIMIZE, 237 | Portlet::TOOL_CLOSE, 238 | ], 239 | ]); 240 | ``` 241 | #### Select2 242 | 243 | Select2 renders Select2 component. 244 | For example: 245 | ```php 246 | echo Select2::widget([ 247 | 'name' => 'select', 248 | 'data' => ['1' => 'Item 1', '2' => 'Item 2'], 249 | 'multiple' => true, 250 | ]); 251 | ``` 252 | 253 | #### Badge 254 | 255 | Badge widget. For example, 256 | ``` 257 | Badge::widget([ 258 | 'label' => 'NEW', 259 | 'type' => Badge::TYPE_SUCCESS, 260 | 'round' 261 | ]); 262 | ``` 263 | #### IonRangeSlider 264 | IonRangeSlider renders ionRangeSlider widget. 265 | For example, if [[model]] and [[attribute]] are not set: 266 | ```php 267 | echo IonRangeSlider::widget([ 268 | 'name' => 'ionRangeSlider', 269 | 'clientOptions' => [ 270 | 'min' => 0, 271 | 'max' => 5000, 272 | 'from' => 1000, // default value 273 | 'to' => 4000, // default value 274 | 'type' => 'double', 275 | 'step' => 1, 276 | 'prefix' => "$", 277 | 'prettify' => false, 278 | 'hasGrid' => true 279 | ], 280 | ]); 281 | ``` 282 | #### Spinner 283 | 284 | Spinner renders an spinner Fuel UX widget. 285 | For example: 286 | 287 | ```php 288 | echo Spinner::widget([ 289 | 'model' => $model, 290 | 'attribute' => 'country', 291 | 'size' => Spinner::SIZE_SMALL, 292 | 'buttonsLocation' => Spinner::BUTTONS_LOCATION_VERTICAL, 293 | 'clientOptions' => ['step' => 2], 294 | 'clientEvents' => ['changed' => 'function(event, value){ console.log(value);}'], 295 | ]); 296 | ``` 297 | 298 | The following example will use the name property instead: 299 | 300 | ```php 301 | echo Spinner::widget([ 302 | 'name' => 'country', 303 | 'clientOptions' => ['step' => 2], 304 | ]); 305 | ``` 306 | #### Accordion 307 | Accordion renders an accordion Metronic component. 308 | For example: 309 | 310 | ```php 311 | echo Accordion::widget([ 312 | 'items' => [ 313 | [ 314 | 'header' => 'Item 1', 315 | 'content' => 'Content 1...', 316 | // open its content by default 317 | 'contentOptions' => ['class' => 'in'], 318 | 'type' => Accordion::ITEM_TYPE_SUCCESS, 319 | ], 320 | [ 321 | 'header' => 'Item 2', 322 | 'content' => 'Content 2...', 323 | ], 324 | ], 325 | 'itemConfig' => ['showIcon' => true], 326 | ]); 327 | ``` 328 | #### Note 329 | 330 | Note renders a metronic button. 331 | For example, 332 | ```php 333 | Note::widget([ 334 | 'title' => 'Success! Some Header Goes Here', 335 | 'body' => 'Duis mollis, est non commodo luctus', 336 | 'type' => Note::TYPE_INFO, 337 | ]); 338 | ``` 339 | 340 | The following example will show the content enclosed between the [[begin()]] 341 | and [[end()]] calls within the alert box: 342 | ```php 343 | Note::begin(['type' => Note::TYPE_DANGER]); 344 | echo 'Some title and body'; 345 | Note::end(); 346 | ``` 347 | 348 | ##### For more details see code documentation ... 349 | -------------------------------------------------------------------------------- /assets/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icron/yii2-metronic/ad21d4ad996167dbc77d24c6c0f8bba98578975b/assets/.empty -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "icron/yii2-metronic", 3 | "description": "Metronic Template for Yii2", 4 | "keywords": ["yii", "metronic", "template"], 5 | "type": "yii2-extension", 6 | "license": "BSD", 7 | "support": { 8 | "forum": "", 9 | "wiki": "" 10 | }, 11 | 12 | "require": { 13 | "yiisoft/yii2": "*" 14 | }, 15 | "autoload": { 16 | "psr-4": { "icron\\metronic\\": "" } 17 | } 18 | } -------------------------------------------------------------------------------- /helpers/Html.php: -------------------------------------------------------------------------------- 1 | $condition) { 32 | if (strpos($url, $file) !== false) { 33 | unset($options['conditions']); 34 | return static::conditionalComment(static::tag('link', '', $options), $condition); 35 | } 36 | } 37 | } 38 | unset($options['conditions']); 39 | 40 | return static::tag('link', '', $options); 41 | } 42 | 43 | /** 44 | * Generates a script tag that refers to an external JavaScript file. 45 | * @param string $url the URL of the external JavaScript file. This parameter will be processed by [[\yii\helpers\Url::to()]]. 46 | * @param array $options the tag options in terms of name-value pairs. These will be rendered as 47 | * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. 48 | * If a value is null, the corresponding attribute will not be rendered. 49 | * See [[renderTagAttributes()]] for details on how attributes are being rendered. 50 | * @return string the generated script tag 51 | * @see \yii\helpers\Url::to() 52 | */ 53 | public static function jsFile($url, $options = []) 54 | { 55 | $options['src'] = Url::to($url); 56 | if (!empty($options['conditions'])) { 57 | foreach ($options['conditions'] as $file => $condition) { 58 | if (strpos($url, $file) !== false) { 59 | unset($options['conditions']); 60 | return static::conditionalComment(static::tag('script', '', $options), $condition); 61 | } 62 | } 63 | } 64 | unset($options['conditions']); 65 | 66 | return static::tag('script', '', $options); 67 | } 68 | 69 | /** 70 | * Generates conditional comments such as ''. 71 | * @param $content string the commented content 72 | * @param $condition string condition. Can contain 'if...' or '' 73 | * @return string the generated result 74 | */ 75 | public static function conditionalComment($content, $condition) 76 | { 77 | $condition = strpos($condition, '') !== false ? ''; 82 | 83 | return implode("\n", $lines); 84 | } 85 | } -------------------------------------------------------------------------------- /helpers/Layout.php: -------------------------------------------------------------------------------- 1 | headerOption, 'fixed') === 0) { 24 | Html::addCssClass($options, 'page-header-fixed'); 25 | } 26 | 27 | switch(Metronic::getComponent()->layoutOption) 28 | { 29 | case Metronic::LAYOUT_FULL_WIDTH : 30 | Html::addCssClass($options, Metronic::LAYOUT_FULL_WIDTH); 31 | break; 32 | case Metronic::LAYOUT_BOXED: 33 | Html::addCssClass($options, Metronic::LAYOUT_BOXED); 34 | break; 35 | } 36 | 37 | break; 38 | case 'header': 39 | Html::addCssClass($options, 'header navbar'); 40 | if (strcasecmp(Metronic::getComponent()->headerOption, 'fixed') === 0) { 41 | Html::addCssClass($options, 'navbar-fixed-top'); 42 | } else { 43 | Html::addCssClass($options, 'navbar-static-top'); 44 | } 45 | break; 46 | } 47 | 48 | return $asString ? Html::renderTagAttributes($options) : $options; 49 | } 50 | } -------------------------------------------------------------------------------- /layouts/main.php: -------------------------------------------------------------------------------- 1 | beginPage(); 16 | Metronic::registerThemeAsset($this); 17 | ?> 18 | 19 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | <?= Html::encode($this->title) ?> 31 | head() ?> 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | > 40 | beginBody() ?> 41 | 'My Company', 45 | 'brandLogoUrl' => Metronic::getAssetsUrl($this) . '/img/logo.png', 46 | 'brandUrl' => Yii::$app->homeUrl, 47 | 'options' => Layout::getHtmlOptions('header', false), 48 | ] 49 | ); 50 | echo HorizontalMenu::widget( 51 | [ 52 | 'items' => [ 53 | [ 54 | 'label' => 'Mega menu', 55 | 'type' => HorizontalMenu::ITEM_TYPE_MEGA, 56 | 'items' => [ 57 | [ 58 | //'options' => '', 59 | 'label' => 'Layouts', 60 | 'items' => [ 61 | ['label' => 'Promo Page', 'url' => ['/site/index']], 62 | ['label' => 'Email Templates'], 63 | ] 64 | ], 65 | [ 66 | //'options' => '', 67 | 'label' => 'Layouts 2', 68 | 'items' => [ 69 | ['label' => 'Promo Page 2'], 70 | ['label' => 'Email Templates 2'], 71 | ] 72 | ], 73 | ] 74 | ], 75 | [ 76 | 'label' => 'Full Mega menu', 77 | 'type' => HorizontalMenu::ITEM_TYPE_FULL_MEGA, 78 | 'text' => \icron\metronic\widgets\Accordion::widget( 79 | [ 80 | 'items' => [ 81 | [ 82 | 'header' => 'Item 1', 83 | 'content' => 'Content 1...', 84 | // open its content by default 85 | 'contentOptions' => ['class' => 'in'], 86 | 'type' => \icron\metronic\widgets\Accordion::ITEM_TYPE_SUCCESS, 87 | ], 88 | [ 89 | 'header' => 'Item 2', 90 | 'content' => 'Content 2...', 91 | ], 92 | ], 93 | 'itemConfig' => ['showIcon' => true], 94 | ] 95 | ), 96 | 'items' => [ 97 | [ 98 | //'options' => '', 99 | 'label' => 'Layouts', 100 | 'items' => [ 101 | ['label' => 'Promo Page', 'url' => ['/site/index']], 102 | ['label' => 'Email Templates'], 103 | ] 104 | ], 105 | [ 106 | //'options' => '', 107 | 'label' => 'Layouts 2', 108 | 'items' => [ 109 | ['label' => 'Promo Page 2'], 110 | ['label' => 'Email Templates 2'], 111 | ] 112 | ], 113 | ] 114 | ], 115 | [ 116 | 117 | 'label' => 'Home', 118 | 'items' => [ 119 | ['label' => 'About', 'url' => ['/site/about']], 120 | ['label' => 'About', 'url' => ['/site/about']], 121 | ['label' => 'About', 'url' => ['/site/about']], 122 | ['label' => 'About', 'url' => ['/site/about']], 123 | ['label' => 'About', 'url' => ['/site/about']], 124 | ['label' => 'About', 'url' => ['/site/about']], 125 | ] 126 | ], 127 | [ 128 | 'label' => 'Home 2', 129 | 'url' => ['/site/index'], 130 | 'items' => [ 131 | [ 132 | 'badge' => Badge::widget(['label' => 'Новинка', 'round' => false]), 133 | 'label' => 'About', 134 | 'url' => ['/site/about'] 135 | ], 136 | ['label' => 'About', 'url' => ['/site/about']], 137 | [ 138 | 'label' => 'About', 139 | 'url' => ['/site/about'], 140 | 'items' => [ 141 | ['label' => 'About', 'url' => ['/site/about']], 142 | ['label' => 'About', 'url' => ['/site/about']], 143 | ['label' => 'Index', 'url' => ['/site/index']], 144 | ] 145 | ], 146 | ['label' => 'About', 'url' => ['/site/about']], 147 | ] 148 | ], 149 | 150 | ], 151 | ] 152 | ); 153 | echo Nav::widget( 154 | [ 155 | 'items' => [ 156 | [ 157 | 'icon' => 'fa fa-warning', 158 | 'badge' => Badge::widget(['label' => 'xxx']), 159 | 'label' => 'Home', 160 | 'url' => ['/site/index'], 161 | // dropdown title 162 | 'title' => 'xx', 163 | 'more' => ['label' => 'xxx', 'url' => '/', 'icon' => 'm-icon-swapright'], 164 | // scroller 165 | 'scroller' => ['height' => 200], 166 | // end dropdown 167 | 'items' => [ 168 | ['label' => 'About', 'url' => ['/site/aboutz']], 169 | ['label' => 'About', 'url' => ['/site/aboutz']], 170 | ['label' => 'About', 'url' => ['/site/aboutz']], 171 | ['label' => 'About', 'url' => ['/site/aboutz']], 172 | ['label' => 'About', 'url' => ['/site/aboutz']], 173 | ['label' => 'About', 'url' => ['/site/aboutz']], 174 | ] 175 | ], 176 | [ 177 | 'label' => Nav::userItem('Bob Nilson', Metronic::getAssetsUrl($this) . '/img/avatar1_small.jpg'), 178 | 'url' => '#', 179 | 'type' => 'user', 180 | 'items' => [ 181 | [ 182 | 'icon' => 'fa fa-calendar', 183 | 'label' => 'About', 184 | 'url' => ['/site/about'], 185 | 'badge' => Badge::widget(['label' => 'xxx']), 186 | ], 187 | ['label' => 'About', 'url' => ['/site/about']], 188 | ['label' => 'About', 'url' => ['/site/about']], 189 | ['label' => 'About', 'url' => ['/site/about']], 190 | ['divider'], 191 | ['label' => 'About', 'url' => ['/site/about']], 192 | ['label' => 'About', 'url' => ['/site/about']], 193 | ] 194 | ], 195 | // [ 'label' => 'Contact', 'url' => ['/site/contact']], 196 | ], 197 | ] 198 | ); 199 | NavBar::end(); 200 | ?> 201 | layoutOption == Metronic::LAYOUT_BOXED) ? 203 | Html::beginTag('div', ['class' => 'container']) : ''; ?> 204 | 205 |
    206 | 207 | true, 211 | 'items' => [ 212 | // Important: you need to specify url as 'controller/action', 213 | // not just as 'controller' even if default action is used. 214 | ['icon' => 'fa fa-home', 'label' => 'Home', 'url' => ['site/indea']], 215 | // 'Products' menu item will be selected as long as the route is 'product/index' 216 | [ 217 | 'icon' => 'fa fa-cogs', 218 | 'badge' => Badge::widget(['label' => 'New', 'round' => false, 'type' => Badge::TYPE_SUCCESS]), 219 | 'label' => 'Products', 220 | 'url' => '#', 221 | 'items' => [ 222 | ['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']], 223 | [ 224 | 'label' => 'Home', 225 | 'url' => '#', 226 | 'items' => [ 227 | [ 228 | 'icon' => 'fa fa-cogs', 229 | 'label' => 'Products', 230 | 'url' => ['site/index'], 231 | 'badge' => Badge::widget( 232 | ['label' => 'New', 'round' => false, 'type' => Badge::TYPE_SUCCESS] 233 | ), 234 | ], 235 | ] 236 | ], 237 | ] 238 | ], 239 | [ 240 | 'icon' => 'fa fa-bookmark-o', 241 | 'label' => 'UI Features', 242 | 'url' => '#', 243 | 'items' => [ 244 | [ 245 | 'label' => 'Buttons & Icons', 246 | 'url' => ['site/'], 247 | ], 248 | ], 249 | ], 250 | [ 251 | 'icon' => 'fa fa-user', 252 | 'label' => 'Login', 253 | 'url' => ['site/login'], 254 | 'visible' => Yii::$app->user->isGuest 255 | ], 256 | [ 257 | 'icon' => 'fa fa-user', 258 | 'label' => 'Login', 259 | 'url' => ['site/login'], 260 | 'visible' => Yii::$app->user->isGuest 261 | ], 262 | ], 263 | ] 264 | ); ?> 265 | 266 | 267 |
    268 |
    269 | 270 |
    271 |
    272 | 273 |

    274 | title) ?> 275 | title) ?> 276 |

    277 | [ 281 | 'label' => 'Action', 282 | 'button' => [ 283 | 'type' => Button::TYPE_M_BLUE, 284 | 'options' => ['data-hover' => 'dropdown', 'delay' => '1000'], 285 | ], 286 | 'dropdown' => [ 287 | 'items' => [ 288 | ['label' => 'DropdownA', 'url' => '/'], 289 | ['divider'], 290 | ['label' => 'DropdownB', 'url' => '#'], 291 | ], 292 | ], 293 | ], 294 | 'homeLink' => [ 295 | 'label' => 'Home', 296 | 'icon' => 'fa fa-home', 297 | 'url' => ['/'] 298 | ], 299 | 'links' => [ 300 | [ 301 | 'icon' => 'fa fa-cogs', 302 | 'label' => 'Sample Post', 303 | 'url' => ['post/edit', 'id' => 1] 304 | ], 305 | 'Edit', 306 | ], 307 | ] 308 | ); 309 | ?> 310 |
    311 |
    312 | 313 | 314 |
    315 |
    316 | 317 |
    318 |
    319 | 320 |
    321 |
    322 | 323 |
    324 | 325 | 326 | 336 | layoutOption == Metronic::LAYOUT_BOXED) ? Html::endTag('div') : ''; ?> 337 | endBody() ?> 338 | 339 | 340 | 341 | endPage() ?> -------------------------------------------------------------------------------- /widgets/Accordion.php: -------------------------------------------------------------------------------- 1 | [ 21 | * [ 22 | * 'header' => 'Item 1', 23 | * 'content' => 'Content 1...', 24 | * // open its content by default 25 | * 'contentOptions' => ['class' => 'in'], 26 | * 'type' => Accordion::ITEM_TYPE_SUCCESS, 27 | * ], 28 | * [ 29 | * 'header' => 'Item 2', 30 | * 'content' => 'Content 2...', 31 | * ], 32 | * ], 33 | * 'itemConfig' => ['showIcon' => true], 34 | *]); 35 | * ``` 36 | * 37 | * @see http://getbootstrap.com/javascript/#collapse 38 | */ 39 | class Accordion extends Widget 40 | { 41 | // Item types 42 | const ITEM_TYPE_DEFAULT = 'default'; 43 | const ITEM_TYPE_SUCCESS = 'success'; 44 | const ITEM_TYPE_DANGER = 'danger'; 45 | const ITEM_TYPE_WARNING = 'warning'; 46 | const ITEM_TYPE_INFO = 'info'; 47 | 48 | /** 49 | * @var array list of groups in the collapse widget. Each array element represents a single 50 | * group with the following structure: 51 | * 52 | * ```php 53 | * [ 54 | * // required, the header (HTML) of the group 55 | * 'header' => 'Item 1', 56 | * // required, the content (HTML) of the group 57 | * 'content' => '', 58 | * // optional the HTML attributes of the content group 59 | * 'contentOptions' => [], 60 | * // optional, the HTML attributes of the group 61 | * 'options' => [], 62 | * // optional, the item type. Valid values are 'default', 'success', 'danger', 'warning', 'info' 63 | * // Determines color of the item. 64 | * 'type' => '', 65 | * ] 66 | * ``` 67 | */ 68 | public $items = []; 69 | /** 70 | * @var array the default configuration used by item. 71 | */ 72 | public $itemConfig = []; 73 | 74 | /** 75 | * Initializes the widget. 76 | */ 77 | public function init() 78 | { 79 | parent::init(); 80 | Html::addCssClass($this->options, 'panel-group accordion'); 81 | } 82 | 83 | /** 84 | * Renders the widget. 85 | */ 86 | public function run() 87 | { 88 | echo Html::beginTag('div', $this->options) . "\n"; 89 | echo $this->renderItems() . "\n"; 90 | echo Html::endTag('div') . "\n"; 91 | $this->registerPlugin('collapse'); 92 | } 93 | 94 | /** 95 | * Renders collapsible items as specified on [[items]]. 96 | * @return string the rendering result 97 | * @throws InvalidConfigException. 98 | */ 99 | public function renderItems() 100 | { 101 | $items = []; 102 | $index = 0; 103 | foreach ($this->items as $item) { 104 | if (!isset($item['header'])) { 105 | throw new InvalidConfigException("The 'header' option is required."); 106 | } 107 | if (!isset($item['content'])) { 108 | throw new InvalidConfigException("The 'content' option is required."); 109 | } 110 | 111 | $options = ArrayHelper::getValue($item, 'options', []); 112 | $type = ArrayHelper::getValue($item, 'type', self::ITEM_TYPE_DEFAULT); 113 | Html::addCssClass($options, 'panel panel-' . $type); 114 | $items[] = Html::tag('div', $this->renderItem(array_merge($this->itemConfig, $item), ++$index), $options); 115 | } 116 | 117 | return implode("\n", $items); 118 | } 119 | 120 | /** 121 | * Renders a single collapsible item group 122 | * @param array $item a single item from [[items]] 123 | * @param integer $index the item index as each item group content must have an id 124 | * @return string the rendering result 125 | * @throws InvalidConfigException 126 | */ 127 | protected function renderItem($item, $index) 128 | { 129 | $options = ArrayHelper::getValue($item, 'options', []); 130 | $type = ArrayHelper::getValue($item, 'type', self::ITEM_TYPE_DEFAULT); 131 | Html::addCssClass($options, 'panel panel-' . $type); 132 | $id = $this->options['id'] . '-collapse' . $index; 133 | $options = ArrayHelper::getValue($item, 'contentOptions', []); 134 | $options['id'] = $id; 135 | Html::addCssClass($options, 'panel-collapse collapse'); 136 | $styled = ''; 137 | if (ArrayHelper::getValue($item, 'showIcon', false)) { 138 | if (preg_match('/[^\w]*in[^\w]*/', $options['class'])) { 139 | $styled = 'accordion-toggle-styled'; 140 | } else { 141 | $styled = 'accordion-toggle-styled collapsed'; 142 | } 143 | } 144 | $headerToggle = Html::a( 145 | $item['header'], 146 | '#' . $id, 147 | [ 148 | 'class' => 'accordion-toggle ' . $styled, 149 | 'data-toggle' => 'collapse', 150 | 'data-parent' => '#' . $this->options['id'] 151 | ] 152 | ) . "\n"; 153 | 154 | $header = Html::tag('h4', $headerToggle, ['class' => 'panel-title']); 155 | $content = Html::tag('div', $item['content'], ['class' => 'panel-body']) . "\n"; 156 | 157 | $group = []; 158 | $group[] = Html::tag('div', $header, ['class' => 'panel-heading']); 159 | $group[] = Html::tag('div', $content, $options); 160 | 161 | return implode("\n", $group); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /widgets/ActiveField.php: -------------------------------------------------------------------------------- 1 | options['tag']) ? $this->options['tag'] : 'div') . "\n"; 29 | } 30 | /** 31 | * Generates a icon for input. 32 | * @param array $options icon options. 33 | * The options have following structure: 34 | * ```php 35 | * [ 36 | * 'icon' => 'fa fa-bookmark-o', 37 | * 'position' => ActiveField::ICON_POSITION_LEFT, 38 | * ] 39 | * ``` 40 | * @return static the field object itself 41 | */ 42 | public function icon($options = []) 43 | { 44 | $icon = ArrayHelper::remove($options, 'icon', null); 45 | if($icon) { 46 | $position = ArrayHelper::remove($options, 'position', self::ICON_POSITION_LEFT); 47 | if ($position != self::ICON_POSITION_RIGHT) { 48 | $position = ''; 49 | } 50 | $this->parts['{input}'] = Html::tag('i', '', ['class' => $icon]) . "\n" . $this->parts['{input}']; 51 | $this->parts['{input}'] = Html::tag('div', $this->parts['{input}'], ['class' => 'input-icon ' . $position]); 52 | } 53 | 54 | return $this; 55 | } 56 | /** 57 | * Generates a groupAddon for input. 58 | * GroupAddon similar to [[icon()]]. 59 | * @param array $options icon options. 60 | * The options have following structure: 61 | * ```php 62 | * [ 63 | * 'icon' => 'fa fa-bookmark-o', 64 | * 'position' => ActiveField::ICON_POSITION_LEFT, 65 | * ] 66 | * ``` 67 | * @return static the field object itself 68 | */ 69 | public function groupAddon($options = []) 70 | { 71 | $icon = ArrayHelper::remove($options, 'icon', null); 72 | if($icon) { 73 | $addon = Html::tag('span', Html::tag('i', '', ['class' => $icon]), ['class' => 'input-group-addon']); 74 | $position = ArrayHelper::remove($options, 'position', self::ICON_POSITION_LEFT); 75 | if ($position == self::ICON_POSITION_RIGHT) { 76 | $this->parts['{input}'] .= "\n" . $addon; 77 | } else { 78 | $this->parts['{input}'] = $addon . "\n" . $this->parts['{input}']; 79 | } 80 | $this->parts['{input}'] = Html::tag('div', $this->parts['{input}'], ['class' => 'input-group']); 81 | } 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * Generates a tag that contains error. 88 | * @param $error string the error to use. 89 | * @param array $options the tag options in terms of name-value pairs. It will be merged with [[errorOptions]]. 90 | * @return static the field object itself 91 | */ 92 | public function staticError($error, $options = []) 93 | { 94 | $options = array_merge($this->errorOptions, $options); 95 | $tag = isset($options['tag']) ? $options['tag'] : 'div'; 96 | unset($options['tag']); 97 | $this->parts['{error}'] = Html::tag($tag, $error, $options); 98 | 99 | return $this; 100 | } 101 | 102 | /** 103 | * Generates spinner component. 104 | * @param array $options spinner options 105 | * @return $this 106 | */ 107 | public function spinner($options = []) 108 | { 109 | $this->parts['{input}'] = Spinner::widget(array_merge($options, ['model' => $this->model, 'attribute' => $this->attribute])); 110 | 111 | return $this; 112 | } 113 | 114 | /** 115 | * Generates dateRangePicker component [[DateRangePicker]]. 116 | * @param array $options dateRangePicker options 117 | * @return $this 118 | */ 119 | public function dateRangePicker($options = []) 120 | { 121 | if ($this->form->type == ActiveForm::TYPE_VERTICAL) { 122 | //$options = array_merge($options, ['options' => ['style' => 'display:table-cell;']]); 123 | $options = array_merge($options, ['options' => ['class' => 'show']]); 124 | } 125 | $this->parts['{input}'] = DateRangePicker::widget(array_merge($options, ['model' => $this->model, 'attribute' => $this->attribute])); 126 | 127 | return $this; 128 | } 129 | 130 | /** 131 | * Generates dateRangePicker component [[DateRangePicker]]. 132 | * @param array $options dateRangePicker options 133 | * @return $this 134 | */ 135 | public function datePicker($options = []) 136 | { 137 | /*if ($this->form->type == ActiveForm::TYPE_VERTICAL) { 138 | //$options = array_merge($options, ['options' => ['style' => 'display:table-cell;']]); 139 | $options = array_merge($options, ['options' => ['class' => 'show']]); 140 | }*/ 141 | $this->parts['{input}'] = DatePicker::widget(array_merge($options, ['model' => $this->model, 'attribute' => $this->attribute])); 142 | 143 | return $this; 144 | } 145 | 146 | /** 147 | * Generates select2 component [[Select2]]. 148 | * @param array $options select2 options 149 | * @return $this 150 | */ 151 | public function select2($options = []) 152 | { 153 | $this->parts['{input}'] = Select2::widget(array_merge($options, ['model' => $this->model, 'attribute' => $this->attribute])); 154 | 155 | return $this; 156 | } 157 | 158 | /** 159 | * Generates multiSelect component [[MultiSelect]]. 160 | * @param array $options multiSelect options 161 | * @return $this 162 | */ 163 | public function multiSelect($options = []) 164 | { 165 | $this->parts['{input}'] = MultiSelect::widget(array_merge($options, ['model' => $this->model, 'attribute' => $this->attribute])); 166 | 167 | return $this; 168 | } 169 | 170 | public function range($options = []) 171 | { 172 | $this->parts['{input}'] = IonRangeSlider::widget(array_merge($options, ['model' => $this->model, 'attribute' => $this->attribute])); 173 | 174 | return $this; 175 | } 176 | } -------------------------------------------------------------------------------- /widgets/ActiveForm.php: -------------------------------------------------------------------------------- 1 | ActiveForm::BUTTONS_POSITION_LEFT, 53 | * //optional, vertical position 54 | * 'position' => ActiveForm::BUTTONS_POSITION_BOTTOM, 55 | * //optional, array of buttons 56 | * 'items' => [ 57 | * Button::widget('label' => 'Save', 'options' => ['type' => 'submit']), 58 | * Button::widget('label' => 'Back'), 59 | * ], 60 | * // optional, the HTML attributes (name-value pairs) for the form actions tag. 61 | * 'options' => ['class' => 'fluid'] 62 | * ] 63 | * ``` 64 | */ 65 | public $buttons = []; 66 | /** 67 | * @var array the default configuration used by [[field()]] when creating a new field object. 68 | */ 69 | public $fieldConfig = []; 70 | 71 | /** 72 | * @var bool indicates whether the tag 'form' is rendered. 73 | * In case 'true' widget renders 'div' instead 'form'. 74 | */ 75 | public $fake = false; 76 | /** 77 | * Initializes the widget. 78 | * This renders the form open tag. 79 | */ 80 | public function init() 81 | { 82 | if (!isset($this->options['id'])) { 83 | $this->options['id'] = $this->getId(); 84 | } 85 | 86 | switch ($this->type) { 87 | case self::TYPE_HORIZONTAL: 88 | if ($this->stripped) { 89 | Html::addCssClass($this->options, 'form-row-stripped'); 90 | } 91 | if ($this->separated) { 92 | Html::addCssClass($this->options, 'form-row-seperated'); 93 | } 94 | if ($this->bordered) { 95 | Html::addCssClass($this->options, 'form-bordered'); 96 | } 97 | Html::addCssClass($this->options, 'form-horizontal'); 98 | $this->fieldConfig = ArrayHelper::merge([ 99 | 'labelOptions' => ['class' => 'col-md-3 control-label'], 100 | 'template' => "{label}\n" . Html::tag('div', "{input}\n{error}\n{hint}", ['class' => 'col-md-9']), 101 | ], $this->fieldConfig); 102 | break; 103 | case self::TYPE_INLINE: 104 | Html::addCssClass($this->options, 'form-inline'); 105 | $this->fieldConfig = ArrayHelper::merge([ 106 | 'labelOptions' => ['class' => 'sr-only'], 107 | ], $this->fieldConfig); 108 | break; 109 | } 110 | if (!isset($this->fieldConfig['class'])) { 111 | $this->fieldConfig['class'] = ActiveField::className(); 112 | } 113 | if ($this->fake) { 114 | echo Html::beginTag('div', $this->options); 115 | } else { 116 | echo Html::beginForm($this->action, $this->method, $this->options); 117 | } 118 | echo $this->renderActions(self::BUTTONS_POSITION_TOP); 119 | echo Html::beginTag('div', ['class' => 'form-body']); 120 | } 121 | /** 122 | * Runs the widget. 123 | * This registers the necessary javascript code and renders the form close tag. 124 | */ 125 | public function run() 126 | { 127 | echo Html::endTag('div'); 128 | echo $this->renderActions(self::BUTTONS_POSITION_BOTTOM); 129 | if (!empty($this->attributes)) { 130 | $id = $this->options['id']; 131 | $options = Json::encode($this->getClientOptions()); 132 | $attributes = Json::encode($this->attributes); 133 | $view = $this->getView(); 134 | ActiveFormAsset::register($view); 135 | $view->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);"); 136 | } 137 | if ($this->fake) { 138 | echo Html::endTag('div'); 139 | } else { 140 | echo Html::endForm(); 141 | } 142 | } 143 | 144 | /** 145 | * Generates a form field. 146 | * A form field is associated with a model and an attribute. It contains a label, an input and an error message 147 | * and use them to interact with end users to collect their inputs for the attribute. 148 | * @param Model $model the data model 149 | * @param string $attribute the attribute name or expression. See [[Html::getAttributeName()]] for the format 150 | * about attribute expression. 151 | * @param array $options the additional configurations for the field object 152 | * @return ActiveField the created ActiveField object 153 | * @see fieldConfig 154 | */ 155 | public function field($model, $attribute, $options = []) { 156 | return parent::field($model, $attribute, $options); 157 | } 158 | 159 | protected function renderActions($currentPosition) 160 | { 161 | $position = ArrayHelper::getValue($this->buttons, 'position', self::BUTTONS_POSITION_BOTTOM); 162 | if (!empty($this->buttons['items']) && $position == $currentPosition) { 163 | $actionsOptions = ArrayHelper::getValue($this->buttons, 'options', []); 164 | Html::addCssClass($actionsOptions, 'form-actions'); 165 | if ($position == self::BUTTONS_POSITION_TOP) { 166 | Html::addCssClass($actionsOptions, 'top'); 167 | } 168 | if (isset($this->buttons['align']) && $this->buttons['align'] == self::BUTTONS_ALIGN_RIGHT) { 169 | Html::addCssClass($actionsOptions, 'right'); 170 | } 171 | $rowOptions = []; 172 | $buttons = implode("\n", $this->buttons['items']); 173 | switch ($this->type) { 174 | case self::TYPE_HORIZONTAL: 175 | Html::addCssClass($actionsOptions, 'fluid'); 176 | preg_match('#col-md-(\d+)#', $this->fieldConfig['labelOptions']['class'], $matches); 177 | if (isset($matches[1])) { 178 | $offset = $matches[1]; 179 | Html::addCssClass($rowOptions, 'col-md-offset-' . $offset); 180 | Html::addCssClass($rowOptions, 'col-md-' . 12 - $offset); 181 | $buttons = Html::tag('div', $buttons, $rowOptions); 182 | } 183 | break; 184 | } 185 | 186 | return Html::tag('div', $buttons, $actionsOptions); 187 | } 188 | 189 | return ''; 190 | } 191 | } -------------------------------------------------------------------------------- /widgets/Alert.php: -------------------------------------------------------------------------------- 1 | 'Say hello...', 21 | * 'closeButton' => [ 22 | * 'label' => '×', 23 | * 'tag' => 'a', 24 | * 'type' => Alert::TYPE_DANGER, 25 | * ], 26 | * ]); 27 | * ``` 28 | * 29 | * The following example will show the content enclosed between the [[begin()]] 30 | * and [[end()]] calls within the alert box: 31 | * 32 | * ```php 33 | * Alert::begin([ 34 | * 'type' => Alert::TYPE_DANGER, 35 | * 'closeButton' => ['label' => '×'], 36 | * ]); 37 | * 38 | * echo 'Say hello...'; 39 | * 40 | * Alert::end(); 41 | * ``` 42 | */ 43 | class Alert extends \yii\bootstrap\Alert 44 | { 45 | // type 46 | const TYPE_SUCCESS = 'success'; 47 | const TYPE_INFO = 'info'; 48 | const TYPE_WARNING = 'warning'; 49 | const TYPE_DANGER = 'danger'; 50 | /** 51 | * @var string the note type. 52 | * Valid values are 'success', 'info', 'warning', 'danger'. 53 | */ 54 | public $type = self::TYPE_SUCCESS; 55 | /** 56 | * @var boolean when set, alert has a larger block size. 57 | */ 58 | public $block = true; 59 | /** 60 | * @var boolean when set, alert will fade out using transitions when closed. 61 | */ 62 | public $fade = true; 63 | 64 | /** 65 | * Initializes the widget. 66 | */ 67 | public function init() 68 | { 69 | Html::addCssClass($this->options, 'alert-'.$this->type); 70 | if ($this->block) { 71 | Html::addCssClass($this->options, 'alert-block'); 72 | } 73 | if ($this->fade) { 74 | Html::addCssClass($this->options, 'fade in'); 75 | } 76 | parent::init(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /widgets/Badge.php: -------------------------------------------------------------------------------- 1 | 'NEW', 18 | * 'type' => Badge::TYPE_SUCCESS, 19 | * 'round' 20 | * ]); 21 | * 22 | * @package icron\metronic\widgets 23 | */ 24 | class Badge extends \yii\base\Widget 25 | { 26 | // type 27 | const TYPE_DEFAULT = ''; 28 | const TYPE_GRAY = 'default'; 29 | const TYPE_SUCCESS = 'success'; 30 | const TYPE_WARNING = 'warning'; 31 | const TYPE_DANGER = 'danger'; 32 | const TYPE_INFO = 'info'; 33 | /** 34 | * @var string the badge label 35 | */ 36 | public $label; 37 | /** 38 | * @var string the badge type 39 | * Valid values '', 'default', 'success', 'warning', 'danger', 'info' 40 | */ 41 | public $type = self::TYPE_DEFAULT; 42 | /** 43 | * @var bool Indicates whether badge is rounded or not. 44 | */ 45 | public $round = true; 46 | 47 | /** 48 | * Executes the widget. 49 | */ 50 | public function run() 51 | { 52 | $options = []; 53 | Html::addCssClass($options, 'badge'); 54 | if (!$this->round) { 55 | Html::addCssClass($options, 'badge-roundless'); 56 | } 57 | Html::addCssClass($options, 'badge-' . $this->type); 58 | 59 | echo Html::tag('span', $this->label, $options); 60 | } 61 | } -------------------------------------------------------------------------------- /widgets/Breadcrumbs.php: -------------------------------------------------------------------------------- 1 | [ 27 | * ['label' => 'Sample Post', 'url' => ['post/edit', 'id' => 1]], 28 | * 'Edit', 29 | * ], 30 | * ]); 31 | * ``` 32 | * 33 | * Because breadcrumbs usually appears in nearly every page of a website, you may consider placing it in a layout view. 34 | * You can use a view parameter (e.g. `$this->params['breadcrumbs']`) to configure the links in different 35 | * views. In the layout view, you assign this view parameter to the [[links]] property like the following: 36 | * 37 | * ```php 38 | * // $this is the view object currently being used 39 | * echo Breadcrumbs::widget([ 40 | * 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], 41 | * ]); 42 | * ``` 43 | */ 44 | class Breadcrumbs extends \yii\widgets\Breadcrumbs 45 | { 46 | /** 47 | * @var string the template used to render each inactive item in the breadcrumbs. The token `{link}` 48 | * will be replaced with the actual HTML link for each inactive item. 49 | */ 50 | public $itemTemplate = "
  • {link}
  • \n"; 51 | /** 52 | * @var string the template used to render each active item in the breadcrumbs. The token `{link}` 53 | * will be replaced with the actual HTML link for each active item. 54 | */ 55 | public $activeItemTemplate = "
  • {link}
  • \n"; 56 | /** 57 | * @var array|string 58 | */ 59 | public $actions; 60 | 61 | public function init() 62 | { 63 | parent::init(); 64 | Html::addCssClass($this->options, 'page-breadcrumb breadcrumb'); 65 | } 66 | 67 | 68 | public function run() 69 | { 70 | if (empty($this->links)) { 71 | return; 72 | } 73 | $links = []; 74 | 75 | if ($this->actions !== null) { 76 | Html::addCssClass($this->actions['dropdown']['options'], 'pull-right'); 77 | if (is_string($this->actions)) { 78 | $links[] = $this->actions; 79 | } else if (is_array($this->actions)) { 80 | $links[] = ButtonDropdown::widget($this->actions); 81 | } else { 82 | throw new InvalidConfigException('Actions must be of type "string" or "array".'); 83 | } 84 | } 85 | 86 | if ($this->homeLink === null) { 87 | $links[] = $this->renderItem([ 88 | 'label' => Yii::t('yii', 'Home'), 89 | 'url' => Yii::$app->homeUrl, 90 | ], $this->itemTemplate); 91 | } elseif ($this->homeLink !== false) { 92 | $links[] = $this->renderItem($this->homeLink, $this->itemTemplate); 93 | } 94 | foreach ($this->links as $link) { 95 | if (!is_array($link)) { 96 | $link = ['label' => $link]; 97 | } 98 | $links[] = $this->renderItem($link, isset($link['url']) ? $this->itemTemplate : $this->activeItemTemplate); 99 | } 100 | echo Html::tag($this->tag, implode('', $links), $this->options); 101 | } 102 | 103 | /** 104 | * Renders a single breadcrumb item. 105 | * @param array $link the link to be rendered. It must contain the "label" element. The "url" element is optional. 106 | * @param string $template the template to be used to rendered the link. The token "{link}" will be replaced by the link. 107 | * @return string the rendering result 108 | * @throws InvalidConfigException if `$link` does not have "label" element. 109 | */ 110 | protected function renderItem($link, $template) 111 | { 112 | if (isset($link['label'])) { 113 | $label = $this->encodeLabels ? Html::encode($link['label']) : $link['label']; 114 | } else { 115 | throw new InvalidConfigException('The "label" element is required for each link.'); 116 | } 117 | 118 | $icon = ArrayHelper::getValue($link, 'icon', ''); 119 | if ($icon) { 120 | $icon = Html::tag('i', '', ['class' => 'fa ' . $icon]) . ' '; 121 | } 122 | if (isset($link['url'])) { 123 | return strtr($template, ['{link}' => $icon . Html::a($label, $link['url'])]); 124 | } else { 125 | return strtr($template, ['{link}' => $icon . $label]); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /widgets/Button.php: -------------------------------------------------------------------------------- 1 | 'Action', 21 | * 'icon' => 'fa fa-bookmark-o', 22 | * 'iconPosition' => Button::ICON_POSITION_LEFT, 23 | * 'size' => Button::SIZE_SMALL, 24 | * 'disabled' => false, 25 | * 'block' => false, 26 | * 'type' => Button::TYPE_M_BLUE, 27 | * ]); 28 | * ``` 29 | */ 30 | class Button extends \yii\bootstrap\Button 31 | { 32 | // Button bootstrap types. 33 | const TYPE_PRIMARY = 'primary'; 34 | const TYPE_INFO = 'info'; 35 | const TYPE_SUCCESS = 'success'; 36 | const TYPE_WARNING = 'warning'; 37 | const TYPE_DANGER = 'danger'; 38 | const TYPE_INVERSE = 'inverse'; 39 | const TYPE_LINK = 'link'; 40 | 41 | // Button metronic types. 42 | const TYPE_M_DEFAULT = 'default'; 43 | const TYPE_M_RED = 'red'; 44 | const TYPE_M_BLUE = 'blue'; 45 | const TYPE_M_GREEN = 'green'; 46 | const TYPE_M_YELLOW = 'yellow'; 47 | const TYPE_M_PURPLE = 'purple'; 48 | const TYPE_M_DARK = 'dark'; 49 | 50 | // Button sizes. 51 | const SIZE_MINI = 'xs'; 52 | const SIZE_SMALL = 'sm'; 53 | const SIZE_LARGE = 'lg'; 54 | 55 | // Позиция иконки 56 | const ICON_POSITION_LEFT = 'left'; 57 | const ICON_POSITION_RIGHT = 'right'; 58 | 59 | /** 60 | * @var string The button size. 61 | * Valid values are 'xs', 'sm', 'lg'. 62 | */ 63 | public $size; 64 | /** 65 | * @var string The button type. 66 | * Valid values for metronic styles are 'default', 'red', 'blue', 'green', 'yellow', 'purple', 'dark'. 67 | * Valid values for bootstrap styles are 'primary', 'info', 'success', 'warning', 'danger', 'inverse', 'link'. 68 | */ 69 | public $type = self::TYPE_M_DEFAULT; 70 | /** 71 | * @var string The button icon. 72 | */ 73 | public $icon; 74 | /** 75 | * @var string Icon position. 76 | * Valid values are 'left', 'right'. 77 | */ 78 | public $iconPosition = self::ICON_POSITION_LEFT; 79 | /** 80 | * @var bool Indicates whether button is disabled or not. 81 | */ 82 | public $disabled = false; 83 | /** 84 | * @var bool Indicates whether the button should span the full width of the a parent. 85 | */ 86 | public $block = false; 87 | 88 | /** 89 | * Initializes the widget. 90 | */ 91 | public function init() 92 | { 93 | parent::init(); 94 | $bootstrapTypes = [ 95 | self::TYPE_PRIMARY, 96 | self::TYPE_INFO, 97 | self::TYPE_SUCCESS, 98 | self::TYPE_WARNING, 99 | self::TYPE_DANGER, 100 | self::TYPE_INVERSE, 101 | self::TYPE_LINK, 102 | ]; 103 | 104 | $metronicTypes = [ 105 | self::TYPE_M_DEFAULT, 106 | self::TYPE_M_RED, 107 | self::TYPE_M_BLUE, 108 | self::TYPE_M_GREEN, 109 | self::TYPE_M_YELLOW, 110 | self::TYPE_M_PURPLE, 111 | self::TYPE_M_DARK, 112 | ]; 113 | 114 | if (in_array($this->type, $bootstrapTypes)) { 115 | Html::addCssClass($this->options, 'btn-' . $this->type); 116 | } elseif (in_array($this->type, $metronicTypes)) { 117 | Html::addCssClass($this->options, $this->type); 118 | } else { 119 | throw new InvalidConfigException("The button type is invalid."); 120 | } 121 | 122 | $sizes = [ 123 | self::SIZE_MINI, 124 | self::SIZE_SMALL, 125 | self::SIZE_LARGE, 126 | ]; 127 | 128 | if (in_array($this->size, $sizes)) { 129 | Html::addCssClass($this->options, 'btn-' . $this->size); 130 | } 131 | 132 | if ($this->disabled === true) { 133 | Html::addCssClass($this->options, 'disabled'); 134 | } 135 | 136 | if ($this->block === true) { 137 | Html::addCssClass($this->options, 'btn-block'); 138 | } 139 | } 140 | 141 | /** 142 | * Renders the widget. 143 | */ 144 | public function run() 145 | { 146 | $label = $this->encodeLabel ? Html::encode($this->label) : $this->label; 147 | if ($this->icon !== null) { 148 | $icon = Html::tag('i', '', ['class' => $this->icon]); 149 | $label = strcasecmp($this->iconPosition, self::ICON_POSITION_LEFT) === 0 ? ($icon . ' ' . $label) : $label . ' ' . $icon; 150 | } 151 | echo Html::tag($this->tagName, $label, $this->options); 152 | $this->registerPlugin('button'); 153 | } 154 | } -------------------------------------------------------------------------------- /widgets/ButtonDropdown.php: -------------------------------------------------------------------------------- 1 | 'Action', 22 | * 'button' => [ 23 | * 'icon' => 'fa fa-bookmark-o', 24 | * 'iconPosition' => Button::ICON_POSITION_LEFT, 25 | * 'size' => Button::SIZE_SMALL, 26 | * 'disabled' => false, 27 | * 'block' => false, 28 | * 'type' => Button::TYPE_M_BLUE, 29 | * ], 30 | * 'dropdown' => [ 31 | * 'items' => [ 32 | * ['label' => 'DropdownA', 'url' => '/'], 33 | * ['label' => 'DropdownB', 'url' => '#'], 34 | * ], 35 | * ], 36 | * ]); 37 | * ``` 38 | * 39 | **/ 40 | class ButtonDropdown extends \yii\bootstrap\ButtonDropdown 41 | { 42 | /** 43 | * @var array The configuration array for [[Button]]. 44 | */ 45 | public $button = []; 46 | /** 47 | * Renders the widget. 48 | */ 49 | public function run() 50 | { 51 | echo Html::tag('li', $this->renderButton() . "\n" . $this->renderDropdown(), ['class' => 'btn-group']); 52 | } 53 | 54 | /** 55 | * Renders the button. 56 | * @return string the rendering result 57 | */ 58 | protected function renderButton() 59 | { 60 | Html::addCssClass($this->options, 'btn'); 61 | $label = $this->label; 62 | if ($this->encodeLabel) { 63 | $label = Html::encode($label); 64 | } 65 | if ($this->split) { 66 | $options = $this->options; 67 | $this->options['data-toggle'] = 'dropdown'; 68 | Html::addCssClass($this->options, 'dropdown-toggle'); 69 | $splitButton = Button::widget([ 70 | 'label' => '', 71 | 'encodeLabel' => false, 72 | 'options' => $this->button, 73 | ]); 74 | } else { 75 | $label .= ' '; 76 | $options = $this->options; 77 | if (!isset($options['href'])) { 78 | $options['href'] = '#'; 79 | } 80 | Html::addCssClass($options, 'dropdown-toggle'); 81 | $options['data-toggle'] = 'dropdown'; 82 | $splitButton = ''; 83 | } 84 | 85 | return Button::widget(ArrayHelper::merge($this->button, [ 86 | 'tagName' => $this->tagName, 87 | 'label' => $label, 88 | 'options' => $options, 89 | 'encodeLabel' => false, 90 | ])) . "\n" . $splitButton; 91 | } 92 | 93 | /** 94 | * Renders the dropdown 95 | * @return string the rendering result 96 | */ 97 | protected function renderDropdown() 98 | { 99 | $config = $this->dropdown; 100 | $config['clientOptions'] = false; 101 | return Dropdown::widget($config); 102 | } 103 | } -------------------------------------------------------------------------------- /widgets/ButtonGroup.php: -------------------------------------------------------------------------------- 1 | true, 21 | * 'buttons' => [ 22 | * ['label' => 'A'], 23 | * ['label' => 'B'], 24 | * ] 25 | * ]); 26 | * 27 | * // a button group with an item as a string 28 | * echo ButtonGroup::widget([ 29 | * 'buttons' => [ 30 | * Button::widget(['label' => 'A']), 31 | * ['label' => 'B'], 32 | * ] 33 | * ]); 34 | * ``` 35 | */ 36 | class ButtonGroup extends \yii\bootstrap\ButtonGroup 37 | { 38 | /** 39 | * @var bool Indicates whether the button group appears vertically stacked. 40 | */ 41 | public $stacked = false; 42 | 43 | /** 44 | * Initializes the widget. 45 | */ 46 | public function init() 47 | { 48 | if ($this->stacked === true) { 49 | Html::addCssClass($this->options, 'btn-group-vertical'); 50 | } else { 51 | Html::addCssClass($this->options, 'btn-group'); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /widgets/CheckboxList.php: -------------------------------------------------------------------------------- 1 | [ 22 | * 'full_name', 23 | * [ 24 | * 'name' => 'city', 25 | * 'label' => 'Location', 26 | * ] 27 | * ], 28 | * 'model' => $filterModel, 29 | * 'attribute' => 'columns', 30 | * ]); 31 | * ``` 32 | */ 33 | class CheckboxList extends InputWidget 34 | { 35 | /** 36 | * @var array list of checkbox items. 37 | * Each item must be either as string like the name checkbox 38 | * or as array with following special options: 39 | * - name, required, item name 40 | * - label, optional, item label 41 | * Item can not belong to the model. 42 | */ 43 | public $items = []; 44 | /** 45 | * @var string the model attribute that this widget is associated with. 46 | * This model attribute contains list of checkboxes name, separated $separator value. 47 | */ 48 | public $attribute; 49 | /** 50 | * @var string separator values 51 | */ 52 | public $separator = ','; 53 | /** 54 | * @var array the HTML attributes for items. 55 | */ 56 | public $itemOptions = []; 57 | /** 58 | * @var array items name, that will be checked. 59 | */ 60 | private $_checked = []; 61 | 62 | /** 63 | * Initializes the widget. 64 | */ 65 | public function init() 66 | { 67 | parent::init(); 68 | $items = []; 69 | if ($this->hasModel()) { 70 | $this->_checked = array_map('trim', explode(',', $this->model->{$this->attribute})); 71 | foreach ($this->items as $item) { 72 | $input = []; 73 | if (is_string($item)) { 74 | $input['name'] = $item; 75 | $input['label'] = $this->model->getAttributeLabel($item); 76 | } else { 77 | if (!isset($item['name'])) { 78 | throw new InvalidConfigException('Option "name" is required.'); 79 | } 80 | $input['name'] = $item['name']; 81 | $input['label'] = isset($item['label']) ? $item['label'] : $this->model->getAttributeLabel( 82 | $item['name'] 83 | ); 84 | } 85 | $items[] = $input; 86 | } 87 | } else { 88 | $this->_checked = array_map('trim', explode(',', $this->value)); 89 | foreach ($this->items as $item) { 90 | $input = []; 91 | if (is_string($item)) { 92 | $input['name'] = $item; 93 | $input['label'] = Inflector::camel2words($item); 94 | } else { 95 | if (!isset($item['name'])) { 96 | throw new InvalidConfigException('Option "name" is required.'); 97 | } 98 | $input['name'] = $item['name']; 99 | $input['label'] = isset($item['label']) ? $item['label'] : Inflector::camel2words($item['name']); 100 | } 101 | $items[] = $input; 102 | } 103 | } 104 | 105 | $this->items = $items; 106 | } 107 | 108 | /** 109 | * Executes the widget. 110 | */ 111 | public function run() 112 | { 113 | if ($this->hasModel()) { 114 | $hiddenInput = Html::activeHiddenInput($this->model, $this->attribute); 115 | $inputId = Html::getInputId($this->model, $this->attribute); 116 | } else { 117 | $hiddenInput = Html::textInput($this->name, $this->name); 118 | $inputId = $this->name; 119 | } 120 | 121 | $items = []; 122 | usort($this->items, function($a, $b){ 123 | $aKey = array_search($a['name'], $this->_checked); 124 | $bKey = array_search($b['name'], $this->_checked); 125 | if ($aKey == $bKey) { 126 | return 0; 127 | } 128 | 129 | return ($aKey > $bKey) ? 1 : -1; 130 | }); 131 | Html::addCssClass($this->itemOptions, 'btn btn-xs default'); 132 | foreach ($this->items as $item) { 133 | $checkbox = Html::checkbox($item['name'], in_array($item['name'], $this->_checked)); 134 | $items[] = Html::tag('span', $checkbox . ' ' . $item['label'], $this->itemOptions); 135 | } 136 | 137 | echo Html::beginTag('div', $this->options); 138 | echo Sortable::widget([ 139 | 'items' => $items, 140 | 'options' => ['tag' => 'div'], 141 | 'itemOptions' => ['tag' => 'span'], 142 | 'clientOptions' => ['cursor' => 'move', 143 | 'start' => new JsExpression('function(e, ui){ 144 | ui.placeholder.height(ui.item.height()); 145 | ui.placeholder.width(ui.item.width()); 146 | }'), 147 | 'update' => new JsExpression("function(e, ui){ 148 | var values = $.map($('#{$this->options['id']} input:checkbox:checked'), 149 | function(item){ return $(item).attr('name')}); 150 | $('#{$inputId}').val(values.join('{$this->separator}')); 151 | }"), 152 | ], 153 | ]); 154 | echo $hiddenInput; 155 | echo Html::endTag('div'); 156 | $this->registerJs($inputId); 157 | } 158 | 159 | protected function registerJs($inputId) 160 | { 161 | $this->view->registerJs(" 162 | ;(function($){ 163 | $('#{$this->options['id']}').on('change', 'input:checkbox', function(){ 164 | var values = $.map($('#{$this->options['id']} input:checkbox:checked'), 165 | function(item){ return $(item).attr('name')}); 166 | $('#{$inputId}').val(values.join('{$this->separator}')); 167 | }); 168 | })(jQuery); 169 | ", View::POS_READY); 170 | } 171 | } -------------------------------------------------------------------------------- /widgets/DatePicker.php: -------------------------------------------------------------------------------- 1 | 'ru', 22 | * 'model' => $model, 23 | * 'attribute' => 'country', 24 | * 'clientOptions' => [ 25 | * 'dateFormat' => 'yy-mm-dd', 26 | * ], 27 | * ]); 28 | * ``` 29 | * 30 | * The following example will use the name property instead: 31 | * 32 | * ```php 33 | * echo DatePicker::widget([ 34 | * 'language' => 'ru', 35 | * 'name' => 'country', 36 | * 'clientOptions' => [ 37 | * 'dateFormat' => 'yy-mm-dd', 38 | * ], 39 | * ]); 40 | *``` 41 | * 42 | * @see http://api.jqueryui.com/datepicker/ 43 | * @author Alexander Kochetov 44 | * @since 2.0 45 | */ 46 | class DatePicker extends InputWidget 47 | { 48 | /** 49 | * @var string the locale ID (eg 'fr', 'de') for the language to be used by the date picker. 50 | * If this property set to false, I18N will not be involved. That is, the date picker will show in English. 51 | */ 52 | public $language = false; 53 | /** 54 | * @var boolean If true, shows the widget as an inline calendar and the input as a hidden field. 55 | */ 56 | public $inline = false; 57 | /** 58 | * @var array the HTML attributes for the container tag. This is only used when [[inline]] is true. 59 | */ 60 | public $containerOptions = []; 61 | 62 | /** 63 | * Initializes the widget. 64 | */ 65 | public function init() 66 | { 67 | parent::init(); 68 | if ($this->inline && !isset($this->containerOptions['id'])) { 69 | $this->containerOptions['id'] = $this->options['id'] . '-container'; 70 | } else { 71 | Html::addCssClass($this->options, 'form-control form-control-inline'); 72 | } 73 | } 74 | 75 | /** 76 | * Renders the widget. 77 | */ 78 | public function run() 79 | { 80 | $contents = []; 81 | if($this->inline) { 82 | if ($this->hasModel()) { 83 | $contents[] = Html::activeHiddenInput($this->model, $this->attribute, $this->options); 84 | 85 | } else { 86 | $contents[] = Html::hiddenInput($this->name, $this->value, $this->options); 87 | } 88 | $contents[] = Html::tag('div', '', $this->containerOptions); 89 | } else { 90 | if ($this->hasModel()) { 91 | $contents[] = Html::activeTextInput($this->model, $this->attribute, $this->options); 92 | } else { 93 | $contents[] = Html::textInput($this->name, $this->value, $this->options); 94 | } 95 | } 96 | echo implode("\n", $contents); 97 | if($this->language) { 98 | DatePickerAsset::$extraJs[] = 'plugins/bootstrap-datepicker-extended/js/locales/bootstrap-datepicker.' . $this->language . '.js'; 99 | $this->clientOptions['language'] = $this->language; 100 | } 101 | DatePickerAsset::register($this->view); 102 | $this->registerPlugin('datepicker'); 103 | if ($this->inline) { 104 | $this->view->registerJs(" 105 | !(function($){ 106 | var el = $('#{$this->options['id']}'), 107 | val = el.val(), 108 | container = $('#{$this->containerOptions['id']}'); 109 | container.on('changeDate', function(e){ 110 | el.val(e.format()); 111 | }); 112 | if(val) { 113 | container.datepicker('update', new Date(Date.parse(val))); 114 | } 115 | })(jQuery); 116 | ", View::POS_READY); 117 | } 118 | } 119 | 120 | /** 121 | * Registers a specific Bootstrap plugin and the related events 122 | * @param string $name the name of the Bootstrap plugin 123 | */ 124 | protected function registerPlugin($name) 125 | { 126 | $view = $this->getView(); 127 | $id = $this->inline ? $this->containerOptions['id'] : $this->options['id']; 128 | if ($this->clientOptions !== false) { 129 | $options = empty($this->clientOptions) ? '' : Json::encode($this->clientOptions); 130 | $js = "jQuery('#$id').$name($options);"; 131 | $view->registerJs($js); 132 | } 133 | if (!empty($this->clientEvents)) { 134 | $js = []; 135 | foreach ($this->clientEvents as $event => $handler) { 136 | $js[] = "jQuery('#$id').on('$event', $handler);"; 137 | } 138 | $view->registerJs(implode("\n", $js)); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /widgets/DatePickerAsset.php: -------------------------------------------------------------------------------- 1 | js = array_merge($this->js, static::$extraJs); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /widgets/DateRangePicker.php: -------------------------------------------------------------------------------- 1 | DateRangePicker::MODE_ADVANCE, 26 | * 'labelDateFormat' => 'MMMM D, YYYY', 27 | * 'type' => DateRangePicker::TYPE_BLUE, 28 | * 'clientOptions' => [ 29 | * 'format' => 'YYYY-MM-DD', 30 | * 'ranges' => new \yii\web\JsExpression("{ 31 | * 'Today': [moment(), moment()], 32 | * 'Yesterday': [moment().subtract('days', 1), moment().subtract('days', 1)], 33 | * 'Last 7 Days': [moment().subtract('days', 6), moment()], 34 | * 'Last 30 Days': [moment().subtract('days', 29), moment()], 35 | * 'This Month': [moment().startOf('month'), moment().endOf('month')], 36 | * 'Last Month': [moment().subtract('month', 1).startOf('month'), moment().subtract('month', 1).endOf('month')] 37 | * }"), 38 | * ], 39 | * 'name' => 'date', 40 | * 'icon' => 'fa fa-calendar', 41 | * 'value' => '2014-02-15 - 2014-02-18', 42 | * ]); 43 | * ``` 44 | * @see https://github.com/dangrossman/bootstrap-daterangepicker 45 | */ 46 | class DateRangePicker extends InputWidget 47 | { 48 | // mode 49 | const MODE_INPUT = 'input'; 50 | const MODE_ADVANCE = 'advance'; 51 | // type 52 | const TYPE_DEFAULT = 'default'; 53 | const TYPE_RED = 'red'; 54 | const TYPE_BLUE = 'blue'; 55 | const TYPE_GREEN = 'green'; 56 | const TYPE_YELLOW = 'yellow'; 57 | const TYPE_PURPLE = 'purple'; 58 | const TYPE_DARK = 'dark'; 59 | /** 60 | * @var string dateRangePicker icon. 61 | */ 62 | public $icon; 63 | /** 64 | * @var string date separator. 65 | */ 66 | public $separator = ' - '; 67 | /** 68 | * @var string dateRangePicker format is displayed on the span HTML element in case mode 'advance'. 69 | * Using the format from JS dateRangePicker formats. 70 | */ 71 | public $labelDateFormat; 72 | /** 73 | * @var string dateRangePicker mode. 74 | * Valid values are 'input', 'advance'. 75 | * When it is set 'input' then it will be shown input element. 76 | */ 77 | public $mode = self::MODE_ADVANCE; 78 | /** 79 | * @var string dateRangePicker type. 80 | * Valid values are 'default', 'red', 'blue', 'green', 'yellow', 'purple', 'dark' 81 | * Type determines what color will have widget. 82 | */ 83 | public $type = self::TYPE_DEFAULT; 84 | /** 85 | * Executes the widget. 86 | */ 87 | public function run() 88 | { 89 | if (empty($this->clientOptions['format'])) { 90 | $this->clientOptions['format'] = 'MM/DD/YYYY'; 91 | } 92 | 93 | if (empty($this->labelDateFormat)) { 94 | $this->labelDateFormat = $this->clientOptions['format']; 95 | } 96 | 97 | if ($this->hasModel()) { 98 | $hiddenInput = Html::activeHiddenInput($this->model, $this->attribute); 99 | $input = Html::activeTextInput($this->model, $this->attribute); 100 | $name = Html::getInputName($this->model, $this->attribute); 101 | $value = $this->model->{$this->attribute}; 102 | $this->options['id'] .= '-picker'; 103 | } else { 104 | $hiddenInput = Html::hiddenInput($this->name, $this->value); 105 | $input = Html::textInput($this->name, $this->value, ['class' => 'form-control']); 106 | $name = $this->name; 107 | $value = $this->value; 108 | } 109 | $arrValue = array_map('trim', ($value = explode($this->separator, $value)) ? $value : []); 110 | $callback = ''; 111 | $initJS = ''; 112 | $lines = []; 113 | switch ($this->mode) { 114 | case self::MODE_ADVANCE: 115 | Html::addCssClass($this->options, 'btn ' . $this->type); 116 | $lines[] = $hiddenInput; 117 | $lines[] = Html::beginTag('div', $this->options); 118 | if (!empty($this->icon)) { 119 | $lines[] = Html::tag('i', '', ['class' => $this->icon]) . ' '; 120 | } 121 | $lines[] = Html::tag('span', ' '); 122 | $lines[] = Html::tag('b', '', ['class' => 'fa fa-angle-down']); 123 | $lines[] = Html::endTag('div'); 124 | $callback = "function (start, end) { 125 | $('#{$this->options['id']} span') 126 | .html(start.format('{$this->labelDateFormat}') 127 | + '{$this->separator}' + end.format('{$this->labelDateFormat} ')); 128 | // set value to hidden input 129 | $('input[name=\"{$name}\"]').val(start.format('{$this->clientOptions['format']}') 130 | + '{$this->separator}' + end.format('{$this->clientOptions['format']}')); 131 | }"; 132 | if(count($arrValue) == 2) { 133 | $initJS = (" 134 | !(function($){ 135 | var el = $('#{$this->options['id']}'); 136 | el.data('daterangepicker') 137 | .setStartDate(moment('{$arrValue[0]}', '{$this->clientOptions['format']}')); 138 | el.data('daterangepicker') 139 | .setEndDate(moment('{$arrValue[1]}', '{$this->clientOptions['format']}')); 140 | el.find('span') 141 | .html(moment('{$arrValue[0]}', '{$this->clientOptions['format']}').format('{$this->labelDateFormat}') 142 | + '{$this->separator}' 143 | + moment('{$arrValue[1]}', '{$this->clientOptions['format']}').format('{$this->labelDateFormat} ')); 144 | })(jQuery); 145 | "); 146 | } 147 | break; 148 | case self::MODE_INPUT: 149 | Html::addCssClass($this->options, 'input-group'); 150 | $lines[] = Html::beginTag('div', $this->options); 151 | $lines[] = $input; 152 | $lines[] = Html::beginTag('span', ['class' => 'input-group-btn']); 153 | $lines[] = Button::widget( 154 | [ 155 | 'label' => ' ', 156 | 'icon' => $this->icon, 157 | 'type' => $this->type, 158 | 'iconPosition' => Button::ICON_POSITION_RIGHT 159 | ] 160 | ); 161 | $lines[] = Html::endTag('span'); 162 | $lines[] = Html::endTag('div'); 163 | $callback = "function (start, end) { 164 | $('#{$this->options['id']} input') 165 | .val(start.format('{$this->clientOptions['format']}') 166 | + '{$this->separator}' + end.format('{$this->clientOptions['format']} ')); 167 | // set value to hidden input 168 | $('input[name=\"{$name}\"]').val(start.format('{$this->clientOptions['format']}') 169 | + '{$this->separator}' + end.format('{$this->clientOptions['format']}')); 170 | }"; 171 | if(count($arrValue) == 2) { 172 | $initJS = (" 173 | !(function($){ 174 | var el = $('#{$this->options['id']}'); 175 | el.data('daterangepicker') 176 | .setStartDate(moment('{$arrValue[0]}', '{$this->clientOptions['format']}')); 177 | el.data('daterangepicker') 178 | .setEndDate(moment('{$arrValue[1]}', '{$this->clientOptions['format']}')); 179 | el.find('input') 180 | .val(moment('{$arrValue[0]}', '{$this->clientOptions['format']}').format('{$this->clientOptions['format']}') 181 | + '{$this->separator}' 182 | + moment('{$arrValue[1]}', '{$this->clientOptions['format']}').format('{$this->clientOptions['format']} ')); 183 | })(jQuery); 184 | "); 185 | } 186 | break; 187 | } 188 | echo implode("\n", $lines); 189 | DateRangePickerAsset::register($this->view); 190 | $this->registerPlugin('daterangepicker', $callback); 191 | if (!empty($initJS)) { 192 | $this->view->registerJs($initJS, View::POS_READY); 193 | } 194 | } 195 | 196 | /** 197 | * Registers a specific Bootstrap plugin and the related events 198 | * @param string $name the name of the Bootstrap plugin 199 | * @param string $callback second parameter option of the Bootstrap plugin 200 | */ 201 | protected function registerPlugin($name, $callback) { 202 | $view = $this->getView(); 203 | $id = $this->options['id']; 204 | if ($this->clientOptions !== false) { 205 | $options = empty($this->clientOptions) ? '' : Json::encode($this->clientOptions); 206 | if (!empty($callback)) { 207 | $js = "jQuery('#$id').$name($options, $callback);"; 208 | } else { 209 | $js = "jQuery('#$id').$name($options);"; 210 | } 211 | $view->registerJs($js); 212 | } 213 | if (!empty($this->clientEvents)) { 214 | $js = []; 215 | foreach ($this->clientEvents as $event => $handler) { 216 | $js[] = "jQuery('#$id').on('$event', $handler);"; 217 | } 218 | $view->registerJs(implode("\n", $js)); 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /widgets/DateRangePickerAsset.php: -------------------------------------------------------------------------------- 1 | $model, 26 | * 'attributes' => [ 27 | * 'title', // title attribute (in plain text) 28 | * 'description:html', // description attribute in HTML 29 | * [ // the owner name of the model 30 | * 'label' => 'Owner', 31 | * 'value' => $model->owner->name, 32 | * ], 33 | * ], 34 | * ]); 35 | * ~~~ 36 | */ 37 | class DetailView extends \yii\widgets\DetailView 38 | { 39 | /** 40 | * @var string|callback the template used to render a single attribute. If a string, the token `{label}` 41 | * and `{value}` will be replaced with the label and the value of the corresponding attribute. 42 | * If a callback (e.g. an anonymous function), the signature must be as follows: 43 | * 44 | * ~~~ 45 | * function ($attribute, $index, $widget) 46 | * ~~~ 47 | * 48 | * where `$attribute` refer to the specification of the attribute being rendered, `$index` is the zero-based 49 | * index of the attribute in the [[attributes]] array, and `$widget` refers to this widget instance. 50 | */ 51 | public $template = '
    52 |
    {label}
    53 |
    {value}
    54 |
    '; 55 | } 56 | -------------------------------------------------------------------------------- /widgets/Dropdown.php: -------------------------------------------------------------------------------- 1 | 'Dropdown title', 21 | * 'more' => ['label' => 'xxx', 'url' => '/', 'icon' => 'm-icon-swapright'], 22 | * 'scroller' => ['height' => 200], 23 | * 'items' => [ 24 | * ['label' => 'Level 1 - Dropdown A', 'url' => '#'], 25 | * '
  • ', 26 | * '', 27 | * ['label' => 'Level 1 - Dropdown B', 'url' => '#'], 28 | * ], 29 | * ]); 30 | * 31 | */ 32 | class Dropdown extends \yii\bootstrap\Dropdown 33 | { 34 | /** 35 | * @var string the dropdown title 36 | */ 37 | public $title; 38 | /** 39 | * @var array the dropdown last item options 40 | * with the following structure: 41 | * ```php 42 | * [ 43 | * // optional, item label 44 | * 'label' => 'Show all messages', 45 | * // optional, item icon 46 | * 'icon' => 'm-icon-swapright', 47 | * // optional, item url 48 | * 'url' => '/', 49 | * ] 50 | * ``` 51 | */ 52 | public $more = []; 53 | /** 54 | * @var array the dropdown item options 55 | * is an array of the following structure: 56 | * ```php 57 | * [ 58 | * // required, height of the body portlet as a px 59 | * 'height' => 150, 60 | * // optional, HTML attributes of the scroller 61 | * 'options' => [], 62 | * // optional, footer of the scroller. May contain string or array(the options of Link component) 63 | * 'footer' => [ 64 | * 'label' => 'Show all', 65 | * ], 66 | * ] 67 | * ``` 68 | */ 69 | public $scroller = []; 70 | 71 | /** 72 | * Executes the widget. 73 | */ 74 | public function run() 75 | { 76 | echo $this->renderItems($this->items); 77 | } 78 | 79 | /** 80 | * Renders menu items. 81 | * @param array $items the menu items to be rendered 82 | * @return string the rendering result. 83 | * @throws InvalidConfigException if the label option is not specified in one of the items. 84 | */ 85 | protected function renderItems($items, $options = []) 86 | { 87 | $lines = []; 88 | if ($this->title) { 89 | $lines[] = Html::tag('li', Html::tag('p', $this->title)); 90 | } 91 | 92 | if (!empty($this->scroller)) { 93 | if (!isset($this->scroller['height'])) { 94 | throw new InvalidConfigException("The 'height' option of Scroller is required."); 95 | } 96 | $lines[] = Html::beginTag('li'); 97 | $lines[] = Html::beginTag( 98 | 'ul', 99 | [ 100 | 'style' => 'height: ' . $this->scroller['height'] . 'px;', 101 | 'class' => 'dropdown-menu-list scroller' 102 | ] 103 | ); 104 | } 105 | 106 | foreach ($items as $i => $item) { 107 | if (isset($item['visible']) && !$item['visible']) { 108 | unset($items[$i]); 109 | continue; 110 | } 111 | if (is_string($item)) { 112 | $lines[] = $item; 113 | continue; 114 | } 115 | 116 | if (in_array('divider', $item)) { 117 | $lines[] = Html::tag('li', '', ['class' => 'divider']); 118 | continue; 119 | } 120 | 121 | if (!isset($item['label'])) { 122 | throw new InvalidConfigException("The 'label' option is required."); 123 | } 124 | $label = $this->encodeLabels ? Html::encode($item['label']) : $item['label']; 125 | 126 | $icon = ArrayHelper::getValue($item, 'icon', null); 127 | if ($icon) { 128 | $label = Html::tag('i', '', ['alt' => $label, 'class' => $icon]) . ' ' . $label; 129 | } 130 | $label .= ArrayHelper::getValue($item, 'badge', ''); 131 | $options = ArrayHelper::getValue($item, 'options', []); 132 | $linkOptions = ArrayHelper::getValue($item, 'linkOptions', []); 133 | $linkOptions['tabindex'] = '-1'; 134 | $content = Html::a($label, ArrayHelper::getValue($item, 'url', '#'), $linkOptions); 135 | $lines[] = Html::tag('li', $content, $options); 136 | } 137 | 138 | if (!empty($this->scroller)) { 139 | $lines[] = Html::endTag('ul'); 140 | $lines[] = Html::endTag('li'); 141 | } 142 | 143 | if (!empty($this->more)) { 144 | $url = ArrayHelper::getValue($this->more, 'url', '#'); 145 | $text = ArrayHelper::getValue($this->more, 'label', ''); 146 | $icon = ArrayHelper::getValue($this->more, 'icon', ''); 147 | if ($icon) { 148 | $icon = Html::tag('i', '', ['class' => $icon]); 149 | } 150 | $lines[] = Html::tag('li', Html::tag('a', $text . $icon, ['href' => $url]), ['class' => 'external']); 151 | } 152 | 153 | return Html::tag('ul', implode("\n", $lines), $this->options); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /widgets/DropdownContent.php: -------------------------------------------------------------------------------- 1 | 'Action', 21 | * 'dropdown' => 'Content', 22 | * ]); 23 | * ``` 24 | */ 25 | class DropdownContent extends ButtonDropdown 26 | { 27 | /** 28 | * @var string the button label 29 | */ 30 | public $label = 'Button'; 31 | /** 32 | * @var array the HTML attributes of the button. 33 | * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. 34 | */ 35 | public $options = []; 36 | /** 37 | * @var string the dropdown content. 38 | */ 39 | public $dropdown = ''; 40 | 41 | /** 42 | * Executes the widget. 43 | */ 44 | public function run() 45 | { 46 | echo Html::beginTag('div', ['class' => 'btn-group']); 47 | echo $this->renderButton() . "\n" . $this->renderDropdown(); 48 | echo Html::endTag('div'); 49 | 50 | $this->registerPlugin('button'); 51 | 52 | } 53 | 54 | /** 55 | * Generates the button dropdown. 56 | * @return string the rendering result. 57 | */ 58 | protected function renderButton() 59 | { 60 | Html::addCssClass($this->options, 'btn'); 61 | $label = $this->label; 62 | if ($this->encodeLabel) { 63 | $label = Html::encode($label); 64 | } 65 | if ($this->split) { 66 | $options = $this->options; 67 | $this->options['data-toggle'] = 'dropdown'; 68 | Html::addCssClass($this->options, 'dropdown-toggle'); 69 | $splitButton = BButton::widget([ 70 | 'label' => '', 71 | 'encodeLabel' => false, 72 | 'options' => $this->options, 73 | 'view' => $this->getView(), 74 | ]); 75 | } else { 76 | $label .= ' '; 77 | $options = $this->options; 78 | if (!isset($options['href'])) { 79 | $options['href'] = '#'; 80 | } 81 | Html::addCssClass($options, 'dropdown-toggle'); 82 | $options['data-toggle'] = 'dropdown'; 83 | $splitButton = ''; 84 | } 85 | 86 | return BButton::widget([ 87 | 'tagName' => $this->tagName, 88 | 'label' => $label, 89 | 'options' => $options, 90 | 'encodeLabel' => false, 91 | 'view' => $this->getView(), 92 | ]) . "\n" . $splitButton; 93 | } 94 | 95 | /** 96 | * Gets dropdown content. 97 | * @return string the rendering result. 98 | */ 99 | protected function renderDropdown() 100 | { 101 | return Html::tag('div', $this->dropdown, ['class' => 'dropdown-menu']); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /widgets/GridView.php: -------------------------------------------------------------------------------- 1 | 'table table-striped table-bordered table-hover dataTable']; 26 | /** 27 | * @var array the HTML attributes for the table header row 28 | */ 29 | public $headerRowOptions = ['class' => 'heading']; 30 | 31 | public function init() 32 | { 33 | parent::init(); 34 | $this->initVisible(); 35 | $this->initSortable(); 36 | } 37 | 38 | /** 39 | * Renders the data models for the grid view. 40 | */ 41 | public function renderItems() 42 | { 43 | $content = array_filter([ 44 | $this->renderCaption(), 45 | $this->renderColumnGroup(), 46 | $this->showHeader ? $this->renderTableHeader() : false, 47 | $this->showFooter ? $this->renderTableFooter() : false, 48 | $this->renderTableBody(), 49 | ]); 50 | $table = Html::tag('table', implode("\n", $content), $this->tableOptions); 51 | if ($this->responsive) { 52 | $table = Html::tag('div', $table, ['class' => 'table-responsive']); 53 | } 54 | 55 | return $table; 56 | } 57 | 58 | protected function initVisible() 59 | { 60 | $columns = $this->getStorageColumns(); 61 | if (empty($columns)) { 62 | return; 63 | } 64 | foreach ($this->columns as $i => $column) { 65 | if (array_search($i, $columns) === false) { 66 | unset($this->columns[$i]); 67 | } 68 | } 69 | } 70 | 71 | protected function initSortable() 72 | { 73 | $positions = $this->getStorageColumns(); 74 | if (empty($positions)) { 75 | return; 76 | } 77 | uksort( 78 | $this->columns, 79 | function ($a, $b) use ($positions) { 80 | $aIndex = array_search($a, $positions); 81 | $bIndex = array_search($b, $positions); 82 | if ($aIndex == $bIndex) { 83 | return 0; 84 | } 85 | 86 | return ($aIndex < $bIndex) ? -1 : 1; 87 | } 88 | ); 89 | } 90 | 91 | protected function getStorageColumns() 92 | { 93 | return []; 94 | } 95 | } -------------------------------------------------------------------------------- /widgets/HorizontalMenu.php: -------------------------------------------------------------------------------- 1 | [ 38 | * // Important: you need to specify url as 'controller/action', 39 | * // not just as 'controller' even if default action is used. 40 | * ['label' => 'Home', 'url' => ['site/index']], 41 | * // 'Products' menu item will be selected as long as the route is 'product/index' 42 | * ['label' => 'Products', 'url' => ['product/index'], 'items' => [ 43 | * ['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']], 44 | * ['label' => 'Most Popular', 'url' => ['product/index', 'tag' => 'popular']], 45 | * ]], 46 | * ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest], 47 | * ], 48 | * 'search' => [ 49 | * // required, whether search box is visible. Defaults to 'true'. 50 | * 'visible' => true, 51 | * // optional, the configuration array for [[ActiveForm]]. 52 | * 'form' => [], 53 | * // optional, input options with default values 54 | * 'input' => [ 55 | * 'name' => 'search', 56 | * 'value' => '', 57 | * 'options' => [ 58 | * 'placeholder' => 'Search...', 59 | * ] 60 | * ], 61 | * ]); 62 | * 63 | * // Mega Menu without search box 64 | * echo HorizontalMenu::widget([ 65 | * 'items' => [ 66 | * ['label' => 'Home', 'url' => ['site/index']], 67 | * [ 68 | * 'label' => 'Mega Menu', 69 | * 'type' => HorizontalMenu::ITEM_TYPE_FULL_MEGA, 70 | * //optional, HTML text for last column 71 | * 'text' => 'Other HTML text', 72 | * 'items' => [ 73 | * [ 74 | * 'label' => 'Column 1', // First column title 75 | * 'items' => [ 76 | * ['label' => 'Column 1 Item 1'], 77 | * ['label' => 'Column 1 Item 2'], 78 | * ] 79 | * ], 80 | * [ 81 | * 'label' => 'Column 2', // Second column title 82 | * 'items' => [ 83 | * ['label' => 'Column 2 Item 1'], 84 | * ['label' => 'Column 2 Item 2'], 85 | * ] 86 | * ], 87 | * ] 88 | * ], 89 | * ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest], 90 | * ], 91 | * ]); 92 | * ``` 93 | * 94 | */ 95 | class HorizontalMenu extends Menu 96 | { 97 | const ITEM_TYPE_CLASSIC = 'classic-menu-dropdown'; 98 | const ITEM_TYPE_MEGA = 'mega-menu-dropdown'; 99 | const ITEM_TYPE_FULL_MEGA = 'mega-menu-dropdown mega-menu-full'; 100 | /** 101 | * @var string the template used to render the body of a menu which is a link. 102 | * In this template, the token `{url}` will be replaced with the corresponding link URL; 103 | * while `{label}` will be replaced with the link text. 104 | * This property will be overridden by the `template` option set in individual menu items via [[items]]. 105 | */ 106 | public $linkTemplate = '{icon}{label}'; 107 | /** 108 | * @var boolean whether to activate parent menu items when one of the corresponding child menu items is active. 109 | * The activated parent menu items will also have its CSS classes appended with [[activeCssClass]]. 110 | */ 111 | public $activateParents = true; 112 | /** 113 | * @var array Search options 114 | * is an array of the following structure: 115 | * ```php 116 | * [ 117 | * // required, whether search box is visible 118 | * 'visible' => true, 119 | * // optional, ActiveForm options 120 | * 'form' => [], 121 | * // optional, input options with default values 122 | * 'input' => [ 123 | * 'name' => 'search', 124 | * 'value' => '', 125 | * 'options' => [ 126 | * 'placeholder' => 'Search...', 127 | * ] 128 | * ], 129 | * ] 130 | * ``` 131 | */ 132 | public $search = ['visible' => true]; 133 | /** 134 | * @var string the template used to render a list of sub-menus. 135 | * In this template, the token `{items}` will be replaced with the renderer sub-menu items. 136 | */ 137 | public $submenuTemplate = "\n\n"; 138 | /** 139 | * @var string the template used to render the body of a dropdown which is a link. 140 | * In this template, the token `{label}` will be replaced with the link text; 141 | * the token `{url}` will be replaced with the corresponding link URL; 142 | */ 143 | public $dropdownLinkTemplate = '{label}'; 144 | /** 145 | * @var string the template used to render the body of a dropdown which is a link for the Mega Menu. 146 | * In this template, the token `{label}` will be replaced with the link text; 147 | * the token `{url}` will be replaced with the corresponding link URL; 148 | */ 149 | public $dropdownLinkMegaTemplate = '{label}'; 150 | 151 | /** 152 | * Renders the menu. 153 | */ 154 | public function run() 155 | { 156 | Html::addCssClass($this->options, 'nav navbar-nav'); 157 | echo Html::beginTag('div', ['class' => 'hor-menu hidden-sm hidden-xs']); 158 | if ($this->route === null && Yii::$app->controller !== null) { 159 | $this->route = Yii::$app->controller->getRoute(); 160 | } 161 | if ($this->params === null) { 162 | $this->params = $_GET; 163 | } 164 | $items = $this->normalizeItems($this->items, $hasActiveChild); 165 | $options = $this->options; 166 | $tag = ArrayHelper::remove($options, 'tag', 'ul'); 167 | $data = [$this->renderItems($items)]; 168 | if (!isset($this->search['visible'])) { 169 | throw new InvalidConfigException("The 'visible' option of search is required."); 170 | } 171 | $data[] = Html::tag('li', $this->renderSearch()); 172 | echo Html::tag($tag, implode("\n", $data), $options); 173 | echo Html::endTag('div'); 174 | } 175 | 176 | /** 177 | * Recursively renders the menu items (without the container tag). 178 | * @param array $items the menu items to be rendered recursively 179 | * @param integer $level Indicates the level of the menu items 180 | * @param integer $type Item type. Valid values are: 181 | * HorizontalMenu::ITEM_TYPE_CLASSIC, 182 | * HorizontalMenu::ITEM_TYPE_MEGA, 183 | * HorizontalMenu::ITEM_TYPE_FULL_MEGA 184 | * @return string the rendering result 185 | */ 186 | protected function renderItems($items, $level = 1, $type = null) 187 | { 188 | $n = count($items); 189 | $lines = []; 190 | foreach ($items as $i => $item) { 191 | $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', [])); 192 | $tag = ArrayHelper::remove($options, 'tag', 'li'); 193 | $itemType = ($type === null) ? ArrayHelper::getValue($item, 'type', self::ITEM_TYPE_CLASSIC) : $type; 194 | $class = []; 195 | if ($item['active']) { 196 | $class[] = $this->activeCssClass; 197 | } 198 | if ($i === 0 && $this->firstItemCssClass !== null) { 199 | $class[] = $this->firstItemCssClass; 200 | } 201 | if ($i === $n - 1 && $this->lastItemCssClass !== null) { 202 | $class[] = $this->lastItemCssClass; 203 | } 204 | if (!empty($class)) { 205 | if (empty($options['class'])) { 206 | $options['class'] = implode(' ', $class); 207 | } else { 208 | $options['class'] .= ' ' . implode(' ', $class); 209 | } 210 | } 211 | if ($level == 1) { 212 | Html::addCssClass($options, $itemType); 213 | $item['template'] = ($itemType == self::ITEM_TYPE_CLASSIC) 214 | ? $this->dropdownLinkTemplate : $this->dropdownLinkMegaTemplate; 215 | if ($item['active']) { 216 | $item['label'] = Html::tag('span', '', ['class' => 'selected']) . $item['label']; 217 | } 218 | if (!empty($item['items'])) { 219 | $item['label'] .= ' ' . Html::tag('i', '',['class' => 'fa fa-angle-down']); 220 | } 221 | } else { 222 | if (!empty($item['items'])) { 223 | Html::addCssClass($options, 'dropdown-submenu'); 224 | } 225 | } 226 | if ($itemType == self::ITEM_TYPE_CLASSIC) { 227 | $menu = $this->renderItem($item); 228 | if (!empty($item['items'])) { 229 | $menu .= strtr($this->submenuTemplate, [ 230 | '{items}' => $this->renderItems($item['items'], $level + 1, $itemType), 231 | '{class}' => 'dropdown-menu', 232 | ]); 233 | } 234 | } else { 235 | if ($level == 1) { 236 | $menu = $this->renderItem($item); 237 | if (!empty($item['items'])) { 238 | $submenu = $this->renderItems($item['items'], $level + 1, $itemType); 239 | if ($itemType == self::ITEM_TYPE_FULL_MEGA) { 240 | $text = ArrayHelper::getValue($item, 'text', ''); 241 | $submenu = Html::tag('div', $submenu, ['class' => 'col-md-7']); 242 | $submenu .= Html::tag('div', $text, ['class' => 'col-md-5']); 243 | } 244 | $submenu = Html::tag('div', $submenu, ['class' => 'row']); 245 | $submenu = Html::tag('div', $submenu, ['class' => 'mega-menu-content']); 246 | $submenu = Html::tag('li', $submenu); 247 | $menu .= strtr($this->submenuTemplate, [ 248 | '{items}' => $submenu, 249 | '{class}' => 'dropdown-menu', 250 | ]); 251 | } 252 | } else { 253 | if (!empty($item['items'])) { 254 | $headerItem = $item; 255 | unset($headerItem['items']); 256 | $headerItem['template'] = '

    {label}

    '; 257 | array_unshift($item['items'], $headerItem); 258 | $lines[] = strtr($this->submenuTemplate, [ 259 | '{items}' => $this->renderItems($item['items'], $level + 1, $itemType), 260 | '{class}' => 'col-md-4 mega-menu-submenu', 261 | ]); 262 | continue; 263 | } else { 264 | $item['icon'] = 'fa fa-angle-right'; 265 | $menu = $this->renderItem($item); 266 | } 267 | } 268 | } 269 | $lines[] = Html::tag($tag, $menu, $options); 270 | } 271 | 272 | return implode("\n", $lines); 273 | } 274 | 275 | /** 276 | * Renders the content of a menu item. 277 | * Note that the container and the sub-menus are not rendered here. 278 | * @param array $item the menu item to be rendered. Please refer to [[items]] to see what data might be in the item. 279 | * @return string the rendering result 280 | */ 281 | protected function renderItem($item) 282 | { 283 | $item['url'] = ArrayHelper::getValue($item, 'url', '#'); 284 | $item['label'] = ArrayHelper::getValue($item, 'badge', '') . $item['label']; 285 | $item['icon'] = ArrayHelper::getValue($item, 'icon', ''); 286 | if ($item['icon']) { 287 | $item['icon'] = Html::tag('i', '', ['class' => $item['icon']]); 288 | } 289 | $template = ArrayHelper::getValue($item, 'template', $this->linkTemplate); 290 | 291 | return strtr($template, [ 292 | '{url}' => Url::toRoute($item['url']), 293 | '{label}' => $item['label'], 294 | '{icon}' => $item['icon'], 295 | ]); 296 | } 297 | 298 | /** 299 | * Renders search box 300 | * @return string the rendering result 301 | */ 302 | protected function renderSearch() 303 | { 304 | $defaultFormOptions = ['options' => ['class' => 'sidebar-search']]; 305 | $defaultInputOptions = [ 306 | 'name' => 'search', 307 | 'value' => '', 308 | 'options' => [ 309 | 'placeholder' => 'Search...', 310 | 'class' => 'form-control', 311 | ] 312 | ]; 313 | $formOptions = ArrayHelper::merge(ArrayHelper::getValue($this->search, 'form', []), $defaultFormOptions); 314 | $inputOptions = ArrayHelper::merge(ArrayHelper::getValue($this->search, 'input', []), $defaultInputOptions); 315 | ob_start(); 316 | ob_implicit_flush(false); 317 | echo Html::tag('span', ' ', ['class' => 'hor-menu-search-form-toggler']); 318 | echo '
    '; 319 | CoreActiveForm::begin($formOptions); 320 | echo '
    '; 321 | echo Html::input('text', $inputOptions['name'], $inputOptions['value'], $inputOptions['options']); 322 | echo '
    '; 323 | echo ''; 324 | echo '
    '; // end .input-group-btn 325 | echo '
    '; // end .input-group 326 | echo '
    '; // end .search-form 327 | CoreActiveForm::end(); 328 | 329 | return ob_get_clean(); 330 | } 331 | } -------------------------------------------------------------------------------- /widgets/InputWidget.php: -------------------------------------------------------------------------------- 1 | hasModel() && $this->name === null) { 42 | throw new InvalidConfigException("Either 'name', or 'model' and 'attribute' properties must be specified."); 43 | } 44 | if (!isset($this->options['id'])) { 45 | $this->options['id'] = $this->hasModel() ? Html::getInputId($this->model, $this->attribute) : $this->getId(); 46 | } 47 | parent::init(); 48 | } 49 | 50 | /** 51 | * @return boolean whether this widget is associated with a data model. 52 | */ 53 | protected function hasModel() 54 | { 55 | return $this->model instanceof Model && $this->attribute !== null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /widgets/IonRangeSlider.php: -------------------------------------------------------------------------------- 1 | 'ionRangeSlider', 21 | * 'clientOptions' => [ 22 | * 'min' => 0, 23 | * 'max' => 5000, 24 | * 'from' => 1000, // default value 25 | * 'to' => 4000, // default value 26 | * 'type' => 'double', 27 | * 'step' => 1, 28 | * 'prefix' => "$", 29 | * 'prettify' => false, 30 | * 'hasGrid' => true 31 | * ], 32 | * ]); 33 | * ``` 34 | * @see https://github.com/IonDen/ion.rangeSlider 35 | */ 36 | class IonRangeSlider extends InputWidget 37 | { 38 | /** 39 | * @var string separator values 40 | */ 41 | public $separator = ';'; 42 | 43 | /** 44 | * Executes the widget. 45 | */ 46 | public function run() 47 | { 48 | if ($this->hasModel()) { 49 | $values = explode($this->separator, $this->model->{$this->attribute}); 50 | if (count($values) == 2) { 51 | $this->clientOptions['from'] = (int)$values[0]; 52 | $this->clientOptions['to'] = (int)$values[1]; 53 | } 54 | echo Html::activeTextInput($this->model, $this->attribute, $this->options); 55 | } else { 56 | $values = explode($this->separator, $this->value); 57 | if (count($values) == 2) { 58 | $this->clientOptions['from'] = (int)$values[0]; 59 | $this->clientOptions['to'] = (int)$values[1]; 60 | } 61 | echo Html::textInput($this->name, $this->value, $this->options); 62 | } 63 | IonRangeSliderAsset::register($this->view); 64 | $this->registerPlugin('ionRangeSlider'); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /widgets/IonRangeSliderAsset.php: -------------------------------------------------------------------------------- 1 | 'Link', 20 | * 'url' => 'http://yii2metronic.icron.org/', 21 | * 'icon' => 'm-icon-swapright m-icon-gray', 22 | * 'iconPosition' => Link::ICON_POSITION_LEFT, 23 | * ]); 24 | * ``` 25 | */ 26 | class Link extends Widget 27 | { 28 | // Icon position 29 | const ICON_POSITION_LEFT = 'left'; 30 | const ICON_POSITION_RIGHT = 'right'; 31 | 32 | /** 33 | * @var string The button label 34 | */ 35 | public $label; 36 | /** 37 | * @var bool Whether the label should be HTML-encoded 38 | */ 39 | public $encodeLabel = true; 40 | /** 41 | * @var string The link url 42 | */ 43 | public $url = '#'; 44 | /** 45 | * @var string The button icon 46 | */ 47 | public $icon = 'm-icon-swapright m-icon-gray'; 48 | /** 49 | * @var string Icon position 50 | * Valid values are 'left', 'right' 51 | */ 52 | public $iconPosition = self::ICON_POSITION_RIGHT; 53 | 54 | /** 55 | * Initializes the widget. 56 | * @throws InvalidConfigException 57 | */ 58 | public function init() 59 | { 60 | if ($this->label === null) { 61 | throw new InvalidConfigException("The 'label' option is required."); 62 | } 63 | 64 | if ($this->url === null) { 65 | $this->url = '#'; 66 | } 67 | } 68 | 69 | /** 70 | * Renders the widget. 71 | */ 72 | public function run() 73 | { 74 | $icon = ($this->icon === null) ? '' : Html::tag('i', '', ['class' => $this->icon]); 75 | if (strcasecmp($this->iconPosition, self::ICON_POSITION_LEFT) === 0) { 76 | $label = $icon . ' ' . Html::encode($this->label); 77 | } else { 78 | $label = Html::encode($this->label) . ' ' . $icon; 79 | } 80 | echo Html::a($label, $this->url, $this->options); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /widgets/Menu.php: -------------------------------------------------------------------------------- 1 | [ 34 | * // Important: you need to specify url as 'controller/action', 35 | * // not just as 'controller' even if default action is used. 36 | * [ 37 | * 'icon' => '', 38 | * 'label' => 'Home', 39 | * 'url' => ['site/index'] 40 | * ], 41 | * // 'Products' menu item will be selected as long as the route is 'product/index' 42 | * ['label' => 'Products', 'url' => ['product/index'], 'items' => [ 43 | * ['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']], 44 | * ['label' => 'Most Popular', 'url' => ['product/index', 'tag' => 'popular']], 45 | * ]], 46 | * ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest], 47 | * ], 48 | * 'search' => [ 49 | * // required, whether search box is visible. Defaults to 'true'. 50 | * 'visible' => true, 51 | * // optional, the configuration array for [[ActiveForm]]. 52 | * 'form' => [], 53 | * // optional, input options with default values 54 | * 'input' => [ 55 | * 'name' => 'search', 56 | * 'value' => '', 57 | * 'options' => [ 58 | * 'placeholder' => 'Search...', 59 | * ] 60 | * ], 61 | * ] 62 | * ]); 63 | * ``` 64 | * 65 | */ 66 | class Menu extends \yii\widgets\Menu 67 | { 68 | /** 69 | * @var boolean whether to activate parent menu items when one of the corresponding child menu items is active. 70 | * The activated parent menu items will also have its CSS classes appended with [[activeCssClass]]. 71 | */ 72 | public $activateParents = true; 73 | /** 74 | * @var string the CSS class that will be assigned to the first item in the main menu or each submenu. 75 | */ 76 | public $firstItemCssClass = 'start'; 77 | /** 78 | * @var string the CSS class that will be assigned to the last item in the main menu or each submenu. 79 | */ 80 | public $lastItemCssClass = 'last'; 81 | /** 82 | * @var string the template used to render a list of sub-menus. 83 | * In this template, the token `{items}` will be replaced with the renderer sub-menu items. 84 | */ 85 | public $submenuTemplate = "\n\n"; 86 | /** 87 | * @var string the template used to render the body of a menu which is a link. 88 | * In this template, the token `{url}` will be replaced with the corresponding link URL; 89 | * while `{label}` will be replaced with the link text. 90 | * The token `{icon}` will be replaced with the corresponding link icon. 91 | * The token `{arrow}` will be replaced with the corresponding link arrow. 92 | * This property will be overridden by the `template` option set in individual menu items via [[items]]. 93 | */ 94 | public $linkTemplate = '{icon}{label}{arrow}{badge}'; 95 | /** 96 | * @var array Search options 97 | * is an array of the following structure: 98 | * ```php 99 | * [ 100 | * // required, whether search box is visible 101 | * 'visible' => true, 102 | * // optional, ActiveForm options 103 | * 'form' => [], 104 | * // optional, input options with default values 105 | * 'input' => [ 106 | * 'name' => 'search', 107 | * 'value' => '', 108 | * 'options' => [ 109 | * 'placeholder' => 'Search...', 110 | * ] 111 | * ], 112 | * ] 113 | * ``` 114 | */ 115 | public $search = ['visible' => true]; 116 | /** 117 | * @var bool Indicates whether menu is visible. 118 | */ 119 | public $visible = true; 120 | 121 | /** 122 | * Initializes the widget. 123 | */ 124 | public function init() 125 | { 126 | Metronic::registerThemeAsset($this->getView()); 127 | Html::addCssClass($this->options, 'page-sidebar-menu'); 128 | if (!$this->visible || Metronic::getComponent()->layoutOption == Metronic::LAYOUT_FULL_WIDTH) { 129 | Html::addCssClass($this->options, 'visible-sm visible-xs'); 130 | } 131 | } 132 | 133 | /** 134 | * Renders the widget. 135 | */ 136 | public function run() 137 | { 138 | echo Html::beginTag('div', ['class' => 'page-sidebar navbar-collapse collapse']); 139 | if ($this->route === null && Yii::$app->controller !== null) { 140 | $this->route = Yii::$app->controller->getRoute(); 141 | } 142 | if ($this->params === null) { 143 | $this->params = $_GET; 144 | } 145 | $items = $this->normalizeItems($this->items, $hasActiveChild); 146 | $options = $this->options; 147 | $tag = ArrayHelper::remove($options, 'tag', 'ul'); 148 | $data = [Html::tag('li', Html::tag('div', '', ['class' => 'sidebar-toggler hidden-phone']))]; 149 | 150 | if (!isset($this->search['visible'])) { 151 | throw new InvalidConfigException("The 'visible' option of search is required."); 152 | } 153 | $data[] = Html::tag('li', $this->renderSearch()); 154 | $data[] = $this->renderItems($items); 155 | echo Html::tag($tag, implode("\n", $data), $options); 156 | echo Html::endTag('div'); 157 | } 158 | 159 | /** 160 | * Renders search box 161 | * @return string the rendering result 162 | */ 163 | public function renderSearch() 164 | { 165 | $defaultFormOptions = ['options' => ['class' => 'sidebar-search']]; 166 | $defaultInputOptions = ['name' => 'search', 'value' => '', 'options' => ['placeholder' => 'Search...']]; 167 | $formOptions = ArrayHelper::merge(ArrayHelper::getValue($this->search, 'form', []), $defaultFormOptions); 168 | $inputOptions = ArrayHelper::merge(ArrayHelper::getValue($this->search, 'input', []), $defaultInputOptions); 169 | ob_start(); 170 | ob_implicit_flush(false); 171 | CoreActiveForm::begin($formOptions); 172 | echo '
    '; 173 | echo '
    '; 174 | echo ''; 175 | echo Html::input('text', $inputOptions['name'], $inputOptions['value'], $inputOptions['options']); 176 | echo ''; 177 | echo '
    '; 178 | echo '
    '; 179 | CoreActiveForm::end(); 180 | 181 | return ob_get_clean(); 182 | } 183 | 184 | /** 185 | * Recursively renders the menu items (without the container tag). 186 | * @param array $items the menu items to be rendered recursively 187 | * @param integer $level the item level, starting with 1 188 | * @return string the rendering result 189 | */ 190 | protected function renderItems($items, $level = 1) 191 | { 192 | $n = count($items); 193 | $lines = []; 194 | foreach ($items as $i => $item) { 195 | $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', [])); 196 | $tag = ArrayHelper::remove($options, 'tag', 'li'); 197 | $class = []; 198 | if ($item['active']) { 199 | $class[] = $this->activeCssClass; 200 | } 201 | if ($i === 0 && $this->firstItemCssClass !== null) { 202 | $class[] = $this->firstItemCssClass; 203 | } 204 | if ($i === $n - 1 && $this->lastItemCssClass !== null) { 205 | $class[] = $this->lastItemCssClass; 206 | } 207 | if (!empty($class)) { 208 | if (empty($options['class'])) { 209 | $options['class'] = implode(' ', $class); 210 | } else { 211 | $options['class'] .= ' ' . implode(' ', $class); 212 | } 213 | } 214 | 215 | // set parent flag 216 | $item['level'] = $level; 217 | $menu = $this->renderItem($item); 218 | if (!empty($item['items'])) { 219 | $menu .= strtr($this->submenuTemplate, [ 220 | '{items}' => $this->renderItems($item['items'], $level + 1), 221 | ]); 222 | } 223 | $lines[] = Html::tag($tag, $menu, $options); 224 | } 225 | return implode("\n", $lines); 226 | } 227 | 228 | /** 229 | * Renders the content of a menu item. 230 | * Note that the container and the sub-menus are not rendered here. 231 | * @param array $item the menu item to be rendered. Please refer to [[items]] to see what data might be in the item. 232 | * @return string the rendering result 233 | */ 234 | protected function renderItem($item) 235 | { 236 | $icon = isset($item['icon']) ? Html::tag('i', '', ['class' => $item['icon']]) : ''; 237 | $label = ($item['level'] == 1) ? 238 | Html::tag('span', $item['label'], ['class' => 'title']) : (' ' . $item['label']); 239 | $arrow = !empty($item['items']) ? 240 | Html::tag('span', '', ['class' => 'arrow' . ($item['active'] ? ' open' : '')]) : ''; 241 | 242 | if ($item['active'] && $item['level'] == 1) { 243 | $arrow = Html::tag('div', '', ['class' => 'selected']) . $arrow; 244 | } 245 | $badge = ArrayHelper::getValue($item, 'badge', ''); 246 | if (isset($item['url'])) { 247 | $template = ArrayHelper::getValue($item, 'template', $this->linkTemplate); 248 | return strtr($template, [ 249 | '{url}' => Url::toRoute($item['url']), 250 | '{label}' => $label, 251 | '{icon}' => $icon, 252 | '{arrow}' => $arrow, 253 | '{badge}' => $badge, 254 | ]); 255 | } else { 256 | $template = ArrayHelper::getValue($item, 'template', $this->labelTemplate); 257 | return strtr($template, [ 258 | '{label}' => $label, 259 | '{icon}' => $icon, 260 | '{arrow}' => $arrow, 261 | '{badge}' => $badge, 262 | ]); 263 | } 264 | } 265 | } -------------------------------------------------------------------------------- /widgets/Modal.php: -------------------------------------------------------------------------------- 1 | 'Configuration', 22 | * 'toggleButton' => [ 23 | * 'type' => Button::TYPE_M_GREEN, 24 | * 'label' => 'Modal', 25 | * 'icon' => 'fa fa-bell-o', 26 | * 'fullWidth' => true, 27 | * 'stackable' => true, 28 | * ], 29 | * ]); 30 | * 31 | * echo 'Say hello...'; 32 | * 33 | * Modal::end(); 34 | * ~~~ 35 | * 36 | * @see http://twitter.github.io/bootstrap/javascript.html#modals 37 | */ 38 | class Modal extends Widget 39 | { 40 | /** 41 | * @var string the title in the modal window. 42 | */ 43 | public $title; 44 | /** 45 | * @var string the footer content in the modal window. 46 | */ 47 | public $footer; 48 | /** 49 | * @var array the options for rendering the close button tag. 50 | * The close button is displayed in the header of the modal window. Clicking 51 | * on the button will hide the modal window. If this is null, no close button will be rendered. 52 | * 53 | * The following special options are supported: 54 | * 55 | * - tag: string, the tag name of the button. Defaults to 'button'. 56 | * - label: string, the label of the button. Defaults to '×'. 57 | * 58 | * The rest of the options will be rendered as the HTML attributes of the button tag. 59 | * Please refer to the [Modal plugin help](http://twitter.github.com/bootstrap/javascript.html#modals) 60 | * for the supported HTML attributes. 61 | */ 62 | public $closeButton = []; 63 | /** 64 | * @var array the configuration array for [[Button]]. 65 | */ 66 | public $toggleButton = []; 67 | /** 68 | * @var bool indicates whether the modal in full screen width. 69 | */ 70 | public $fullWidth = false; 71 | /** 72 | * @var bool indicates whether the modal is stacked. 73 | */ 74 | public $stackable = false; 75 | 76 | /** 77 | * Initializes the widget. 78 | */ 79 | public function init() 80 | { 81 | parent::init(); 82 | 83 | $this->initOptions(); 84 | 85 | echo $this->renderToggleButton() . "\n"; 86 | echo Html::beginTag('div', $this->options) . "\n"; 87 | echo $this->renderHeader() . "\n"; 88 | echo $this->renderBodyBegin() . "\n"; 89 | } 90 | 91 | /** 92 | * Renders the widget. 93 | */ 94 | public function run() 95 | { 96 | echo "\n" . $this->renderBodyEnd(); 97 | echo "\n" . $this->renderFooter(); 98 | echo "\n" . Html::endTag('div'); 99 | 100 | ModalAsset::register($this->view); 101 | $this->registerPlugin('spinner'); 102 | } 103 | 104 | /** 105 | * Renders the header HTML markup of the modal 106 | * @return string the rendering result 107 | */ 108 | protected function renderHeader() 109 | { 110 | $button = $this->renderCloseButton(); 111 | if ($button !== null) { 112 | $this->title = $button . "\n" . Html::tag('h4', $this->title, ['class' => 'modal-title']); 113 | } 114 | if ($this->title !== null) { 115 | return Html::tag('div', "\n" . $this->title . "\n", ['class' => 'modal-header']); 116 | } else { 117 | return null; 118 | } 119 | } 120 | 121 | /** 122 | * Renders the opening tag of the modal body. 123 | * @return string the rendering result 124 | */ 125 | protected function renderBodyBegin() 126 | { 127 | return Html::beginTag('div', ['class' => 'modal-body']); 128 | } 129 | 130 | /** 131 | * Renders the closing tag of the modal body. 132 | * @return string the rendering result 133 | */ 134 | protected function renderBodyEnd() 135 | { 136 | return Html::endTag('div'); 137 | } 138 | 139 | /** 140 | * Renders the HTML markup for the footer of the modal 141 | * @return string the rendering result 142 | */ 143 | protected function renderFooter() 144 | { 145 | if ($this->footer !== null) { 146 | return Html::tag('div', "\n" . $this->footer . "\n", ['class' => 'modal-footer']); 147 | } else { 148 | return null; 149 | } 150 | } 151 | 152 | /** 153 | * Renders the toggle button. 154 | * @return string the rendering result 155 | */ 156 | protected function renderToggleButton() 157 | { 158 | if (!empty($this->toggleButton)) { 159 | return Button::widget($this->toggleButton); 160 | } else { 161 | return null; 162 | } 163 | } 164 | 165 | /** 166 | * Renders the close button. 167 | * @return string the rendering result 168 | */ 169 | protected function renderCloseButton() 170 | { 171 | if ($this->closeButton !== null) { 172 | $tag = ArrayHelper::remove($this->closeButton, 'tag', 'button'); 173 | $label = ArrayHelper::remove($this->closeButton, 'label', '×'); 174 | if ($tag === 'button' && !isset($this->closeButton['type'])) { 175 | $this->closeButton['type'] = 'button'; 176 | } 177 | return Html::tag($tag, $label, $this->closeButton); 178 | } else { 179 | return null; 180 | } 181 | } 182 | 183 | /** 184 | * Initializes the widget options. 185 | * This method sets the default values for various options. 186 | */ 187 | protected function initOptions() 188 | { 189 | $this->options = array_merge([ 190 | 'class' => 'fade', 191 | 'tabindex' => -1, 192 | ], $this->options); 193 | Html::addCssClass($this->options, 'modal'); 194 | if ($this->fullWidth) { 195 | Html::addCssClass($this->options, 'container'); 196 | } 197 | if ($this->stackable) { 198 | $this->options = array_merge($this->options, ['data-focus-on' => 'input:first']); 199 | } 200 | if ($this->clientOptions !== false) { 201 | $this->clientOptions = array_merge(['show' => false], $this->clientOptions); 202 | } 203 | 204 | if ($this->closeButton !== null) { 205 | $this->closeButton = array_merge([ 206 | 'data-dismiss' => 'modal', 207 | 'aria-hidden' => 'true', 208 | 'class' => 'close', 209 | ], $this->closeButton); 210 | } 211 | 212 | if (!empty($this->toggleButton)) { 213 | $this->toggleButton = array_merge([ 214 | 'options' => ['data-toggle' => 'modal'], 215 | ], $this->toggleButton); 216 | if (!isset($this->toggleButton['options']['data-target']) && !isset($this->toggleButton['options']['href'])) { 217 | $this->toggleButton['options']['data-target'] = '#' . $this->options['id']; 218 | } 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /widgets/ModalAsset.php: -------------------------------------------------------------------------------- 1 | 'select1', 19 | * 'data' => ['1' => 'Item 1', '2' => 'Item 2'], 20 | * ]); 21 | * ``` 22 | * 23 | * @see http://loudev.com/ 24 | */ 25 | class MultiSelect extends InputWidget 26 | { 27 | /** 28 | * @var bool indicates whether the multiSelect is disabled or not. 29 | */ 30 | public $disabled; 31 | /** 32 | * @var array the option data items. The array keys are option values, and the array values 33 | * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). 34 | * For each sub-array, an option group will be generated whose label is the key associated with the sub-array. 35 | * If you have a list of data models, you may convert them into the format described above using 36 | * [[\yii\helpers\ArrayHelper::map()]]. 37 | */ 38 | public $data = []; 39 | 40 | /** 41 | * Initializes the widget. 42 | */ 43 | public function init() 44 | { 45 | parent::init(); 46 | $this->options['multiple'] = true; 47 | if ($this->disabled) { 48 | $this->options['disabled'] = true; 49 | } 50 | Html::addCssClass($this->options, 'multi-select'); 51 | } 52 | /** 53 | * Executes the widget. 54 | */ 55 | public function run() 56 | { 57 | if ($this->hasModel()) { 58 | echo Html::activeDropDownList($this->model, $this->attribute, $this->data, $this->options); 59 | } else { 60 | echo Html::dropDownList($this->name, $this->value, $this->data, $this->options); 61 | } 62 | MultiSelectAsset::register($this->view); 63 | $this->registerPlugin('multiSelect'); 64 | } 65 | 66 | 67 | } -------------------------------------------------------------------------------- /widgets/MultiSelectAsset.php: -------------------------------------------------------------------------------- 1 | [ 23 | * [ 24 | * 'icon' => 'fa fa-warning', 25 | * 'badge' => Badge::widget(['label' => 'New', 'round' => false]), 26 | * 'label' => 'Home', 27 | * 'url' => ['site/index'], 28 | * 'linkOptions' => [...], 29 | * ], 30 | * [ 31 | * 'label' => 'Dropdown', 32 | * 'items' => [ 33 | * ['label' => 'Level 1 - Dropdown A', 'url' => '#'], 34 | * '
  • ', 35 | * '', 36 | * ['label' => 'Level 1 - Dropdown B', 'url' => '#'], 37 | * ], 38 | * ], 39 | * ], 40 | * ]); 41 | * ``` 42 | * 43 | * Note: Multilevel dropdowns beyond Level 1 are not supported in Bootstrap 3. 44 | */ 45 | class Nav extends \yii\bootstrap\Nav 46 | { 47 | // position 48 | const POS_LEFT = ''; 49 | const POS_RIGHT = 'pull-right'; 50 | 51 | /** 52 | * @var array list of items in the nav widget. Each array element represents a single 53 | * menu item which can be either a string or an array with the following structure: 54 | * 55 | * - label: string, required, the nav item label. 56 | * - icon: string, optional, the nav item icon. 57 | * - badge: array, optional 58 | * - url: optional, the item's URL. Defaults to "#". 59 | * - visible: boolean, optional, whether this menu item is visible. Defaults to true. 60 | * - linkOptions: array, optional, the HTML attributes of the item's link. 61 | * - options: array, optional, the HTML attributes of the item container (LI). 62 | * - active: boolean, optional, whether the item should be on active state or not. 63 | * - items: array|string, optional, the configuration array for creating a [[Dropdown]] widget, 64 | * or a string representing the dropdown menu. Note that Bootstrap does not support sub-dropdown menus. 65 | * 66 | * If a menu item is a string, it will be rendered directly without HTML encoding. 67 | */ 68 | public $items = []; 69 | /** 70 | * @var string the nav position 71 | */ 72 | public $position = self::POS_RIGHT; 73 | 74 | /** 75 | * Initializes the widget. 76 | */ 77 | public function init() 78 | { 79 | Html::addCssClass($this->options, 'navbar-nav'); 80 | Html::addCssClass($this->options, $this->position); 81 | parent::init(); 82 | } 83 | 84 | /** 85 | * Renders a widget's item. 86 | * @param string|array $item the item to render. 87 | * @return string the rendering result. 88 | * @throws InvalidConfigException 89 | */ 90 | public function renderItem($item) 91 | { 92 | if (is_string($item)) { 93 | return $item; 94 | } 95 | if (!isset($item['label']) && !isset($item['icon'])) { 96 | throw new InvalidConfigException("The 'label' option is required."); 97 | } 98 | $type = ArrayHelper::getValue($item, 'type', ''); 99 | $options = ArrayHelper::getValue($item, 'options', []); 100 | if ($type === 'user') { 101 | $label = $item['label']; 102 | Html::addCssClass($options, 'user'); 103 | } else { 104 | $label = $this->encodeLabels ? Html::encode($item['label']) : $item['label']; 105 | } 106 | $icon = ArrayHelper::getValue($item, 'icon', null); 107 | if ($icon) { 108 | $label = Html::tag('i', '', ['alt' => $label, 'class' => $icon]); 109 | } 110 | $label .= ArrayHelper::getValue($item, 'badge', ''); 111 | $items = ArrayHelper::getValue($item, 'items'); 112 | $url = Url::toRoute(ArrayHelper::getValue($item, 'url', '#')); 113 | $linkOptions = ArrayHelper::getValue($item, 'linkOptions', []); 114 | 115 | if (isset($item['active'])) { 116 | $active = ArrayHelper::remove($item, 'active', false); 117 | } else { 118 | $active = $this->isItemActive($item); 119 | } 120 | 121 | if ($active) { 122 | Html::addCssClass($options, 'active'); 123 | } 124 | 125 | if ($items !== null) { 126 | $linkOptions['data-toggle'] = 'dropdown'; 127 | $linkOptions['data-hover'] = 'dropdown'; 128 | $linkOptions['data-close-others'] = 'true'; 129 | Html::addCssClass($options, 'dropdown'); 130 | Html::addCssClass($linkOptions, 'dropdown-toggle'); 131 | 132 | if (is_array($items)) { 133 | $items = Dropdown::widget([ 134 | 'title' => ArrayHelper::getValue($item, 'title', ''), 135 | 'more' => ArrayHelper::getValue($item, 'more', []), 136 | 'scroller' => ArrayHelper::getValue($item, 'scroller', []), 137 | 'items' => $items, 138 | 'encodeLabels' => $this->encodeLabels, 139 | 'clientOptions' => false, 140 | 'options' => $type !== 'user' ? ['class' => 'extended'] : [], 141 | ]); 142 | } 143 | } 144 | 145 | return Html::tag('li', Html::a($label, $url, $linkOptions) . $items, $options); 146 | } 147 | 148 | /** 149 | * Renders user item. 150 | * @param $label string User label 151 | * @param $photo string User photo url 152 | * @return string the rendering result 153 | */ 154 | public static function userItem($label, $photo) 155 | { 156 | $lines = []; 157 | $lines[] = Html::img($photo, ['alt' => $label]); 158 | $lines[] = Html::tag('span', $label, ['class' => 'username']); 159 | $lines[] = Html::tag('i', '', ['class' => 'fa fa-angle-down']); 160 | return implode("\n", $lines); 161 | } 162 | } -------------------------------------------------------------------------------- /widgets/NavBar.php: -------------------------------------------------------------------------------- 1 | 'NavBar Test', 26 | * 'brandLogoUrl' => '/img/logo.png', 27 | * ]); 28 | * echo Nav::widget([ 29 | * 'items' => [ 30 | * ['label' => 'Home', 'url' => ['/site/index']], 31 | * ['label' => 'About', 'url' => ['/site/about']], 32 | * ], 33 | * ]); 34 | * NavBar::end(); 35 | * ``` 36 | * 37 | * @see http://twitter.github.io/bootstrap/components.html#navbar 38 | */ 39 | class NavBar extends \yii\bootstrap\NavBar 40 | { 41 | /** 42 | * @var string the url to logo of the brand. 43 | */ 44 | public $brandLogoUrl; 45 | 46 | /** 47 | * Renders toggle button 48 | * @return string the rendering result 49 | */ 50 | protected function renderToggleButton() 51 | { 52 | return Html::tag( 53 | 'a', 54 | Html::img(Metronic::getAssetsUrl($this->view) . '/img/menu-toggler.png'), 55 | [ 56 | 'href' => '#', 57 | 'class' => 'navbar-toggle', 58 | 'data-toggle' => 'collapse', 59 | 'data-target' => '.navbar-collapse' 60 | ] 61 | ); 62 | } 63 | 64 | /** 65 | * Renders Brand 66 | * @return string the rendering result 67 | */ 68 | protected function renderBrand() 69 | { 70 | if ($this->brandLogoUrl) { 71 | $content = Html::img($this->brandLogoUrl, ['class' => 'img-responsive','alt' => $this->brandLabel]); 72 | } else { 73 | $content = $this->brandLabel; 74 | } 75 | Html::addCssClass($this->brandOptions, 'navbar-brand'); 76 | $this->brandOptions['href'] = $this->brandUrl; 77 | return Html::tag('a', $content, $this->brandOptions); 78 | } 79 | 80 | /** 81 | * Initializes the widget. 82 | */ 83 | public function init() 84 | { 85 | if (!isset($this->options['id'])) { 86 | $this->options['id'] = $this->getId(); 87 | } 88 | Html::addCssClass($this->options, 'mega-menu'); 89 | echo Html::beginTag('div', $this->options); 90 | echo Html::beginTag('div', ['class' => 'header-inner']); 91 | echo $this->renderBrand(); 92 | echo $this->renderToggleButton(); 93 | } 94 | 95 | /** 96 | * Executes the widget. 97 | */ 98 | public function run() 99 | { 100 | echo Html::endTag('div'); 101 | echo Html::endTag('div'); 102 | echo Html::tag('div', '', ['class' => 'clearfix']); 103 | } 104 | } -------------------------------------------------------------------------------- /widgets/Note.php: -------------------------------------------------------------------------------- 1 | 'Success! Some Header Goes Here', 18 | * 'body' => 'Duis mollis, est non commodo luctus', 19 | * 'type' => Note::TYPE_INFO, 20 | * ]); 21 | * ``` 22 | * 23 | * The following example will show the content enclosed between the [[begin()]] 24 | * and [[end()]] calls within the alert box: 25 | * ```php 26 | * Note::begin(['type' => Note::TYPE_DANGER]); 27 | * echo 'Some title and body'; 28 | * Note::end(); 29 | * ``` 30 | */ 31 | class Note extends Widget 32 | { 33 | const TYPE_DANGER = 'danger'; 34 | const TYPE_INFO = 'info'; 35 | const TYPE_SUCCESS = 'success'; 36 | const TYPE_WARNING = 'warning'; 37 | 38 | /** 39 | * @var string the note title 40 | */ 41 | public $title; 42 | /** 43 | * @var string the note body 44 | */ 45 | public $body; 46 | /** 47 | * @var string the note type. 48 | * Valid values are 'danger', 'info', 'success', 'warning'. 49 | */ 50 | public $type = self::TYPE_SUCCESS; 51 | 52 | /** 53 | * Initializes the widget. 54 | */ 55 | public function init() 56 | { 57 | parent::init(); 58 | Html::addCssClass($this->options, 'note note-' . $this->type); 59 | echo Html::beginTag('div', $this->options); 60 | echo $this->renderTitle(); 61 | } 62 | 63 | /** 64 | * Executes the widget. 65 | */ 66 | public function run() 67 | { 68 | echo $this->renderBody(); 69 | echo Html::endTag('div'); 70 | } 71 | 72 | /** 73 | * Renders title 74 | * @return string the rendering result 75 | */ 76 | public function renderTitle() 77 | { 78 | return !empty($this->title) ? Html::tag('h4', $this->title, ['class' => 'block']) : ''; 79 | } 80 | 81 | /** 82 | * Renders body 83 | * @return string the rendering result 84 | */ 85 | public function renderBody() 86 | { 87 | return !empty($this->body) ? Html::tag('p', $this->body) : ''; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /widgets/Notification.php: -------------------------------------------------------------------------------- 1 | 'Success! Some Header Goes Here', 22 | * 'body' => 'Duis mollis, est non commodo luctus', 23 | * 'type' => Notification::TYPE_INFO, 24 | * 'openButton' => [ 25 | * 'type' => Button::TYPE_M_GREEN, 26 | * 'label' => 'Notification', 27 | * 'icon' => 'fa fa-bell-o', 28 | * ] 29 | * ]); 30 | * ``` 31 | * 32 | * The following example will show the content enclosed between the [[begin()]] 33 | * and [[end()]] calls within the alert box: 34 | * ```php 35 | * Notification::begin(['type' => Notification::TYPE_DANGER]); 36 | * echo 'Some title and body'; 37 | * Notification::end(); 38 | * ``` 39 | * @see https://github.com/CodeSeven/toastr 40 | */ 41 | class Notification extends Widget 42 | { 43 | // type 44 | const TYPE_ERROR = 'error'; 45 | const TYPE_INFO = 'info'; 46 | const TYPE_SUCCESS = 'success'; 47 | const TYPE_WARNING = 'warning'; 48 | // position 49 | const POSITION_TOP_RIGHT = 'toast-top-right'; 50 | const POSITION_BOTTOM_RIGHT = 'toast-bottom-right'; 51 | const POSITION_BOTTOM_LEFT = 'toast-bottom-left'; 52 | const POSITION_TOP_LEFT = 'toast-top-left'; 53 | const POSITION_TOP_CENTER = 'toast-top-center'; 54 | const POSITION_BOTTOM_CENTER = 'toast-bottom-center'; 55 | const POSITION_FULL_WIDTH = 'toast-top-full-width'; 56 | // easing 57 | const EASING_LINEAR = 'linear'; 58 | const EASING_SWING = 'swing'; 59 | 60 | /** 61 | * @var string the notification title 62 | */ 63 | public $title = ''; 64 | /** 65 | * @var string the notification body 66 | */ 67 | public $body = ''; 68 | /** 69 | * @var string the notification type. 70 | * Valid values are 'danger', 'info', 'success', 'warning'. 71 | */ 72 | public $type = self::TYPE_SUCCESS; 73 | /** 74 | * @var array the configuration array for [[Button]]. 75 | */ 76 | public $openButton = []; 77 | 78 | /** 79 | * Executes the widget. 80 | */ 81 | public function init() 82 | { 83 | parent::init(); 84 | $this->initOptions(); 85 | ob_start(); 86 | ob_implicit_flush(false); 87 | } 88 | 89 | public function run() 90 | { 91 | $this->body .= ob_get_clean(); 92 | if (!empty($this->openButton)) { 93 | /** @var Button $widget */ 94 | $js = 'toastr.options = ' . Json::encode($this->clientOptions) . ';'; 95 | $this->view->registerJs($js, View::POS_READY); 96 | $this->initOpenButton(); 97 | } else { 98 | return null; 99 | } 100 | NotificationAsset::register($this->view); 101 | } 102 | 103 | /** 104 | * Initializes the widget options. 105 | * This method sets the default values for various options. 106 | */ 107 | public function initOptions() 108 | { 109 | $defaultOptions = [ 110 | 'closeButton'=> true, 111 | 'debug'=> false, 112 | 'positionClass'=> 'toast-top-right', 113 | 'onclick'=> null, 114 | 'showDuration'=> '1000', 115 | 'hideDuration'=> '1000', 116 | 'timeOut'=> '5000', 117 | 'extendedTimeOut'=> '1000', 118 | 'showEasing'=> 'swing', 119 | 'hideEasing'=> 'linear', 120 | 'showMethod'=> 'fadeIn', 121 | 'hideMethod'=> 'fadeOut' 122 | ]; 123 | 124 | $this->clientOptions = array_merge($defaultOptions, $this->clientOptions); 125 | } 126 | 127 | /** 128 | * Initializes the widget Button options. 129 | * This method sets the default values for various options. 130 | */ 131 | public function initOpenButton() 132 | { 133 | $widget = Button::begin($this->openButton); 134 | $msg = "'{$this->body}', '{$this->title}'"; 135 | $jsOpen = 'toastr.' . $this->type . '(' . $msg . ')'; 136 | if(!isset($widget->clientEvents['click'])) { 137 | $widget->clientEvents['click'] = "function(){{$jsOpen};}"; 138 | } 139 | $widget->run(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /widgets/NotificationAsset.php: -------------------------------------------------------------------------------- 1 | 'fa fa-bell-o', 24 | * 'title' => 'Title Portlet', 25 | * ]); 26 | * echo 'Body portlet'; 27 | * Portlet::end(); 28 | * 29 | * // Portlet with tools, actions, scroller, events and remote content 30 | * Portlet::begin([ 31 | * 'title' => 'Extended Portlet', 32 | * 'scroller' => [ 33 | * 'height' => 150, 34 | * 'footer' => ['label' => 'Show all', 'url' => '#'], 35 | * ], 36 | * 'clientOptions' => [ 37 | * 'loadSuccess' => new \yii\web\JsExpression('function(){ console.log("load success"); }'), 38 | * 'remote' => '/?r=site/about', 39 | * ], 40 | * 'clientEvents' => [ 41 | * 'close.mr.portlet' => 'function(e) { console.log("portlet closed"); e.preventDefault(); }' 42 | * ], 43 | * 'tools' => [ 44 | * Portlet::TOOL_RELOAD, 45 | * Portlet::TOOL_MINIMIZE, 46 | * Portlet::TOOL_CLOSE, 47 | * ], 48 | * ]); 49 | * ``` 50 | * 51 | * @see http://yii2metronic.icron.org/components.html#portlet 52 | * @author icron.org 53 | * @since 1.0 54 | */ 55 | class Portlet extends Widget 56 | { 57 | //types of the portlet 58 | const TYPE_BOX = 'box'; 59 | const TYPE_SOLID = 'solid'; 60 | const TYPE_NONE = ''; 61 | // color scheme 62 | const COLOR_LIGHT_BLUE = 'light-blue'; 63 | const COLOR_BLUE = 'blue'; 64 | const COLOR_RED = 'red'; 65 | const COLOR_YELLOW = 'yellow'; 66 | const COLOR_GREEN = 'green'; 67 | const COLOR_PURPLE = 'purple'; 68 | const COLOR_LIGHT_GRAY = 'light-grey'; 69 | const COLOR_GRAY = 'grey'; 70 | //tools 71 | const TOOL_MINIMIZE = 'collapse'; 72 | const TOOL_MODAL = 'modal'; 73 | const TOOL_RELOAD = 'reload'; 74 | const TOOL_CLOSE = 'remove'; 75 | /** 76 | * @var string The portlet title 77 | */ 78 | public $title; 79 | /** 80 | * @var string The portlet icon 81 | */ 82 | public $icon; 83 | /** 84 | * @var string The portlet type 85 | * Valid values are 'box', 'solid', '' 86 | */ 87 | public $type = self::TYPE_BOX; 88 | /** 89 | * @var string The portlet color 90 | * Valid values are 'light-blue', 'blue', 'red', 'yellow', 'green', 'purple', 'light-grey', 'grey' 91 | */ 92 | public $color = self::COLOR_BLUE; 93 | /** 94 | * @var array List of actions, where each element must be specified as a string. 95 | */ 96 | public $actions = []; 97 | /** 98 | * @var array The portlet tools 99 | * Valid values are 'collapse', 'modal', 'reload', 'remove' 100 | */ 101 | public $tools = []; 102 | /** 103 | * @var array Scroller options 104 | * is an array of the following structure: 105 | * ```php 106 | * [ 107 | * // required, height of the body portlet as a px 108 | * 'height' => 150, 109 | * // optional, HTML attributes of the scroller 110 | * 'options' => [], 111 | * // optional, footer of the scroller. May contain string or array(the options of Link component) 112 | * 'footer' => [ 113 | * 'label' => 'Show all', 114 | * ], 115 | * ] 116 | * ``` 117 | */ 118 | public $scroller = []; 119 | /** 120 | * @var bool Whether the portlet should be bordered 121 | */ 122 | public $bordered = false; 123 | /** 124 | * @var array The HTML attributes for the widget container 125 | */ 126 | public $options = []; 127 | /** 128 | * @var array The HTML attributes for the widget body container 129 | */ 130 | public $bodyOptions = []; 131 | /** 132 | * @var array The HTML attributes for the widget body container 133 | */ 134 | public $headerOptions = []; 135 | 136 | /** 137 | * Initializes the widget. 138 | */ 139 | public function init() 140 | { 141 | parent::init(); 142 | Html::addCssClass($this->options, 'portlet ' . $this->color . ' ' . $this->type); 143 | echo Html::beginTag('div', $this->options); 144 | Html::addCssClass($this->headerOptions, 'portlet-title'); 145 | echo Html::beginTag('div', $this->headerOptions); 146 | $icon = ($this->icon) ? Html::tag('i', '', ['class' => 'fa ' . $this->icon]) : ''; 147 | echo Html::tag('div', $icon . ' ' . $this->title, ['class' => 'caption']); 148 | 149 | if (!empty($this->tools)) { 150 | $tools = []; 151 | foreach ($this->tools as $tool) { 152 | $class = ''; 153 | switch ($tool) { 154 | case self::TOOL_CLOSE : 155 | $class = 'remove'; 156 | break; 157 | 158 | case self::TOOL_MINIMIZE : 159 | $class = 'collapse'; 160 | break; 161 | 162 | case self::TOOL_RELOAD : 163 | $class = 'reload'; 164 | break; 165 | } 166 | $tools[] = Html::tag('a', '', ['class' => $class, 'href' => '']); 167 | } 168 | echo Html::tag('div', implode("\n", $tools), ['class' => 'tools']); 169 | } 170 | 171 | if (!empty($this->actions)) { 172 | echo Html::tag('div', implode("\n", $this->actions), ['class' => 'actions']); 173 | } 174 | echo Html::endTag('div'); 175 | Html::addCssClass($this->bodyOptions, 'portlet-body'); 176 | echo Html::beginTag('div', $this->bodyOptions); 177 | if (!empty($this->scroller)) { 178 | if (!isset($this->scroller['height'])) { 179 | throw new InvalidConfigException("The 'height' option of the scroller is required."); 180 | } 181 | $options = ArrayHelper::getValue($this->scroller, 'options', []); 182 | echo Html::beginTag( 183 | 'div', 184 | ArrayHelper::merge( 185 | ['class' => 'scroller', 'data-always-visible' => '1', 'data-rail-visible' => '0'], 186 | $options, 187 | ['style' => 'height:' . $this->scroller['height'] . 'px;'] 188 | ) 189 | ); 190 | } 191 | } 192 | 193 | /** 194 | * Renders the widget. 195 | */ 196 | public function run() 197 | { 198 | if (!empty($this->scroller)) { 199 | echo Html::endTag('div'); 200 | $footer = ArrayHelper::getValue($this->scroller, 'footer', ''); 201 | if (!empty($footer)) { 202 | echo Html::beginTag('div', ['class' => 'scroller-footer']); 203 | if (is_array($footer)) { 204 | echo Html::tag('div', Link::widget($footer), ['class' => 'pull-right']); 205 | } elseif (is_string($footer)) { 206 | echo $footer; 207 | } 208 | echo Html::endTag('div'); 209 | } 210 | } 211 | echo Html::endTag('div'); // End portlet body 212 | echo Html::endTag('div'); // End portlet div 213 | $loader = Html::img(Metronic::getAssetsUrl($this->view) . '/img/loading-spinner-grey.gif'); 214 | $this->clientOptions['loader'] = ArrayHelper::getValue($this->clientOptions, 'loader', $loader); 215 | $this->registerPlugin('portlet'); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /widgets/Select2.php: -------------------------------------------------------------------------------- 1 | 'select', 19 | * 'data' => ['1' => 'Item 1', '2' => 'Item 2'], 20 | * 'multiple' => true, 21 | * ]); 22 | * ``` 23 | * 24 | * @see http://ivaynberg.github.io/select2/ 25 | */ 26 | class Select2 extends InputWidget 27 | { 28 | /** 29 | * @var bool indicates whether to display a dropdown select box or use it for tagging 30 | */ 31 | public $asDropdownList = true; 32 | /** 33 | * @var bool indicates whether the select2 is disabled or not. 34 | */ 35 | public $disabled = false; 36 | /** 37 | * @var array the option data items. The array keys are option values, and the array values 38 | * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). 39 | * For each sub-array, an option group will be generated whose label is the key associated with the sub-array. 40 | * If you have a list of data models, you may convert them into the format described above using 41 | * [[\yii\helpers\ArrayHelper::map()]]. 42 | */ 43 | public $data = []; 44 | /** 45 | * @var bool indicates whether the select2 is multiple or not. 46 | */ 47 | public $multiple = false; 48 | 49 | /** 50 | * Initializes the widget. 51 | */ 52 | public function init() 53 | { 54 | parent::init(); 55 | Html::addCssClass($this->options, 'form-control'); 56 | if ($this->multiple) { 57 | $this->options['multiple'] = 'multiple'; 58 | } 59 | if ($this->disabled) { 60 | $this->options['disabled'] = true; 61 | } 62 | } 63 | 64 | /** 65 | * Executes the widget. 66 | */ 67 | public function run() 68 | { 69 | if ($this->hasModel()) { 70 | if ($this->asDropdownList) { 71 | echo Html::activeDropDownList($this->model, $this->attribute, $this->data, $this->options); 72 | } else { 73 | if (!isset($this->clientOptions['query']) && !isset($this->clientOptions['ajax']) && !isset($this->clientOptions['data'])) { 74 | throw new InvalidConfigException('Must be set at least one of the client options: query, data, ajax.'); 75 | } 76 | echo Html::activeHiddenInput($this->model, $this->attribute, $this->options); 77 | } 78 | } else { 79 | if ($this->asDropdownList) { 80 | echo Html::dropDownList($this->name, $this->value, $this->data, $this->options); 81 | } else { 82 | if (!isset($this->clientOptions['query']) && !isset($this->clientOptions['ajax']) && !isset($this->clientOptions['data'])) { 83 | throw new InvalidConfigException('Must be set at least one of the options Select2: query, data, ajax.'); 84 | } 85 | echo Html::hiddenInput($this->name, $this->value, $this->options); 86 | } 87 | } 88 | Select2Asset::register($this->view); 89 | $this->registerPlugin('select2'); 90 | } 91 | } -------------------------------------------------------------------------------- /widgets/Select2Asset.php: -------------------------------------------------------------------------------- 1 | $model, 19 | * 'attribute' => 'country', 20 | * 'size' => Spinner::SIZE_SMALL, 21 | * 'buttonsLocation' => Spinner::BUTTONS_LOCATION_VERTICAL, 22 | * 'clientOptions' => ['step' => 2], 23 | * 'clientEvents' => ['changed' => 'function(event, value){ console.log(value);}'], 24 | * ]); 25 | * ``` 26 | * 27 | * The following example will use the name property instead: 28 | * 29 | * ```php 30 | * echo Spinner::widget([ 31 | * 'name' => 'country', 32 | * 'clientOptions' => ['step' => 2], 33 | * ]); 34 | *``` 35 | * 36 | * @see http://exacttarget.github.io/fuelux/#spinner 37 | * @author 38 | */ 39 | class Spinner extends InputWidget 40 | { 41 | // position 42 | const BUTTONS_LOCATION_VERTICAL = 'vertical'; 43 | const BUTTONS_LOCATION_SIDES = 'sides'; 44 | // size 45 | const SIZE_XSMALL = 'xsmall'; 46 | const SIZE_SMALL = 'small'; 47 | const SIZE_MEDIUM = 'medium'; 48 | const SIZE_LARGE = 'large'; 49 | /** 50 | * @var string Spinner size 51 | */ 52 | public $size = self::SIZE_SMALL; 53 | /** 54 | * @var array the configuration array for [[Button]]. 55 | */ 56 | public $buttonsConfig = ['type' => Button::TYPE_M_BLUE]; 57 | /** 58 | * @var array the HTML attributes for the input element. 59 | */ 60 | public $inputOptions = []; 61 | /** 62 | * @var string The buttons location. 63 | * Valid values are 'vertical', 'sides' 64 | */ 65 | public $buttonsLocation = self::BUTTONS_LOCATION_VERTICAL; 66 | /** 67 | * Executes the widget. 68 | */ 69 | public function run() 70 | { 71 | Html::addCssClass($this->inputOptions, 'spinner-input form-control'); 72 | $this->buttonsConfig = array_merge(['label' => ''], $this->buttonsConfig); 73 | if ($this->hasModel()) { 74 | $input = Html::activeTextInput($this->model, $this->attribute, $this->inputOptions); 75 | } else { 76 | $input = Html::textInput($this->name, $this->value, $this->inputOptions); 77 | } 78 | if ($this->buttonsLocation == self::BUTTONS_LOCATION_VERTICAL) { 79 | $this->buttonsConfig = array_merge($this->buttonsConfig, ['size' => Button::SIZE_MINI]); 80 | } 81 | $btnUp = Button::widget( 82 | array_merge($this->buttonsConfig, [ 83 | 'icon' => 'fa fa-angle-up', 84 | 'options' => ['class' => 'spinner-up'] 85 | ]) 86 | ); 87 | $btnDown = Button::widget( 88 | array_merge($this->buttonsConfig, [ 89 | 'icon' => 'fa fa-angle-down', 90 | 'options' => ['class' => 'spinner-down'] 91 | ]) 92 | ); 93 | 94 | if ($this->buttonsLocation == self::BUTTONS_LOCATION_VERTICAL) { 95 | $spinner = $input . Html::tag('div', $btnUp . $btnDown, ['class' => 'spinner-buttons input-group-btn btn-group-vertical']); 96 | } else { 97 | $spinner = Html::tag('div', $btnUp . $input . $btnDown, ['class' => 'spinner-buttons input-group-btn']); 98 | } 99 | echo Html::tag('div' , Html::tag('div', $spinner, ['class' => 'input-group input-' . $this->size]), $this->options); 100 | SpinnerAsset::register($this->view); 101 | $this->registerPlugin('spinner'); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /widgets/SpinnerAsset.php: -------------------------------------------------------------------------------- 1 | [ 21 | * [ 22 | * 'label' => 'One', 23 | * 'content' => 'Anim pariatur cliche...', 24 | * 'active' => true 25 | * ], 26 | * [ 27 | * 'label' => 'Two', 28 | * 'content' => 'Anim pariatur cliche...', 29 | * 'headerOptions' => [...], 30 | * 'options' => ['id' => 'myveryownID'], 31 | * ], 32 | * [ 33 | * 'label' => 'Dropdown', 34 | * 'items' => [ 35 | * [ 36 | * 'label' => 'DropdownA', 37 | * 'content' => 'DropdownA, Anim pariatur cliche...', 38 | * ], 39 | * [ 40 | * 'label' => 'DropdownB', 41 | * 'content' => 'DropdownB, Anim pariatur cliche...', 42 | * ], 43 | * ], 44 | * ], 45 | * ], 46 | * ]); 47 | * ``` 48 | * 49 | */ 50 | class Tabs extends \yii\bootstrap\Tabs 51 | { 52 | 53 | // Tab placements. 54 | const PLACEMENT_ABOVE = 'above'; 55 | const PLACEMENT_BELOW = 'below'; 56 | const PLACEMENT_LEFT = 'left'; 57 | const PLACEMENT_RIGHT = 'right'; 58 | 59 | // Tab type 60 | const NAV_TYPE_TABS = 'nav-tabs'; 61 | const NAV_TYPE_PILLS = 'nav-pills'; 62 | 63 | /** 64 | * @var string, specifies the Bootstrap tab styling. 65 | * Valid values 'nav-tabs', 'nav-pills' 66 | */ 67 | public $navType = self::NAV_TYPE_TABS; 68 | 69 | /** 70 | * @var string the placement of the tabs. 71 | * Valid values are 'above', 'below', 'left' and 'right'. 72 | */ 73 | public $placement = self::PLACEMENT_BELOW; 74 | 75 | /** 76 | * @var bool Indicates whether tabs is styled for Metronic. 77 | */ 78 | public $styled = true; 79 | /** 80 | * @var bool Indicates whether tabs is justified. 81 | */ 82 | public $justified = false; 83 | 84 | /** 85 | * Initializes the widget. 86 | */ 87 | public function init() 88 | { 89 | if ($this->justified) { 90 | Html::addCssClass($this->options, 'nav-justified'); 91 | } 92 | 93 | Html::addCssClass($this->options, 'nav ' . $this->navType); 94 | parent::init(); 95 | } 96 | 97 | /** 98 | * Renders the widget. 99 | */ 100 | public function run() 101 | { 102 | $classWrap = ['tabs-' . $this->placement]; 103 | if ($this->styled) { 104 | $classWrap[] = 'tabbable-custom'; 105 | if ($this->justified) { 106 | $classWrap[] = 'nav-justified'; 107 | } 108 | } else { 109 | $classWrap[]= 'tabbable'; 110 | } 111 | echo Html::beginTag('div', ['class' => implode(' ', $classWrap)]); 112 | parent::run(); 113 | echo Html::endTag('div'); 114 | } 115 | 116 | /** 117 | * Renders tab items as specified on [[items]]. 118 | * @return string the rendering result. 119 | * @throws InvalidConfigException. 120 | */ 121 | protected function renderItems() 122 | { 123 | $headers = []; 124 | $panes = []; 125 | foreach ($this->items as $n => $item) { 126 | if (!isset($item['label'])) { 127 | throw new InvalidConfigException("The 'label' option is required."); 128 | } 129 | $label = $this->encodeLabels ? Html::encode($item['label']) : $item['label']; 130 | $headerOptions = array_merge($this->headerOptions, ArrayHelper::getValue($item, 'headerOptions', [])); 131 | 132 | if (isset($item['items'])) { 133 | if ($this->styled) { 134 | throw new InvalidConfigException("The 'styled' not support dropdown items. Please, set 'styled' to false."); 135 | } 136 | $label .= ' '; 137 | Html::addCssClass($headerOptions, 'dropdown'); 138 | 139 | if ($this->renderDropdown($item['items'], $panes)) { 140 | Html::addCssClass($headerOptions, 'active'); 141 | } 142 | 143 | $header = Html::a($label, "#", ['class' => 'dropdown-toggle', 'data-toggle' => 'dropdown']) . "\n" 144 | . Dropdown::widget(['items' => $item['items'], 'clientOptions' => false]); 145 | } elseif (isset($item['content'])) { 146 | $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', [])); 147 | $options['id'] = ArrayHelper::getValue($options, 'id', $this->options['id'] . '-tab' . $n); 148 | 149 | Html::addCssClass($options, 'tab-pane'); 150 | if (ArrayHelper::remove($item, 'active')) { 151 | Html::addCssClass($options, 'active'); 152 | Html::addCssClass($headerOptions, 'active'); 153 | } 154 | $header = Html::a($label, '#' . $options['id'], ['data-toggle' => 'tab']); 155 | $panes[] = Html::tag('div', $item['content'], $options); 156 | } else { 157 | throw new InvalidConfigException("Either the 'content' or 'items' option must be set."); 158 | } 159 | 160 | $headers[] = Html::tag('li', $header, $headerOptions); 161 | } 162 | 163 | $headers = Html::tag('ul', implode("\n", $headers), $this->options); 164 | $panes = Html::tag('div', implode("\n", $panes), ['class' => 'tab-content']); 165 | 166 | return ($this->placement == self::PLACEMENT_BELOW) ? ($panes . "\n" . $headers) : ($headers . "\n" . $panes); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /widgets/TextInputWidget.php: -------------------------------------------------------------------------------- 1 | hasModel()) { 28 | echo Html::activeTextInput($this->model, $this->attribute, $this->options); 29 | } else { 30 | echo Html::textInput($this->name, $this->value, $this->options); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /widgets/Widget.php: -------------------------------------------------------------------------------- 1 | 16 | * @since 1.0 17 | */ 18 | class Widget extends \yii\bootstrap\Widget 19 | { 20 | /** 21 | * @var array the HTML attributes for the widget container tag. 22 | */ 23 | public $options = []; 24 | /** 25 | * @var array the options for the underlying Metronic JS plugin. 26 | * Please refer to the corresponding Metronic plugin Web page for possible options. 27 | * For example, [this page](http://yii2metronic.icron.org/javascript.html#portlet) shows 28 | * how to use the "Portlet" plugin and the supported options (e.g. "loadSuccess"). 29 | */ 30 | public $clientOptions = []; 31 | /** 32 | * @var array the event handlers for the underlying Metronic JS plugin. 33 | * Please refer to the corresponding Metronic plugin Web page for possible events. 34 | * For example, [this page](http://yii2metronic.icron.org/javascript.html#portlet) shows 35 | * how to use the "Portlet" plugin and the supported events (e.g. "close.mr.portlet"). 36 | */ 37 | public $clientEvents = []; 38 | 39 | /** 40 | * Registers a specific Bootstrap plugin and the related events 41 | * @param string $name the name of the Bootstrap plugin 42 | */ 43 | protected function registerPlugin($name) 44 | { 45 | $view = $this->getView(); 46 | $id = $this->options['id']; 47 | if ($this->clientOptions !== false) { 48 | $options = empty($this->clientOptions) ? '' : Json::encode($this->clientOptions); 49 | $js = "jQuery('#$id').$name($options);"; 50 | $view->registerJs($js); 51 | } 52 | if (!empty($this->clientEvents)) { 53 | $js = []; 54 | foreach ($this->clientEvents as $event => $handler) { 55 | $js[] = "jQuery('#$id').on('$event', $handler);"; 56 | } 57 | $view->registerJs(implode("\n", $js)); 58 | } 59 | } 60 | } 61 | --------------------------------------------------------------------------------