├── .gitignore ├── CHANGELOG.md ├── DataColumn.php ├── GridView.php ├── LinkPageSizer.php ├── README.md ├── TotalColumn.php ├── actions ├── ResizableColumnsAction.php └── VisibleColumnsAction.php ├── assets ├── GridViewAsset.php └── SortableAsset.php ├── base └── BaseAction.php ├── components ├── BaseManager.php └── Manager.php ├── composer.json ├── docs ├── screenshot1.png ├── screenshot2.png └── screenshot3.png ├── interfaces ├── ManagerInterface.php └── StorageInterface.php ├── resources ├── css │ ├── resizable-columns.css │ └── resizable-columns.min.css └── js │ ├── resizable-columns.js │ └── resizable-columns.min.js └── widgets └── VisibleColumnsWidget.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # yii2-grid 2 | =========== 3 | 4 | v1.1.6 [2017-06-09] 5 | ------------------- 6 | 7 | - Fixed #4: README section *Adding ability resize width of columns*. (githubjeka) 8 | - Fixed #3: error `unknown property` if column is not `yii\grid\DataColumn` class. (githubjeka) 9 | - Enhancement: CHANGELOG. 10 | - Fixed: Failed to instantiate component or class "gridManager". 11 | - Enhancement #5: Update README section *Adding ability display all rows of grid*. 12 | 13 | v1.1.5 [2017-04-22] 14 | ------------------- 15 | 16 | - Added ability formatting a footer value. 17 | - Fixed `calculateSummary()` method. 18 | 19 | v1.1.4 [2016-02-27] 20 | ------------------- 21 | 22 | - Added keep last modified the page sizer. 23 | 24 | v1.1.3 [2016-02-08] 25 | ------------------- 26 | 27 | - Added ability resizing columns. 28 | - Add class action `bupy7\grid\actions\ResizableColumnsAction`. 29 | - Moved base functional of grid actions to `bupy7\grid\base\BaseAction`. 30 | - Moved screenshots to `docs` directory. 31 | - Release of `resizable columns jQuery plugin` to Grid. 32 | 33 | v1.1.2 [2015-11-21] 34 | ------------------- 35 | 36 | - Fix of panel HTML-template. 37 | 38 | v1.1.1 [2015-11-15] 39 | ------------------- 40 | 41 | - Added ability sortering of visible a columns. 42 | 43 | v1.1.0 [2015-11-13] 44 | ------------------- 45 | 46 | - Added ability realization custom storage (session, database and etc). 47 | - Rename manager class `GridSettings` to `Manager`. 48 | 49 | v1.0.1 [2015-09-01] 50 | ------------------- 51 | 52 | - Added ability not add footer of grid column. 53 | 54 | v1.0.0 [2015-07-24] 55 | ------------------- 56 | 57 | - First release. 58 | -------------------------------------------------------------------------------- /DataColumn.php: -------------------------------------------------------------------------------- 1 | 8 | * @since 1.0.1 9 | */ 10 | class DataColumn extends \yii\grid\DataColumn 11 | { 12 | /** 13 | * @var boolean Whether set this property `false` then footer column will not added. 14 | * @see renderFooterCell() 15 | */ 16 | public $footerVisible = true; 17 | 18 | /** 19 | * @inheritdoc 20 | */ 21 | public function renderFooterCell() 22 | { 23 | if ($this->footerVisible) { 24 | return parent::renderFooterCell(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /GridView.php: -------------------------------------------------------------------------------- 1 | 71 | {panelHeading} 72 |
{items}
73 | {panelFooter} 74 | 75 | HTML; 76 | /** 77 | * @var boolean Whether set 'true' will be displays heading of panel. 78 | */ 79 | public $panelHeading = true; 80 | /** 81 | * @var string HTML-template for heading of panel. 82 | * Allows uses: 83 | * - `{items}` - the summary section. See [[renderSummary()]]. 84 | * - `{summary}` - the summary section. See [[renderSummary()]]. 85 | * - `{pager}` - the pager. See [[renderPager()]]. 86 | * - `{pageSizer}` - the page size. See [[renderPageSizer]]. 87 | * - `{errors}` - the filter model error summary. See [[renderErrors()]]. 88 | * - `{sorter}` - the sorter. See [[renderSorter()]]. 89 | * @see http://getbootstrap.com/components/#panels-heading 90 | */ 91 | public $panelHeadingTemplate = <<{pageSizer} 93 |
94 | HTML; 95 | /** 96 | * @var boolean Whether set 'true' will be displays footer of panel. 97 | */ 98 | public $panelFooter = true; 99 | /** 100 | * @var string HTML-template for footer of panel. 101 | * Allows uses: 102 | * - `{items}` - the summary section. See [[renderSummary()]]. 103 | * - `{summary}` - the summary section. See [[renderSummary()]]. 104 | * - `{pager}` - the pager. See [[renderPager()]]. 105 | * - `{pageSizer}` - the page size. See [[renderPageSizer]]. 106 | * - `{errors}` - the filter model error summary. See [[renderErrors()]]. 107 | * - `{sorter}` - the sorter. See [[renderSorter()]]. 108 | * @see http://getbootstrap.com/components/#panels-footer 109 | */ 110 | public $panelFooterTemplate = << 112 |
{summary}
113 |
{pager}
114 | 115 | HTML; 116 | /** 117 | * Tags to replace in the rendered layout. Enter this as `$key => $value` pairs, where: 118 | * - $key: string, defines the flag. 119 | * - $value: string|Closure, the value that will be replaced. You can set it as a callback 120 | * function to return a string of the signature: 121 | * function ($widget) { return 'custom'; } 122 | * 123 | * For example: 124 | * ['{flag}' => ' '200', 214 | * 'username' => '123', 215 | * //and etc 216 | * ] 217 | * ~~~ 218 | * @since 1.1.3 219 | */ 220 | public $resizableColumns = false; 221 | /** 222 | * @var array Options of resizable columns plugin. 223 | * List options: 224 | * - `selector` (string): CSS relative path to header columns of table. 225 | */ 226 | public $resizableColumnsOptions = [ 227 | 'selector' => 'tr > th[data-resizable-column], tr > td[data-resizable-column]', 228 | ]; 229 | /** 230 | * @var array|string URL to action for save width of resizable column after changes it. 231 | */ 232 | public $resizableColumnsUrl = ['url/to/action']; 233 | /** 234 | * @var ManagerInterface|array|string 235 | * @since 1.1.4 236 | */ 237 | public $gridManager = 'gridManager'; 238 | /** 239 | * @var boolean Keeping the page sizer. If set as `true` then last modified page sizer will be keep to 240 | * the storage. 241 | * @since 1.1.4 242 | */ 243 | public $keepPageSizer = false; 244 | 245 | /** 246 | * @inheritdoc 247 | * @since 1.1.4 248 | */ 249 | public function init() 250 | { 251 | parent::init(); 252 | if ($this->keepPageSizer) { 253 | $this->gridManager = Instance::ensure($this->gridManager, 'bupy7\grid\interfaces\ManagerInterface'); 254 | } 255 | } 256 | 257 | /** 258 | * @inheritdoc 259 | */ 260 | public function run() 261 | { 262 | Html::addCssClass($this->tableOptions, 'table'); 263 | if ($this->hover) { 264 | Html::addCssClass($this->tableOptions, 'table-hover'); 265 | } 266 | if ($this->bordered) { 267 | Html::addCssClass($this->tableOptions, 'table-bordered'); 268 | } 269 | if ($this->striped) { 270 | Html::addCssClass($this->tableOptions, 'table-striped'); 271 | } 272 | $this->initLayout(); 273 | 274 | GridViewAsset::register($this->view); 275 | 276 | $options = Json::htmlEncode($this->getClientOptions()); 277 | $this->view->registerJs("$('#{$this->options['id']}').yiiGridView({$options});"); 278 | 279 | if ($this->resizableColumns !== false) { 280 | $options = Json::htmlEncode($this->resizableColumnsOptions); 281 | $url = Url::toRoute($this->resizableColumnsUrl); 282 | $js = <<options['id']}').resizableColumns({$options}).on('afterDragging.rc', function(event) { 284 | var column = $(this).closest('[data-resizable-column]'), 285 | data = {}; 286 | data[column.data('resizable-column')] = column.outerWidth(); 287 | $.ajax({ 288 | type: 'post', 289 | url: '{$url}', 290 | data: data 291 | }); 292 | }); 293 | JS; 294 | $this->view->registerJs($js); 295 | } 296 | BaseListView::run(); 297 | } 298 | 299 | /** 300 | * @inheritdoc 301 | */ 302 | public function renderSection($name) 303 | { 304 | switch ($name) { 305 | case '{pageSizer}': 306 | return $this->renderPageSizer(); 307 | default: 308 | return parent::renderSection($name); 309 | } 310 | } 311 | 312 | /** 313 | * Renders the page sizer. 314 | * @return string the rendering result 315 | */ 316 | public function renderPageSizer() 317 | { 318 | $pagination = $this->getPagination(); 319 | if ($pagination === false) { 320 | return ''; 321 | } 322 | /* @var $class LinkPageSizer */ 323 | $pageSizer = $this->pageSizer; 324 | $class = ArrayHelper::remove($pageSizer, 'class', LinkPageSizer::className()); 325 | $pageSizer['pagination'] = clone $pagination; 326 | $pageSizer['view'] = $this->getView(); 327 | 328 | return $class::widget($pageSizer); 329 | } 330 | 331 | /** 332 | * Generation layout with panel if it uses. 333 | */ 334 | public function renderPanel() 335 | { 336 | if ($this->panel === false) { 337 | return; 338 | } 339 | $this->layout = strtr( 340 | $this->panelTemplate, 341 | [ 342 | '{panelHeading}' => $this->panelHeading !== false 343 | ? Html::tag('div', $this->panelHeadingTemplate, ['class' => 'panel-heading']) 344 | : '', 345 | '{type}' => $this->panel, 346 | '{panelFooter}' => $this->panelFooter !== false 347 | ? Html::tag('div', $this->panelFooterTemplate, ['class' => 'panel-footer']) 348 | : '', 349 | ] 350 | ); 351 | } 352 | 353 | /** 354 | * Initialization layout of GridView. 355 | */ 356 | public function initLayout() 357 | { 358 | $this->renderPanel(); 359 | foreach ($this->customTags as $key => $value) { 360 | if ($value instanceof \Closure) { 361 | $value = call_user_func($value, $this); 362 | } 363 | $this->layout = str_replace("{{$key}}", $value, $this->layout); 364 | } 365 | } 366 | 367 | /** 368 | * @inheritdoc 369 | */ 370 | public function renderTableHeader() 371 | { 372 | $content = parent::renderTableHeader(); 373 | return strtr( 374 | $content, 375 | [ 376 | '' => "\n" . $this->generateRows($this->beforeHeader), 377 | '' => $this->generateRows($this->afterHeader) . "\n", 378 | ] 379 | ); 380 | } 381 | 382 | /** 383 | * @inheritdoc 384 | */ 385 | public function renderTableFooter() 386 | { 387 | $content = parent::renderTableFooter(); 388 | return strtr( 389 | $content, 390 | [ 391 | '' => "\n" . $this->generateRows($this->beforeFooter), 392 | '' => $this->generateRows($this->afterFooter) . "\n", 393 | ] 394 | ); 395 | } 396 | 397 | /** 398 | * @inheritdoc 399 | */ 400 | public function initColumns() 401 | { 402 | $visibleColumns = false; 403 | if (is_array($this->visibleColumns)) { 404 | $visibleColumns = array_flip($this->visibleColumns); 405 | } 406 | if (empty($this->columns)) { 407 | $this->guessColumns(); 408 | } 409 | $dataColumns = []; 410 | $serviceColumns = []; 411 | foreach ($this->columns as $i => $column) { 412 | if (is_string($column)) { 413 | $column = $this->createDataColumn($column); 414 | } else { 415 | $column = Yii::createObject(array_merge([ 416 | 'class' => $this->dataColumnClass ? : DataColumn::className(), 417 | 'grid' => $this, 418 | ], $column)); 419 | } 420 | if ($column->visible) { 421 | $key = $i; 422 | if ($visibleColumns !== false) { 423 | if ($column instanceof DataColumn) { 424 | if (isset($visibleColumns[$column->attribute])) { 425 | $key = $visibleColumns[$column->attribute]; 426 | } else { 427 | continue; 428 | } 429 | } else { 430 | $serviceColumns[$i] = $column; 431 | continue; 432 | } 433 | } 434 | $dataColumns[$key] = $column; 435 | } 436 | } 437 | if ($this->resizableColumns !== false) { 438 | foreach ($dataColumns as $column) { 439 | if (!($column instanceof DataColumn)) { 440 | continue; 441 | } 442 | $column->headerOptions['data-resizable-column'] = $column->attribute; 443 | if (isset($this->resizableColumns[$column->attribute])) { 444 | Html::addCssStyle($column->headerOptions, [ 445 | 'width' => $this->resizableColumns[$column->attribute] . 'px', 446 | 'min-width' => $this->resizableColumns[$column->attribute] . 'px', 447 | ]); 448 | } 449 | } 450 | } 451 | $this->columns = $this->mergeColumns($dataColumns, $serviceColumns); 452 | } 453 | 454 | /** 455 | * Sorting and ordering columns by specific criteria. 456 | * @param array $dataColumns Array of columns DataColumn instance. 457 | * @param array $serviceColumns Array of columns with other class instance. 458 | * @return array 459 | * @since 1.1.3 460 | */ 461 | protected function mergeColumns(array $dataColumns, array $serviceColumns) 462 | { 463 | $columns = $dataColumns; 464 | ksort($columns); 465 | foreach ($serviceColumns as $i => $column) { 466 | $tmp = array_slice($columns, 0, $i); 467 | $tmp[] = $column; 468 | $columns = array_merge($tmp, array_slice($columns, $i)); 469 | } 470 | return $columns; 471 | } 472 | 473 | /** 474 | * Generate HTML markup for additional table rows for header and/or footer 475 | * 476 | * @param array|string $data the table rows configuration 477 | * @return string 478 | */ 479 | protected function generateRows($data) 480 | { 481 | if (empty($data)) { 482 | return ''; 483 | } 484 | if (is_string($data)) { 485 | return $data; 486 | } 487 | $rows = ''; 488 | if (is_array($data)) { 489 | foreach ($data as $row) { 490 | if (empty($row['columns'])) { 491 | continue; 492 | } 493 | $rowOptions = ArrayHelper::getValue($row, 'options', []); 494 | $rows .= Html::beginTag('tr', $rowOptions); 495 | foreach ($row['columns'] as $col) { 496 | $colOptions = ArrayHelper::getValue($col, 'options', []); 497 | $colContent = ArrayHelper::getValue($col, 'content', ''); 498 | $tag = ArrayHelper::getValue($col, 'tag', 'th'); 499 | $rows .= "\t" . Html::tag($tag, $colContent, $colOptions) . "\n"; 500 | } 501 | $rows .= Html::endTag('tr') . "\n"; 502 | } 503 | } 504 | return $rows; 505 | } 506 | 507 | /** 508 | * @return Pagination|false The pagination object. If this is false, it means the pagination is disabled. 509 | * @since 1.1.4 510 | */ 511 | protected function getPagination() 512 | { 513 | $pagination = $this->dataProvider->getPagination(); 514 | if ($pagination === false) { 515 | return false; 516 | } 517 | if ($this->keepPageSizer) { 518 | $defaultPageSize = $this->gridManager->getDefaultPageSize($this->getId()); 519 | if ($defaultPageSize !== false) { 520 | $pagination->defaultPageSize = $defaultPageSize; 521 | } 522 | $this->gridManager->setDefaultPageSize($this->getId(), $pagination->getPageSize()); 523 | } 524 | return $pagination; 525 | } 526 | } 527 | -------------------------------------------------------------------------------- /LinkPageSizer.php: -------------------------------------------------------------------------------- 1 | '10', 20 => '20', 40 => '40']; 37 | /** 38 | * @var array HTML attributes for the pager container tag. 39 | * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. 40 | */ 41 | public $options = ['class' => 'pagination']; 42 | /** 43 | * @var array HTML attributes for the link in a pager container tag. 44 | * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. 45 | */ 46 | public $linkOptions = []; 47 | /** 48 | * @var string the CSS class for the active (currently selected) page size button. 49 | */ 50 | public $activePageSizeCssClass = 'active'; 51 | 52 | /** 53 | * Initializes the pager. 54 | */ 55 | public function init() 56 | { 57 | if ($this->pagination === null) { 58 | throw new InvalidConfigException('The "pagination" property must be set.'); 59 | } 60 | } 61 | 62 | /** 63 | * Executes the widget. 64 | * This overrides the parent implementation by displaying the generated page size buttons. 65 | */ 66 | public function run() 67 | { 68 | echo $this->renderPageSizerButtons(); 69 | } 70 | 71 | /** 72 | * Renders the page buttons. 73 | * @return string the rendering result 74 | */ 75 | protected function renderPageSizerButtons() 76 | { 77 | if (count($this->availableSizes) === 0) { 78 | return ''; 79 | } 80 | 81 | $buttons = []; 82 | $currentPageSize = $this->pagination->getPageSize(); 83 | 84 | foreach ($this->availableSizes as $size => $label) { 85 | $buttons[] = $this->renderPageSizeButton($label, $size, null, $size == $currentPageSize); 86 | } 87 | 88 | return Html::tag('ul', implode(PHP_EOL, $buttons), $this->options); 89 | } 90 | 91 | /** 92 | * Renders a page size button. 93 | * You may override this method to customize the generation of page size buttons. 94 | * @param string $label the text label for the button 95 | * @param integer $pageSize the page size 96 | * @param string $class the CSS class for the page button. 97 | * @param boolean $active whether this page button is active 98 | * @return string the rendering result 99 | */ 100 | protected function renderPageSizeButton($label, $pageSize, $class, $active) 101 | { 102 | $options = ['class' => $class === '' ? null : $class]; 103 | if ($active) { 104 | Html::addCssClass($options, $this->activePageSizeCssClass); 105 | } 106 | $linkOptions = $this->linkOptions; 107 | $linkOptions['data-page-size'] = $pageSize; 108 | 109 | return Html::tag( 110 | 'li', 111 | Html::a($label, $this->createUrl($this->pagination, $pageSize), $linkOptions), 112 | $options 113 | ); 114 | } 115 | 116 | /** 117 | * Creates an url for the specified page size. 118 | * @param \yii\data\Pagination $pagination 119 | * @param integer $pageSize page size 120 | * @param boolean $absolute whether to create an absolute URL. Defaults to `false`. 121 | * @return string the created URL 122 | */ 123 | protected function createUrl($pagination, $pageSize, $absolute = false) 124 | { 125 | if (($params = $pagination->params) === null) { 126 | $request = Yii::$app->getRequest(); 127 | $params = $request instanceof Request ? $request->getQueryParams() : []; 128 | } 129 | 130 | $currentPageSize = $pagination->getPageSize(); 131 | $currentPage = $pagination->getPage(); 132 | $target = $currentPage * $currentPageSize; 133 | $page = (int)($target / $pageSize); 134 | 135 | if ($page > 0 || $page >= 0 && $pagination->forcePageParam) { 136 | $params[$pagination->pageParam] = $page + 1; 137 | } else { 138 | unset($params[$pagination->pageParam]); 139 | } 140 | if ($pageSize != $pagination->defaultPageSize) { 141 | $params[$pagination->pageSizeParam] = $pageSize; 142 | } else { 143 | unset($params[$pagination->pageSizeParam]); 144 | } 145 | 146 | $params[0] = $pagination->route === null ? Yii::$app->controller->getRoute() : $pagination->route; 147 | $urlManager = $pagination->urlManager === null ? Yii::$app->getUrlManager() : $pagination->urlManager; 148 | if ($absolute) { 149 | return $urlManager->createAbsoluteUrl($params); 150 | } 151 | return $urlManager->createUrl($params); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | yii2-grid 2 | ========= 3 | Simple extended `yii\grid\GridView`. 4 | 5 | **Features:** 6 | 7 | - Wrapping GridView in [Bootstrap3 Panel](http://getbootstrap.com/components/#panels). 8 | - Ability changing size of page. 9 | - Column of 'Total' with ability using custom formulas. 10 | - Hard-header. 11 | - Custom tags of template the GridView. 12 | - Ability disabling/enabling/sort visible columns of real-time. 13 | - Ability resize width of columns. 14 | - Keepping last modified page sizer. 15 | 16 | ![Screenshot1](docs/screenshot1.png) 17 | 18 | ![Screenshot2](docs/screenshot2.png) 19 | 20 | ![Screenshot3](docs/screenshot3.png) 21 | 22 | Installation 23 | ------------ 24 | 25 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 26 | 27 | Either run 28 | 29 | ``` 30 | php composer.phar require --prefer-dist bupy7/yii2-grid "*" 31 | ``` 32 | 33 | or add 34 | 35 | ``` 36 | "bupy7/yii2-grid": "*" 37 | ``` 38 | 39 | to the require section of your `composer.json` file. 40 | 41 | 42 | Usage 43 | ----- 44 | 45 | ### Simple usage 46 | 47 | ```php 48 | use bupy7\grid\GridView; 49 | 50 | echo GridView::widget([ 51 | 'dataProvider' => $dataProvider, 52 | 'filterModel' => $searchModel, 53 | 'columns' => [ 54 | ['class' => 'yii\grid\SerialColumn'], 55 | ['class' => 'yii\grid\CheckboxColumn'], 56 | 'attribute1', 57 | 'attribute2', 58 | ], 59 | ]); 60 | ``` 61 | 62 | ### Adding delete button 63 | 64 | Adding your view: 65 | 66 | ```php 67 | use bupy7\grid\GridView; 68 | 69 | $panelHeadingTemplate = <<{controls} 71 |
{pageSizer}
72 |
73 | HTML; 74 | echo GridView::widget([ 75 | 'customTags' => [ 76 | 'controls' => $this->render('_controls'), 77 | ], 78 | 'panelHeadingTemplate' => $panelHeadingTemplate, 79 | 'dataProvider' => $dataProvider, 80 | 'filterModel' => $searchModel, 81 | 'columns' => [ 82 | ['class' => 'yii\grid\SerialColumn'], 83 | ['class' => 'yii\grid\CheckboxColumn'], 84 | 'attribute1', 85 | 'attribute2', 86 | ], 87 | ]); 88 | ``` 89 | 90 | Adding your ```_controls``` view: 91 | 92 | ```php 93 | 97 | 'all-delete', 99 | 'class' => 'btn btn-danger', 100 | 'title' => Yii::t('app', 'DELETE'), 101 | ]); ?> 102 | registerJs($js); 115 | ``` 116 | 117 | Adding your controller: 118 | 119 | ```php 120 | public function actionDelete($id = null) 121 | { 122 | if ($id === null) { 123 | $ids = (array)Yii::$app->request->post('ids'); 124 | } else { 125 | $ids = (array)$id; 126 | } 127 | for ($i = 0; $i != count($ids); $i++) { 128 | $this->findModel($ids[$i])->delete(); 129 | } 130 | return $this->redirect(['index']); 131 | } 132 | ``` 133 | 134 | ### Adding ability change visible columns 135 | 136 | #### Via session 137 | 138 | Override session component: 139 | 140 | ```php 141 | use bupy7\grid\interfaces\StorageInterface; 142 | 143 | /** 144 | * @inheritdoc 145 | */ 146 | class Session extends \yii\web\Session implements StorageInterface 147 | { 148 | 149 | } 150 | ``` 151 | 152 | Adding your config of application: 153 | 154 | ```php 155 | 'components' => [ 156 | 'gridManager' => [ 157 | 'class' => 'bupy7\grid\components\Manager', 158 | 'storage' => 'session', 159 | ], 160 | ] 161 | ``` 162 | 163 | Adding your controller: 164 | 165 | ```php 166 | use bupy7\grid\actions\VisibleColumnsAction; 167 | use yii\helpers\Url; 168 | 169 | public function actions() 170 | { 171 | return parent::actions() + [ 172 | 'visible-columns' => [ 173 | 'class' => VisibleColumnsAction::className(), 174 | 'gridId' => 'example-grid', 175 | ], 176 | ]; 177 | } 178 | 179 | public function actionIndex() 180 | { 181 | Url::remember(); 182 | 183 | $searchModel = new ExampleSearch(); 184 | $dataProvider = $searchModel->search(Yii::$app->request->queryParams); 185 | 186 | $visibleColumns = Yii::$app->gridManager->getVisibleColumns('example-grid'); 187 | if ($visibleColumns === false) { 188 | $visibleColumns = array_keys($searchModel->gridColumnsList()); 189 | } 190 | 191 | return $this->render('index', [ 192 | 'searchModel' => $searchModel, 193 | 'dataProvider' => $dataProvider, 194 | 'visibleColumns' => $visibleColumns, 195 | ]); 196 | } 197 | ``` 198 | 199 | Adding your search model: 200 | 201 | ```php 202 | public function gridColumnsList() 203 | { 204 | return [ 205 | 'attribute1' => 'Label of attribute1', 206 | 'attribute2' => 'Label of attribute2', 207 | ]; 208 | } 209 | ``` 210 | 211 | Adding your view: 212 | 213 | ```php 214 | use bupy7\grid\GridView; 215 | 216 | $panelHeadingTemplate = <<{controls} 218 |
{pageSizer}
219 |
220 | HTML; 221 | echo GridView::widget([ 222 | 'customTags' => [ 223 | 'controls' => $this->render('_controls', [ 224 | 'visibleColumns' => $visibleColumns, 225 | 'searchModel' => $searchModel, 226 | ]), 227 | ], 228 | 'panelHeadingTemplate' => $panelHeadingTemplate, 229 | 'dataProvider' => $dataProvider, 230 | 'filterModel' => $searchModel, 231 | 'visibleColumns' => $visibleColumns, 232 | 'columns' => [ 233 | ['class' => 'yii\grid\SerialColumn'], 234 | 'attribute1', 235 | 'attribute2', 236 | ], 237 | ]); 238 | ``` 239 | 240 | Adding your `_controls` view: 241 | 242 | ```php 243 | use bupy7\grid\widgets\VisibleColumnsWidget; 244 | 245 | 'example-grid', 247 | 'modalOptions' => [ 248 | 'header' => Yii::t('app', 'SELECT_COLUMNS'), 249 | 'toggleButton' => [ 250 | 'label' => Yii::t('app', 'SELECT_COLUMNS'), 251 | 'class' => 'btn btn-default', 252 | ], 253 | ], 254 | 'actionForm' => ['visible-columns'], 255 | 'submitBtnLabel' => Yii::t('app', 'APPLY'), 256 | 'columnsList' => $searchModel->gridColumnsList(), 257 | ]); ?> 258 | ``` 259 | 260 | ### Adding ability resize width of columns 261 | 262 | #### Via session 263 | 264 | Override session component: 265 | 266 | ```php 267 | use bupy7\grid\interfaces\StorageInterface; 268 | 269 | /** 270 | * @inheritdoc 271 | */ 272 | class Session extends \yii\web\Session implements StorageInterface 273 | { 274 | 275 | } 276 | ``` 277 | 278 | Adding your config of application: 279 | 280 | ```php 281 | 'components' => [ 282 | 'gridManager' => [ 283 | 'class' => 'bupy7\grid\components\Manager', 284 | 'storage' => 'session', 285 | ], 286 | ] 287 | ``` 288 | 289 | Adding your controller: 290 | 291 | ```php 292 | use bupy7\grid\actions\ResizableColumnsAction; 293 | use yii\helpers\Url; 294 | 295 | public function actions() 296 | { 297 | return parent::actions() + [ 298 | 'resizable-columns' => [ 299 | 'class' => ResizableColumnsAction::className(), 300 | 'gridId' => 'example-grid', 301 | ], 302 | ]; 303 | } 304 | 305 | public function actionIndex() 306 | { 307 | Url::remember(); 308 | 309 | $searchModel = new ExampleSearch(); 310 | $dataProvider = $searchModel->search(Yii::$app->request->queryParams); 311 | 312 | $resizableColumns = Yii::$app->gridManager->getResizableColumns('example-grid'); 313 | 314 | return $this->render('index', [ 315 | 'searchModel' => $searchModel, 316 | 'dataProvider' => $dataProvider, 317 | 'resizableColumns' => $resizableColumns, 318 | 'resizableColumnsUrl' => ['resizable-columns'], 319 | ]); 320 | } 321 | ``` 322 | 323 | Adding your view: 324 | 325 | ```php 326 | use bupy7\grid\GridView; 327 | 328 | echo GridView::widget([ 329 | 'resizableColumns' => $resizableColumns, 330 | 'resizableColumnsUrl' => $resizableColumnsUrl, 331 | 'dataProvider' => $dataProvider, 332 | 'filterModel' => $searchModel, 333 | 'columns' => [ 334 | ['class' => 'yii\grid\SerialColumn'], 335 | 'attribute1', 336 | 'attribute2', 337 | ], 338 | ]); 339 | ``` 340 | 341 | 342 | ### Adding ability display all rows of grid 343 | 344 | ```php 345 | echo GridView::widget([ 346 | 347 | ... 348 | 349 | 'pageSizer' => [ 350 | 'availableSizes' => [20 => '20', 50 => '50', 100 => '100', -1 => Yii::t('app', 'ALL_PAGES')], 351 | ], 352 | 353 | ... 354 | ]); 355 | ``` 356 | 357 | And change `$pageSizeLimit` in your model: 358 | 359 | ```php 360 | $dataProvider = new ActiveDataProvider([ 361 | 'query' => $query, 362 | 'pagination' => [ 363 | 'pageSizeLimit' => [-1, 100], 364 | ], 365 | ]); 366 | ``` 367 | 368 | More information to `bupy7\grid\LinkPageSizer`. 369 | 370 | ### Adding total column of grid 371 | 372 | Added sum total: 373 | 374 | ```php 375 | [ 376 | 'class' => 'bupy7\grid\TotalColumn', 377 | 'format' => 'currency', 378 | 'attribute' => 'total_cost', 379 | ] 380 | ``` 381 | 382 | More information to `bupy7\grid\TotalColumn`. 383 | 384 | -------------------------------------------------------------------------------- 385 | 386 | More information about `GridView` to `bupy7\grid\GridView`. 387 | 388 | ### Keeping last modified the page sizer 389 | 390 | By default this function disabled. 391 | 392 | ```php 393 | echo GridView::widget([ 394 | ... 395 | 396 | 'id' => 'unique-id-of-this-grid', 397 | 'keepPageSizer' => true, 398 | 399 | ... 400 | ]); 401 | ``` 402 | 403 | You need to specify `id` as unique for correct work. 404 | 405 | > `id` and `options[id]` - not the same! `id` it identificator of widget, and 406 | `options[id]` it HTML-identificator of grid view. 407 | 408 | ##License 409 | 410 | yii2-grid is released under the BSD 3-Clause License. 411 | -------------------------------------------------------------------------------- /TotalColumn.php: -------------------------------------------------------------------------------- 1 | grid->showFooter = true; 79 | parent::init(); 80 | } 81 | 82 | /** 83 | * @inheritdoc 84 | */ 85 | public function getDataCellValue($model, $key, $index) 86 | { 87 | if (isset($this->realValue)) { 88 | if (is_string($this->realValue)) { 89 | $this->_data[] = ArrayHelper::getValue($model, $this->realValue); 90 | } else { 91 | $this->_data[] = call_user_func($this->realValue, $model, $key, $index, $this); 92 | } 93 | } else { 94 | $this->_data[] = parent::getDataCellValue($model, $key, $index); 95 | } 96 | $this->_models[] = $model; 97 | return parent::getDataCellValue($model, $key, $index); 98 | } 99 | 100 | /** 101 | * @inheritdoc 102 | */ 103 | protected function renderFooterCellContent() 104 | { 105 | $footer = $this->calculateSummary(); 106 | return trim($footer) !== '' ? $this->formattingFooter($footer) : $this->grid->emptyCell; 107 | } 108 | 109 | /** 110 | * Calculates the summary of an input data based on page summary aggregration function. 111 | * @return mixed 112 | * @throws InvalidConfigException 113 | */ 114 | protected function calculateSummary() 115 | { 116 | if (empty($this->_data)) { 117 | return ''; 118 | } 119 | $formula = $this->footer; 120 | if ($formula instanceof Closure) { 121 | $result = call_user_func($this->footer, $this->_data, $this->_models); 122 | } else { 123 | switch ($formula) { 124 | case self::FORMULA_SUM: 125 | $result = array_sum($this->_data); 126 | break; 127 | 128 | case self::FORMULA_COUNT: 129 | $result = count($this->_data); 130 | break; 131 | 132 | case self::FORMULA_AVG: 133 | $result = count($this->_data) > 0 ? array_sum($this->_data) / count($$this->_data) : null; 134 | break; 135 | 136 | case self::FORMULA_MAX: 137 | $result = max($this->_data); 138 | break; 139 | 140 | case self::FORMULA_MIN: 141 | $result = min($this->_data); 142 | break; 143 | 144 | default: 145 | throw new InvalidConfigException('There is invalid value `$footer`.'); 146 | } 147 | } 148 | return $result; 149 | } 150 | 151 | /** 152 | * Formatting footer value uses Formatter component or an anonymous function. 153 | * @param string $value 154 | * @return mixed 155 | * @since 1.1.5 156 | * @throws InvalidConfigException 157 | */ 158 | protected function formattingFooter($value) 159 | { 160 | if (is_string($this->footerFormat) || is_array($this->footerFormat)) { 161 | return $this->grid->formatter->format($value, $this->footerFormat); 162 | } elseif ($this->footerFormat instanceof Closure) { 163 | return call_user_func($this->footerFormat, $value); 164 | } else { 165 | throw new InvalidConfigException('There is invalid value `$footerFormat`.'); 166 | } 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /actions/ResizableColumnsAction.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 1.1.3 12 | */ 13 | class ResizableColumnsAction extends BaseAction 14 | { 15 | /** 16 | * Saving settings of resizable column. 17 | * @return mixed 18 | */ 19 | public function run() 20 | { 21 | $bodyParams = Yii::$app->request->getBodyParams(); 22 | $resizableColumns = $this->gridManager->getResizableColumns($this->gridId); 23 | foreach ($bodyParams as $attribute => $width) { 24 | if (is_string($attribute)) { 25 | $resizableColumns[$attribute] = $width; 26 | } 27 | } 28 | $this->gridManager->setResizableColumns($this->gridId, $resizableColumns); 29 | } 30 | } -------------------------------------------------------------------------------- /actions/VisibleColumnsAction.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 1.0.0 14 | */ 15 | class VisibleColumnsAction extends BaseAction 16 | { 17 | /** 18 | * @var mixed URL of redirect. If this property not set, will be used goBack(). 19 | */ 20 | public $redirectUrl; 21 | 22 | /** 23 | * @inheritdoc 24 | */ 25 | public function init() 26 | { 27 | parent::init(); 28 | if (!isset($this->redirectUrl)) { 29 | $this->redirectUrl = Url::previous(); 30 | } 31 | } 32 | 33 | /** 34 | * Saving settings of visible columns. If body params is invalid - redirect. 35 | * @return mixed 36 | */ 37 | public function run() 38 | { 39 | $params = $this->getBodyParams(); 40 | if (empty($params)) { 41 | return $this->controller->redirect($this->redirectUrl); 42 | } 43 | $this->saveSettings($params); 44 | return $this->controller->redirect($this->redirectUrl); 45 | } 46 | 47 | /** 48 | * Save settings of visible columns to storage of user. 49 | * @param array $params Body params of request. 50 | */ 51 | protected function saveSettings($params) 52 | { 53 | $visibleColumns = []; 54 | foreach ($params['columns'] as $column) { 55 | if (is_string($column)) { 56 | $visibleColumns[] = $column; 57 | } 58 | } 59 | $this->gridManager->setVisibleColumns($this->gridId, $visibleColumns); 60 | } 61 | 62 | /** 63 | * Returned body params of request. 64 | * @return array 65 | */ 66 | protected function getBodyParams() 67 | { 68 | $params = Yii::$app->request->getBodyParams(); 69 | if (empty($params['columns'])) { 70 | return []; 71 | } 72 | return $params; 73 | } 74 | } -------------------------------------------------------------------------------- /assets/GridViewAsset.php: -------------------------------------------------------------------------------- 1 | 12 | * @since 1.1.3 13 | */ 14 | class GridViewAsset extends AssetBundle 15 | { 16 | /** 17 | * @inheritdoc 18 | */ 19 | public $sourcePath = '@bupy7/grid/resources'; 20 | public $js = []; 21 | /** 22 | * @inheritdoc 23 | */ 24 | public $css = []; 25 | /** 26 | * @inheritdoc 27 | */ 28 | public $depends = [ 29 | 'yii\grid\GridViewAsset', 30 | ]; 31 | 32 | /** 33 | * @inheritdoc 34 | */ 35 | public function init() 36 | { 37 | parent::init(); 38 | $this->css[] = 'css/resizable-columns' . (!YII_DEBUG ? '.min' : '') . '.css'; 39 | $this->js[] = 'js/resizable-columns' . (!YII_DEBUG ? '.min' : '') . '.js'; 40 | } 41 | } -------------------------------------------------------------------------------- /assets/SortableAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @see https://github.com/RubaXa/Sortable 12 | * @since 1.1.1 13 | */ 14 | class SortableAsset extends AssetBundle 15 | { 16 | /** 17 | * @inheritdoc 18 | */ 19 | public $sourcePath = '@npm/sortablejs'; 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $js = [ 24 | 'Sortable.min.js', 25 | 'jquery.binding.js', 26 | ]; 27 | /** 28 | * @inheritdoc 29 | */ 30 | public $depends = [ 31 | 'yii\web\JqueryAsset', 32 | ]; 33 | } 34 | -------------------------------------------------------------------------------- /base/BaseAction.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 1.1.3 15 | */ 16 | class BaseAction extends Action 17 | { 18 | /** 19 | * @var mixed Uniqal ID of grid. You can uses not only string, but also other types of variable. 20 | * Example: 21 | * ~~~ 22 | * 'main-grid' 23 | * ~~~ 24 | */ 25 | public $gridId; 26 | /** 27 | * @var array|string|ManagerInterface the grid settings used for set/get actual visible columns of $gridId. 28 | * @since 1.1.0 29 | */ 30 | public $gridManager = 'gridManager'; 31 | 32 | /** 33 | * @inheritdoc 34 | * @throws InvalidConfigException 35 | */ 36 | public function init() 37 | { 38 | parent::init(); 39 | if (empty($this->gridId) || empty($this->gridManager)) { 40 | throw new InvalidConfigException('Property "gridId" and "gridManager" must be specified.'); 41 | } 42 | $this->gridManager = Instance::ensure($this->gridManager, 'bupy7\grid\interfaces\ManagerInterface'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /components/BaseManager.php: -------------------------------------------------------------------------------- 1 | 13 | * @since 1.1.0 14 | */ 15 | abstract class BaseManager extends Component implements ManagerInterface 16 | { 17 | /** 18 | * @var string|array|StorageInterface A place to store the setting and follow-up with this. 19 | * You can pointer name of component, configuration array or instance of which implement from StorageInterface. 20 | */ 21 | public $storage; 22 | 23 | /** 24 | * @inheritdoc 25 | */ 26 | public function init() 27 | { 28 | parent::init(); 29 | $this->storage = Instance::ensure($this->storage, 'bupy7\grid\interfaces\StorageInterface'); 30 | } 31 | 32 | /** 33 | * Generation and returned storage key for indentified the data. 34 | * @param mixed $name Name of storage data. 35 | * @return mixed 36 | */ 37 | public function getStorageKey($name) 38 | { 39 | return md5(serialize([__CLASS__, $name])); 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /components/Manager.php: -------------------------------------------------------------------------------- 1 | 8 | * @since 1.1.0 9 | */ 10 | class Manager extends BaseManager 11 | { 12 | /** 13 | * @inheritdoc 14 | */ 15 | public function setVisibleColumns($gridId, $columns) 16 | { 17 | $key = $this->getStorageKey([$gridId, 'visible-columns']); 18 | $this->storage->set($key, serialize($columns)); 19 | } 20 | 21 | /** 22 | * @inheritdoc 23 | */ 24 | public function getVisibleColumns($gridId) 25 | { 26 | $key = $this->getStorageKey([$gridId, 'visible-columns']); 27 | if ($this->storage->has($key)) { 28 | $columns = @unserialize($this->storage->get($key)); 29 | if (!is_array($columns)) { 30 | return false; 31 | } 32 | return $columns; 33 | } 34 | return false; 35 | } 36 | 37 | /** 38 | * @inheritdoc 39 | */ 40 | public function setResizableColumns($gridId, $columns) 41 | { 42 | $key = $this->getStorageKey([$gridId, 'resizable-columns']); 43 | $this->storage->set($key, serialize($columns)); 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function getResizableColumns($gridId) 50 | { 51 | $key = $this->getStorageKey([$gridId, 'resizable-columns']); 52 | if ($this->storage->has($key)) { 53 | $columns = @unserialize($this->storage->get($key)); 54 | if (!is_array($columns)) { 55 | return []; 56 | } 57 | return $columns; 58 | } 59 | return []; 60 | } 61 | 62 | /** 63 | * @inheritdoc 64 | */ 65 | public function setDefaultPageSize($gridId, $pageSize) 66 | { 67 | $key = $this->getStorageKey([$gridId, 'default-page-size']); 68 | $this->storage->set($key, serialize($pageSize)); 69 | } 70 | 71 | /** 72 | * @inheritdoc 73 | */ 74 | public function getDefaultPageSize($gridId) 75 | { 76 | $key = $this->getStorageKey([$gridId, 'default-page-size']); 77 | if ($this->storage->has($key)) { 78 | return @unserialize($this->storage->get($key)); 79 | } 80 | return false; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bupy7/yii2-grid", 3 | "description": "Simple extended `yii\\grid\\GridView` with buns.", 4 | "type": "yii2-extension", 5 | "keywords": ["yii2","extension","module","grid","grid view"], 6 | "license": "BSD-3-Clause", 7 | "version": "1.1.6", 8 | "authors": [ 9 | { 10 | "name": "Vasilij Belosludcev", 11 | "email": "bupy765@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "yiisoft/yii2": "*", 16 | "yiisoft/yii2-bootstrap": "*", 17 | "npm-asset/sortablejs": "1.4.2" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "bupy7\\grid\\": "" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bupy7/yii2-grid/343ee8f9bab48bdd7e6087b5020cc18d0c8ed46c/docs/screenshot1.png -------------------------------------------------------------------------------- /docs/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bupy7/yii2-grid/343ee8f9bab48bdd7e6087b5020cc18d0c8ed46c/docs/screenshot2.png -------------------------------------------------------------------------------- /docs/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bupy7/yii2-grid/343ee8f9bab48bdd7e6087b5020cc18d0c8ed46c/docs/screenshot3.png -------------------------------------------------------------------------------- /interfaces/ManagerInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * @since 1.1.0 8 | */ 9 | interface ManagerInterface 10 | { 11 | /** 12 | * Save visible columns settings. 13 | * @param mixed $gridId ID of grid. 14 | * @param array $columns List of columns. 15 | */ 16 | public function setVisibleColumns($gridId, $columns); 17 | 18 | /** 19 | * Returned visible columns of grid. 20 | * @param mixed $gridId ID of grid. 21 | * @return false|array 22 | */ 23 | public function getVisibleColumns($gridId); 24 | 25 | /** 26 | * Save resizable columns settings. 27 | * @param mixed $gridId ID of grid. 28 | * @param array $columns List of columns. 29 | * @since 1.1.3 30 | */ 31 | public function setResizableColumns($gridId, $columns); 32 | 33 | /** 34 | * Returned resizable columns of grid. 35 | * @param mixed $gridId ID of grid. 36 | * @return array 37 | * @since 1.1.3 38 | */ 39 | public function getResizableColumns($gridId); 40 | 41 | /** 42 | * Save default page size per page. 43 | * @param mixed $gridId ID of grid. 44 | * @param array $pageSize Default page size. 45 | * @since 1.1.4 46 | */ 47 | public function setDefaultPageSize($gridId, $pageSize); 48 | 49 | /** 50 | * Returned default page size per page. 51 | * @param mixed $gridId ID of grid. 52 | * @return false|integer 53 | * @since 1.1.4 54 | */ 55 | public function getDefaultPageSize($gridId); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /interfaces/StorageInterface.php: -------------------------------------------------------------------------------- 1 | 8 | * @since 1.1.0 9 | */ 10 | interface StorageInterface 11 | { 12 | /** 13 | * Adds a storage data. 14 | * If the specified name already exists, the old value will be overwritten. 15 | * @param string $key Storage data name. 16 | * @param mixed $value Storage data value. 17 | */ 18 | public function set($key, $value); 19 | 20 | /** 21 | * Returns the storage data value with the storage data name. 22 | * If the storage data does not exist, the `$defaultValue` will be returned. 23 | * @param string $key The storage data name. 24 | * @param mixed $defaultValue The default value to be returned when the storage data does not exist. 25 | * @return mixed The storage data value, or $defaultValue if the storage data does not exist. 26 | */ 27 | public function get($key, $defaultValue = null); 28 | 29 | /** 30 | * @param mixed $key Storage data name. 31 | * @return boolean Whether the name storage data is exists. 32 | */ 33 | public function has($key); 34 | 35 | /** 36 | * Removes a storage data. 37 | * @param string $key The name of the storage data to be removed. 38 | * @return mixed The removed value, null if no such storage data. 39 | */ 40 | public function remove($key); 41 | } -------------------------------------------------------------------------------- /resources/css/resizable-columns.css: -------------------------------------------------------------------------------- 1 | body.resizing-columns { 2 | -webkit-user-select:none; 3 | -moz-user-select:none; 4 | -ms-user-select:none; 5 | user-select:none 6 | } 7 | .grid-view .resizable-columns { 8 | position:relative 9 | } 10 | .grid-view .resizable-columns .resizer { 11 | position:absolute; 12 | top:0; 13 | right:-8px; 14 | bottom:0; 15 | left:auto; 16 | width:16px; 17 | cursor:col-resize; 18 | z-index:1 19 | } -------------------------------------------------------------------------------- /resources/css/resizable-columns.min.css: -------------------------------------------------------------------------------- 1 | body.resizing-columns{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.grid-view .resizable-columns{position:relative}.grid-view .resizable-columns .resizer{position:absolute;top:0;right:-8px;bottom:0;left:auto;width:16px;cursor:col-resize;z-index:1} -------------------------------------------------------------------------------- /resources/js/resizable-columns.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Resizable table columns jQuery plugin. 3 | * Options (optional): 4 | * - `selector` (string): CSS relative path to header columns of table. 5 | * @author Vasilij Belosludcev 6 | * @since 1.1.3 7 | */ 8 | 'use strict'; 9 | (function($) { 10 | // constants 11 | var EVENT_MOUSE_DOWN = 'mousedown', 12 | EVENT_MOUSE_UP = 'mouseup', 13 | EVENT_MOUSE_MOVE = 'mousemove', 14 | EVENT_AFTER_DRAGGING = 'afterDragging.rc'; 15 | // protected properties 16 | var startPos = null, 17 | $document = $(document), 18 | $body = $('body'); 19 | // public methods 20 | var methods = { 21 | init: function(options) { 22 | var $columns = this; 23 | if (typeof options.selector != 'undefined') { 24 | $columns = this.find(options.selector); 25 | } 26 | return $columns.each(function () { 27 | var $th = $(this); 28 | 29 | $th 30 | .prepend($('
').on(EVENT_MOUSE_DOWN, $th, startDragging)) 31 | .addClass('resizable-columns'); 32 | }); 33 | } 34 | }; 35 | // protected methods 36 | var startDragging = function(event) { 37 | $body.addClass('resizing-columns'); 38 | 39 | startPos = getMousePos(event); 40 | startPos.width = parseInt(event.data.width(), 10); 41 | startPos.height = parseInt(event.data.height(), 10); 42 | 43 | $document.on(EVENT_MOUSE_MOVE, event.data, doDrag); 44 | $document.on(EVENT_MOUSE_UP, event.data, stopDragging); 45 | }, 46 | doDrag = function(event) { 47 | var pos = getMousePos(event), 48 | newWidth = startPos.width + pos.x - startPos.x; 49 | 50 | event.data.css({ 51 | width: newWidth, 52 | minWidth: newWidth 53 | }); 54 | }, 55 | stopDragging = function(event) { 56 | noop(event); 57 | 58 | $document.off(EVENT_MOUSE_MOVE); 59 | $document.off(EVENT_MOUSE_UP); 60 | 61 | $body.removeClass('resizing-columns'); 62 | 63 | event.data.trigger(EVENT_AFTER_DRAGGING); 64 | }, 65 | getMousePos = function(event) { 66 | var pos = { 67 | x: 0, 68 | y: 0, 69 | width: 0, 70 | height: 0 71 | }; 72 | 73 | if (typeof event.clientX === 'number') { 74 | pos.x = event.clientX; 75 | pos.y = event.clientY; 76 | } else if (event.originalEvent.touches) { 77 | pos.x = event.originalEvent.touches[0].clientX; 78 | pos.y = event.originalEvent.touches[0].clientY; 79 | } else { 80 | return null; 81 | } 82 | 83 | return pos; 84 | }, 85 | noop = function(event) { 86 | event.stopPropagation(); 87 | event.preventDefault(); 88 | }; 89 | 90 | $.fn.resizableColumns = function(options) { 91 | if (methods[options]) { 92 | return methods[options].apply(this, Array.prototype.slice.call(arguments, 1)); 93 | } else if (typeof options === 'object' || ! options) { 94 | return methods.init.apply(this, arguments); 95 | } else { 96 | $.error('Method "' + options + '" not exists.'); 97 | } 98 | }; 99 | })(jQuery); -------------------------------------------------------------------------------- /resources/js/resizable-columns.min.js: -------------------------------------------------------------------------------- 1 | "use strict";(function(f){var g="mousedown",h="mouseup",m="mousemove",j="afterDragging.rc";var i=null,d=f(document),e=f("body");var b={init:function(p){var o=this;if(typeof p.selector!="undefined"){o=this.find(p.selector)}return o.each(function(){var q=f(this);q.prepend(f('
').on(g,q,k)).addClass("resizable-columns")})}};var k=function(o){e.addClass("resizing-columns");i=c(o);i.width=parseInt(o.data.width(),10);i.height=parseInt(o.data.height(),10);d.on(m,o.data,l);d.on(h,o.data,a)},l=function(p){var q=c(p),o=i.width+q.x-i.x;p.data.css({width:o,minWidth:o})},a=function(o){n(o);d.off(m);d.off(h);e.removeClass("resizing-columns");o.data.trigger(j)},c=function(o){var p={x:0,y:0,width:0,height:0};if(typeof o.clientX==="number"){p.x=o.clientX;p.y=o.clientY}else{if(o.originalEvent.touches){p.x=o.originalEvent.touches[0].clientX;p.y=o.originalEvent.touches[0].clientY}else{return null}}return p},n=function(o){o.stopPropagation();o.preventDefault()};f.fn.resizableColumns=function(o){if(b[o]){return b[o].apply(this,Array.prototype.slice.call(arguments,1))}else{if(typeof o==="object"||!o){return b.init.apply(this,arguments)}else{f.error('Method "'+o+'" not exists.')}}}})(jQuery); -------------------------------------------------------------------------------- /widgets/VisibleColumnsWidget.php: -------------------------------------------------------------------------------- 1 | 17 | * @since 1.0.0 18 | */ 19 | class VisibleColumnsWidget extends Widget 20 | { 21 | /** 22 | * @var mixed Uniqal ID of grid. You can uses not only string, but also other types of variable. 23 | * Example: 24 | * ~~~ 25 | * 'main-grid' 26 | * ~~~ 27 | */ 28 | public $gridId; 29 | /** 30 | * @var array|string|ManagerInterface the grid settings used for set/get actual visible columns of $gridId. 31 | * @since 1.1.0 32 | */ 33 | public $gridManager = 'gridManager'; 34 | /** 35 | * @var array Modal window widget options. 36 | * @see \yii\bootstrap\Modal 37 | */ 38 | public $modalOptions = []; 39 | /** 40 | * @var string|array Action URL of form. 41 | * @see Html::beginForm() 42 | */ 43 | public $actionForm = ''; 44 | /** 45 | * @var string Method of form. 46 | * @see Html::beginForm() 47 | */ 48 | public $methodForm = 'post'; 49 | /** 50 | * @var array Options of form. 51 | * @see Html::beginForm() 52 | */ 53 | public $formOptions = []; 54 | /** 55 | * @var array List of available columns in grid with labels: 56 | * Example: 57 | * [ 58 | * 'attribute1' => 'Label of attribute1', 59 | * 'attribute2' => 'Label of attribute2', 60 | * ] 61 | */ 62 | public $columnsList = []; 63 | /** 64 | * @var string Label of submit button. 65 | * @see Html::submitButton() 66 | */ 67 | public $submitBtnLabel = 'Apply'; 68 | /** 69 | * @var array Options of submit button. 70 | * @see Html::submitButton() 71 | */ 72 | public $submitBtnOptions = ['class' => 'btn btn-primary']; 73 | /** 74 | * @var array Options of Sortable plugin. 75 | * @see https://github.com/RubaXa/Sortable 76 | * @since 1.1.1 77 | */ 78 | public $pluginSortableOptions = []; 79 | 80 | /** 81 | * @inheritdoc 82 | * @throws InvalidConfigException 83 | */ 84 | public function init() 85 | { 86 | parent::init(); 87 | if (empty($this->gridId) || empty($this->gridManager)) { 88 | throw new InvalidConfigException('Property "gridId" and "gridManager" must be specified.'); 89 | } 90 | $this->gridManager = Instance::ensure($this->gridManager, 'bupy7\grid\interfaces\ManagerInterface'); 91 | SortableAsset::register($this->view); 92 | } 93 | 94 | /** 95 | * Display modal window with form for selecting visible columns. 96 | */ 97 | public function run() 98 | { 99 | $visibleColumns = $this->gridManager->getVisibleColumns($this->gridId); 100 | if ($visibleColumns === false) { 101 | $visibleColumns = array_keys($this->columnsList); 102 | } 103 | $columnsList = []; 104 | for ($i = 0; $i != count($visibleColumns); $i++) { 105 | $key = $visibleColumns[$i]; 106 | if (isset($this->columnsList[$key])) { 107 | $columnsList[$key] = $this->columnsList[$key]; 108 | unset($this->columnsList[$key]); 109 | } 110 | } 111 | $columnsList = array_merge($columnsList, $this->columnsList); 112 | $modal = Modal::begin($this->modalOptions); 113 | echo Html::beginForm($this->actionForm, $this->methodForm, $this->formOptions); 114 | echo Html::checkboxList('columns', $visibleColumns, $columnsList, ['class' => 'checkbox columns-list']); 115 | echo Html::beginTag('div', ['class' => 'form-group']); 116 | echo Html::submitButton($this->submitBtnLabel, $this->submitBtnOptions); 117 | echo Html::endTag('div'); 118 | echo Html::endForm(); 119 | Modal::end(); 120 | $pluginSortableOptions = Json::encode($this->pluginSortableOptions); 121 | $this->view->registerJs("jQuery('#{$modal->id} .columns-list').sortable({$pluginSortableOptions});"); 122 | } 123 | } 124 | 125 | --------------------------------------------------------------------------------