diff --git a/README.md b/README.md index 19359a4..d47a94b 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,31 @@ This project is here for super-simple demonstration how to create a project with https://examples.contributte.org/datagrid-skeleton/ +## Screenshots + +| Page | Static | Interactive | +|------|--------|-------------| +| Home | | | +| Filters | | | +| Outer Filters | | | +| Columns | | | +| Actions | | | +| Group Actions | | | +| Row | | | +| ItemDetail | | | +| Export | | | +| TreeView | | | +| Edit | | | +| Add | | | +| Localization | | | +| CDN | | | +| No Pagination | | | +| Sorting | | | +| Columns Summary | | | +| Array Datasource | | | +| State Storage | | | +| Events | | | + ## Installation To install latest version of `contributte/datagrid-skeleton` use [Composer](https://getcomposer.org). diff --git a/app/UI/@Templates/@layout.latte b/app/UI/@Templates/@layout.latte index 38d5967..6ddca24 100644 --- a/app/UI/@Templates/@layout.latte +++ b/app/UI/@Templates/@layout.latte @@ -66,6 +66,11 @@
  • Localization
  • CDN
  • No Pagination
  • +
  • Sorting
  • +
  • Columns Summary
  • +
  • Array Datasource
  • +
  • State Storage
  • +
  • Events
  • diff --git a/app/UI/Actions/ActionsPresenter.php b/app/UI/Actions/ActionsPresenter.php index 9d9eee5..045d2eb 100644 --- a/app/UI/Actions/ActionsPresenter.php +++ b/app/UI/Actions/ActionsPresenter.php @@ -3,6 +3,7 @@ namespace App\UI\Actions; use App\UI\AbstractPresenter; +use Contributte\Datagrid\Column\Action\Confirmation\CallbackConfirmation; use Contributte\Datagrid\Column\Action\Confirmation\StringConfirmation; use Contributte\Datagrid\Datagrid; @@ -61,6 +62,18 @@ public function createComponentGrid(): Datagrid new StringConfirmation('Do you really want to delete example %s?', 'name') ); + $grid->addAction('archive', '', 'archive!') + ->setIcon('box-archive') + ->setTitle('Archive') + ->setClass('btn btn-xs btn-warning ajax') + ->setConfirmation( + new CallbackConfirmation( + function ($item): string { + return 'Do you really want to archive "' . $item['name'] . '" (ID: ' . $item['id'] . ')?'; + } + ) + ); + $grid->addToolbarButton('this', 'Toolbar')->addAttributes(['foo' => 'bar']); $grid->addToolbarButton('this#2', 'Button', ['foo' => 'bar']); @@ -85,4 +98,10 @@ public function handleDelete(): void $this->redrawControl('flashes'); } + public function handleArchive(): void + { + $this->flashMessage('Archived!', 'info'); + $this->redrawControl('flashes'); + } + } diff --git a/app/UI/ArrayDatasource/ArrayDatasourcePresenter.php b/app/UI/ArrayDatasource/ArrayDatasourcePresenter.php new file mode 100644 index 0000000..570d6e2 --- /dev/null +++ b/app/UI/ArrayDatasource/ArrayDatasourcePresenter.php @@ -0,0 +1,63 @@ + 1, 'name' => 'John Doe', 'email' => 'john@example.com', 'age' => 32, 'role' => 'admin'], + ['id' => 2, 'name' => 'Jane Smith', 'email' => 'jane@example.com', 'age' => 28, 'role' => 'user'], + ['id' => 3, 'name' => 'Bob Johnson', 'email' => 'bob@example.com', 'age' => 45, 'role' => 'editor'], + ['id' => 4, 'name' => 'Alice Brown', 'email' => 'alice@example.com', 'age' => 35, 'role' => 'admin'], + ['id' => 5, 'name' => 'Charlie Wilson', 'email' => 'charlie@example.com', 'age' => 22, 'role' => 'user'], + ['id' => 6, 'name' => 'Diana Prince', 'email' => 'diana@example.com', 'age' => 30, 'role' => 'editor'], + ['id' => 7, 'name' => 'Edward Norton', 'email' => 'edward@example.com', 'age' => 41, 'role' => 'user'], + ['id' => 8, 'name' => 'Fiona Apple', 'email' => 'fiona@example.com', 'age' => 38, 'role' => 'admin'], + ['id' => 9, 'name' => 'George Lucas', 'email' => 'george@example.com', 'age' => 55, 'role' => 'user'], + ['id' => 10, 'name' => 'Hannah Montana', 'email' => 'hannah@example.com', 'age' => 19, 'role' => 'user'], + ]; + + $grid->setDataSource($data); + + $grid->setItemsPerPageList([5, 10, 20], true); + + // Default per page + $grid->setDefaultPerPage(5); + + $grid->addColumnNumber('id', 'Id') + ->setAlign('start') + ->setSortable(); + + $grid->addColumnText('name', 'Name') + ->setSortable() + ->setFilterText(); + + $grid->addColumnText('email', 'E-mail') + ->setSortable() + ->setFilterText(); + + $grid->addColumnNumber('age', 'Age') + ->setSortable(); + + $grid->addColumnText('role', 'Role') + ->setSortable() + ->setFilterSelect([ + '' => 'All', + 'admin' => 'Admin', + 'user' => 'User', + 'editor' => 'Editor', + ]); + + return $grid; + } + +} diff --git a/app/UI/ArrayDatasource/Templates/default.latte b/app/UI/ArrayDatasource/Templates/default.latte new file mode 100644 index 0000000..ec3ba3a --- /dev/null +++ b/app/UI/ArrayDatasource/Templates/default.latte @@ -0,0 +1,3 @@ +{block content} + {control grid} +{/block} diff --git a/app/UI/Columns/ColumnsPresenter.php b/app/UI/Columns/ColumnsPresenter.php index f4f51d0..d27d811 100644 --- a/app/UI/Columns/ColumnsPresenter.php +++ b/app/UI/Columns/ColumnsPresenter.php @@ -36,6 +36,7 @@ public function createComponentGrid(): Datagrid ->setSortable(); $grid->addColumnLink('email', 'E-mail', 'this') + ->setOpenInNewTab() ->setSortable(); $columnStatus = $grid->addColumnStatus('status', 'Status'); @@ -59,11 +60,17 @@ public function createComponentGrid(): Datagrid ->setFormat('j. n. Y') ->setSortable(); + $grid->addColumnNumber('countries_visited', 'Countries Visited') + ->setFormat(0, '.', ',') + ->setSortable(); + $grid->addColumnNumber('age', 'Age') ->setRenderer(fn (Row $row): ?int => DateTime::fromSafe($row->asDateTime('birth_date'))?->diff(new DateTime())->y); $grid->setColumnsHideable(); + $grid->setColumnsOrder(['id', 'email', 'status', 'countries_visited', 'emojis', 'birth_date', 'age']); + $grid->addColumnCallback('status', function (ColumnStatus $column, Row $row): void { if ($row['id'] === 3) { $column->removeOption('active'); diff --git a/app/UI/ColumnsSummary/ColumnsSummaryPresenter.php b/app/UI/ColumnsSummary/ColumnsSummaryPresenter.php new file mode 100644 index 0000000..017e882 --- /dev/null +++ b/app/UI/ColumnsSummary/ColumnsSummaryPresenter.php @@ -0,0 +1,41 @@ +setDataSource($this->dibiConnection->select('*')->from('users')); + + $grid->setItemsPerPageList([20, 50, 100], true); + + $grid->addColumnNumber('id', 'Id') + ->setAlign('start') + ->setSortable(); + + $grid->addColumnText('name', 'Name') + ->setSortable() + ->setFilterText(); + + $grid->addColumnNumber('countries_visited', 'Countries Visited') + ->setFormat(0, '.', ',') + ->setSortable(); + + $grid->addColumnText('status', 'Status'); + + // Columns summary - shows sum in footer row for specified columns + $columnsSummary = $grid->setColumnsSummary(['id', 'countries_visited']); + $columnsSummary->setFormat('id', 0, '.', ' '); + $columnsSummary->setFormat('countries_visited', 0, '.', ','); + + return $grid; + } + +} diff --git a/app/UI/ColumnsSummary/Templates/default.latte b/app/UI/ColumnsSummary/Templates/default.latte new file mode 100644 index 0000000..ec3ba3a --- /dev/null +++ b/app/UI/ColumnsSummary/Templates/default.latte @@ -0,0 +1,3 @@ +{block content} + {control grid} +{/block} diff --git a/app/UI/Edit/EditPresenter.php b/app/UI/Edit/EditPresenter.php index 162ceda..5965eca 100644 --- a/app/UI/Edit/EditPresenter.php +++ b/app/UI/Edit/EditPresenter.php @@ -84,8 +84,14 @@ public function createComponentGrid(): Datagrid $this->redrawControl('flashes'); }; + $inlineEdit->onCustomRedraw[] = function (): void { + $this['grid']->redrawControl(); + }; + $inlineEdit->setShowNonEditingColumns(); + $grid->allowRowsInlineEdit(fn ($item): bool => $item['id'] % 2 === 0); + return $grid; } diff --git a/app/UI/Events/EventsPresenter.php b/app/UI/Events/EventsPresenter.php new file mode 100644 index 0000000..3a9df74 --- /dev/null +++ b/app/UI/Events/EventsPresenter.php @@ -0,0 +1,66 @@ +setDataSource($this->dibiConnection->select('*')->from('users')); + + $grid->setItemsPerPageList([20, 50, 100], true); + + $grid->addColumnText('id', 'Id') + ->setSortable(); + + $grid->addColumnText('email', 'E-mail') + ->setSortable() + ->setFilterText(); + + $grid->addColumnText('name', 'Name') + ->setSortable() + ->setFilterText(); + + $grid->addColumnText('status', 'Status'); + + $grid->setColumnsHideable(); + + // Event: onRedraw - triggered when grid is redrawn + $grid->onRedraw[] = function () use ($grid): void { + $grid->getPresenter()->flashMessage('Event: onRedraw triggered', 'info'); + }; + + // Event: onRender - triggered when grid is rendered + $grid->onRender[] = function (Datagrid $grid): void { + // Useful for modifying grid state before rendering + }; + + // Event: onColumnAdd - triggered when a column is added + $grid->onColumnAdd[] = function (string $key, $column): void { + // Useful for modifying columns after they are added + }; + + // Event: onFiltersAssembled - triggered when filters are assembled + $grid->onFiltersAssembled[] = function (array $filters): void { + // Useful for modifying filters before they are applied + }; + + // Events: onColumnShow / onColumnHide - triggered when columns visibility changes + $grid->onColumnShow[] = function (string $column): void { + // Useful for tracking column visibility + }; + + $grid->onColumnHide[] = function (string $column): void { + // Useful for tracking column visibility + }; + + return $grid; + } + +} diff --git a/app/UI/Events/Templates/default.latte b/app/UI/Events/Templates/default.latte new file mode 100644 index 0000000..ec3ba3a --- /dev/null +++ b/app/UI/Events/Templates/default.latte @@ -0,0 +1,3 @@ +{block content} + {control grid} +{/block} diff --git a/app/UI/Export/ExportPresenter.php b/app/UI/Export/ExportPresenter.php index a382b82..495f854 100644 --- a/app/UI/Export/ExportPresenter.php +++ b/app/UI/Export/ExportPresenter.php @@ -35,7 +35,7 @@ public function createComponentGrid(): Datagrid die; })->setAjax(); - $grid->addExportCsvFiltered('Csv export (filtered)', 'examples.csv') + $grid->addExportCsvFiltered('Csv export (filtered)', 'examples.csv', 'windows-1250', ',', true) ->setTitle('Csv export (filtered)'); $columnName = new ColumnText($grid, 'name', 'name', 'Name'); @@ -44,13 +44,15 @@ public function createComponentGrid(): Datagrid fn ($item) => $item['id'] % 2 === 0 ? 'No' : 'Yes' ); - $grid->addExportCsv('Csv export', 'examples-all.csv') + $grid->addExportCsv('Csv export', 'examples-all.csv', 'utf-8', ',', true) ->setTitle('Csv export') ->setColumns([ $columnName, $columnEven, ]); + $grid->setColumnsExportOrder(['name', 'status', 'birth_date', 'id']); + return $grid; } diff --git a/app/UI/Filters/FiltersPresenter.php b/app/UI/Filters/FiltersPresenter.php index e7b1387..d3b2d1a 100644 --- a/app/UI/Filters/FiltersPresenter.php +++ b/app/UI/Filters/FiltersPresenter.php @@ -20,12 +20,18 @@ public function createComponentGrid(): Datagrid $grid->setItemsPerPageList([20, 50, 100], true); + $grid->setAutoSubmit(false); + $grid->setColumnReset(); + $grid->setRefreshUrl(); + $grid->addColumnText('id', 'Id') ->setFilterText() ->setExactSearch(); $grid->addColumnText('name', 'Name') - ->setFilterText(); + ->setFilterText() + ->setSplitWordsSearch(true) + ->setConjunctionSearch(); $grid->addColumnStatus('status', 'Status') ->setFilterSelect([ diff --git a/app/UI/GroupActions/GroupActionsPresenter.php b/app/UI/GroupActions/GroupActionsPresenter.php index 6b17904..7c16045 100644 --- a/app/UI/GroupActions/GroupActionsPresenter.php +++ b/app/UI/GroupActions/GroupActionsPresenter.php @@ -16,6 +16,8 @@ public function createComponentGrid(): Datagrid $grid->setItemsPerPageList([20, 50, 100]); + $grid->setShowSelectedRowsCount(); + $grid->addColumnNumber('id', 'Id') ->setAlign('start') ->setSortable(); @@ -49,6 +51,20 @@ public function createComponentGrid(): Datagrid $grid->addGroupButtonAction('Say hello')->onClick[] = [$this, 'sayHello']; + $grid->addGroupSelectAction('Move to group', [ + 'admins' => 'Admins', + 'users' => 'Users', + 'guests' => 'Guests', + ])->onSelect[] = [$this, 'groupMoveToGroup']; + + $grid->addGroupMultiSelectAction('Assign tags', [ + 'php' => 'PHP', + 'js' => 'JavaScript', + 'css' => 'CSS', + ])->onSelect[] = [$this, 'groupAssignTags']; + + $grid->addGroupTextareaAction('Add comment')->onSelect[] = [$this, 'groupAddComment']; + return $grid; } @@ -154,4 +170,58 @@ public function sayHello(array $ids): void } } + /** + * @param mixed[] $ids + */ + public function groupMoveToGroup(array $ids, string $group): void + { + $this->flashMessage( + sprintf('Items [%s] moved to group: [%s]', implode(',', $ids), $group), + 'success' + ); + + if ($this->isAjax()) { + $this->redrawControl('flashes'); + $this['grid']->redrawControl(); + } else { + $this->redirect('this'); + } + } + + /** + * @param mixed[] $ids + */ + public function groupAssignTags(array $ids, string $tags): void + { + $this->flashMessage( + sprintf('Tags [%s] assigned to items: [%s]', $tags, implode(',', $ids)), + 'success' + ); + + if ($this->isAjax()) { + $this->redrawControl('flashes'); + $this['grid']->redrawControl(); + } else { + $this->redirect('this'); + } + } + + /** + * @param mixed[] $ids + */ + public function groupAddComment(array $ids, string $comment): void + { + $this->flashMessage( + sprintf('Comment [%s] added to items: [%s]', $comment, implode(',', $ids)), + 'success' + ); + + if ($this->isAjax()) { + $this->redrawControl('flashes'); + $this['grid']->redrawControl(); + } else { + $this->redirect('this'); + } + } + } diff --git a/app/UI/ItemDetail/ItemDetailPresenter.php b/app/UI/ItemDetail/ItemDetailPresenter.php index 1aa8fd1..b355335 100644 --- a/app/UI/ItemDetail/ItemDetailPresenter.php +++ b/app/UI/ItemDetail/ItemDetailPresenter.php @@ -4,6 +4,7 @@ use App\UI\AbstractPresenter; use Contributte\Datagrid\Datagrid; +use Nette\Utils\Html; final class ItemDetailPresenter extends AbstractPresenter { @@ -29,7 +30,20 @@ public function createComponentGrid(): Datagrid $grid->addColumnDateTime('birth_date', 'Birthday') ->setFormat('j. n. Y'); - $grid->setItemsDetail(); + $detail = $grid->setItemsDetail(); + + $detail->setTemplateParameters(['customParam' => 'Hello from template parameter!']); + + // Render condition - only show detail toggle for active users + $detail->setRenderCondition(function ($item): bool { + return $item['status'] === 'active'; + }); + + $grid->setItemsDetailForm(function ($container): void { + $container->addText('note', 'Note') + ->setRequired('Please enter a note'); + $container->addSubmit('save', 'Save note'); + }); $grid->setTemplateFile(__DIR__ . '/Templates/grid/item-detail-grid.latte'); diff --git a/app/UI/ItemDetail/Templates/grid/item-detail-grid.latte b/app/UI/ItemDetail/Templates/grid/item-detail-grid.latte index bfb8dcb..69831c3 100644 --- a/app/UI/ItemDetail/Templates/grid/item-detail-grid.latte +++ b/app/UI/ItemDetail/Templates/grid/item-detail-grid.latte @@ -1,11 +1,10 @@ {extends $originalTemplate} {block detail} -

    {$item['id']} - {$item['name']} {$item['birth_date']|date:'j. n. Y H:i:s'}

    - -

    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur vitae diam non enim vestibulum interdum. Nulla quis diam. Integer in sapien. Nullam dapibus fermentum ipsum. Curabitur bibendum justo non orci. Etiam neque. Nullam dapibus fermentum ipsum. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Etiam posuere lacus quis dolor. Aliquam ornare wisi eu metus. Nulla non lectus sed nisl molestie malesuada. Vivamus ac leo pretium faucibus. Aliquam ante. Aliquam erat volutpat.

    - -

    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur vitae diam non enim vestibulum interdum. Nulla quis diam. Integer in sapien. Nullam dapibus fermentum ipsum. Curabitur bibendum justo non orci. Etiam neque. Nullam dapibus fermentum ipsum. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Etiam posuere lacus quis dolor. Aliquam ornare wisi eu metus. Nulla non lectus sed nisl molestie malesuada. Vivamus ac leo pretium faucibus. Aliquam ante. Aliquam erat volutpat.

    - - +

    {$item['id']} - {$item['name']}

    +

    Status: {$item['status']}

    +

    Template parameter: {$customParam}

    + {if isset($frm)} + {$frm} + {/if} {/block} diff --git a/app/UI/Row/RowPresenter.php b/app/UI/Row/RowPresenter.php index f144aec..ccd78d4 100644 --- a/app/UI/Row/RowPresenter.php +++ b/app/UI/Row/RowPresenter.php @@ -50,6 +50,13 @@ public function createComponentGrid(): Datagrid $grid->allowRowsAction('detail', fn ($item): bool => $item->id % 4 === 0); + $multiAction = $grid->addMultiAction('multi', 'More') + ->addAction('view', 'View', 'this') + ->addAction('export', 'Export', 'this'); + + $grid->allowRowsMultiAction('multi', 'view', fn ($item): bool => $item->id % 2 === 0); + $grid->allowRowsMultiAction('multi', 'export', fn ($item): bool => $item->id % 3 === 0); + return $grid; } diff --git a/app/UI/Sorting/SortingPresenter.php b/app/UI/Sorting/SortingPresenter.php new file mode 100644 index 0000000..38e4337 --- /dev/null +++ b/app/UI/Sorting/SortingPresenter.php @@ -0,0 +1,46 @@ +setDataSource($this->dibiConnection->select('*')->from('users')); + + $grid->setItemsPerPageList([20, 50, 100], true); + + // Enable multi-sort - allows sorting by multiple columns simultaneously + $grid->setMultiSortEnabled(); + + $grid->addColumnText('id', 'Id') + ->setSortable() + ->setSortableResetPagination(); // Reset pagination when sorting changes + + $grid->addColumnText('email', 'E-mail') + ->setSortable() + ->setSortableResetPagination(); + + $grid->addColumnText('name', 'Name') + ->setSortable() + ->setSortableResetPagination() + ->setSortableCallback(function (Fluent $fluent, string $sort): void { + $fluent->orderBy('CHAR_LENGTH(name)', $sort); // Custom sort: by name length + }); + + $grid->addColumnText('status', 'Status') + ->setSortable(); + + $grid->setDefaultSort(['name' => 'ASC', 'id' => 'DESC']); + + return $grid; + } + +} diff --git a/app/UI/Sorting/Templates/default.latte b/app/UI/Sorting/Templates/default.latte new file mode 100644 index 0000000..ec3ba3a --- /dev/null +++ b/app/UI/Sorting/Templates/default.latte @@ -0,0 +1,3 @@ +{block content} + {control grid} +{/block} diff --git a/app/UI/StateStorage/StateStoragePresenter.php b/app/UI/StateStorage/StateStoragePresenter.php new file mode 100644 index 0000000..bbe355b --- /dev/null +++ b/app/UI/StateStorage/StateStoragePresenter.php @@ -0,0 +1,47 @@ +setDataSource($this->dibiConnection->select('*')->from('users')); + + $grid->setItemsPerPageList([20, 50, 100], true); + + // Remember grid state (filters, sorting, pagination) across requests + $grid->setRememberState(); + + // Refresh URL with current filter state (history API) + $grid->setRefreshUrl(); + + $grid->addColumnText('id', 'Id') + ->setSortable(); + + $grid->addColumnText('email', 'E-mail') + ->setSortable() + ->setFilterText(); + + $grid->addColumnText('name', 'Name') + ->setSortable() + ->setFilterText(); + + $grid->addColumnText('status', 'Status') + ->setFilterSelect([ + '' => 'All', + 'active' => 'Active', + 'inactive' => 'Inactive', + 'deleted' => 'Deleted', + ]); + + return $grid; + } + +} diff --git a/app/UI/StateStorage/Templates/default.latte b/app/UI/StateStorage/Templates/default.latte new file mode 100644 index 0000000..ec3ba3a --- /dev/null +++ b/app/UI/StateStorage/Templates/default.latte @@ -0,0 +1,3 @@ +{block content} + {control grid} +{/block} diff --git a/docker-compose.yml b/docker-compose.yml index a1f568f..66e0f31 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: depends_on: - database environment: - NETTE_DEBUG: 1 + NETTE_DEBUG: 0 database: image: mariadb:11.5 diff --git a/screenshots/00-home.png b/screenshots/00-home.png new file mode 100644 index 0000000..9fd437b Binary files /dev/null and b/screenshots/00-home.png differ diff --git a/screenshots/01-filters.png b/screenshots/01-filters.png new file mode 100644 index 0000000..44a8269 Binary files /dev/null and b/screenshots/01-filters.png differ diff --git a/screenshots/02-outer-filters-expanded.png b/screenshots/02-outer-filters-expanded.png new file mode 100644 index 0000000..4e3ccf1 Binary files /dev/null and b/screenshots/02-outer-filters-expanded.png differ diff --git a/screenshots/02-outer-filters.png b/screenshots/02-outer-filters.png new file mode 100644 index 0000000..6f3ee96 Binary files /dev/null and b/screenshots/02-outer-filters.png differ diff --git a/screenshots/03-columns-hideable.png b/screenshots/03-columns-hideable.png new file mode 100644 index 0000000..22e553c Binary files /dev/null and b/screenshots/03-columns-hideable.png differ diff --git a/screenshots/03-columns.png b/screenshots/03-columns.png new file mode 100644 index 0000000..b7d1353 Binary files /dev/null and b/screenshots/03-columns.png differ diff --git a/screenshots/04-actions-multiaction.png b/screenshots/04-actions-multiaction.png new file mode 100644 index 0000000..ba3a4b3 Binary files /dev/null and b/screenshots/04-actions-multiaction.png differ diff --git a/screenshots/04-actions.png b/screenshots/04-actions.png new file mode 100644 index 0000000..ddbb527 Binary files /dev/null and b/screenshots/04-actions.png differ diff --git a/screenshots/05-group-actions-selected.png b/screenshots/05-group-actions-selected.png new file mode 100644 index 0000000..b003b52 Binary files /dev/null and b/screenshots/05-group-actions-selected.png differ diff --git a/screenshots/05-group-actions.png b/screenshots/05-group-actions.png new file mode 100644 index 0000000..38b6684 Binary files /dev/null and b/screenshots/05-group-actions.png differ diff --git a/screenshots/06-row.png b/screenshots/06-row.png new file mode 100644 index 0000000..8ae064e Binary files /dev/null and b/screenshots/06-row.png differ diff --git a/screenshots/07-item-detail-expanded.png b/screenshots/07-item-detail-expanded.png new file mode 100644 index 0000000..d27ed78 Binary files /dev/null and b/screenshots/07-item-detail-expanded.png differ diff --git a/screenshots/07-item-detail.png b/screenshots/07-item-detail.png new file mode 100644 index 0000000..a457bc6 Binary files /dev/null and b/screenshots/07-item-detail.png differ diff --git a/screenshots/08-export.png b/screenshots/08-export.png new file mode 100644 index 0000000..6876f87 Binary files /dev/null and b/screenshots/08-export.png differ diff --git a/screenshots/09-tree-view-expanded.png b/screenshots/09-tree-view-expanded.png new file mode 100644 index 0000000..8696c8f Binary files /dev/null and b/screenshots/09-tree-view-expanded.png differ diff --git a/screenshots/09-tree-view.png b/screenshots/09-tree-view.png new file mode 100644 index 0000000..5d0da4e Binary files /dev/null and b/screenshots/09-tree-view.png differ diff --git a/screenshots/10-edit-inline.png b/screenshots/10-edit-inline.png new file mode 100644 index 0000000..e99bd4a Binary files /dev/null and b/screenshots/10-edit-inline.png differ diff --git a/screenshots/10-edit.png b/screenshots/10-edit.png new file mode 100644 index 0000000..8daae15 Binary files /dev/null and b/screenshots/10-edit.png differ diff --git a/screenshots/11-add-inline.png b/screenshots/11-add-inline.png new file mode 100644 index 0000000..9b5b24d Binary files /dev/null and b/screenshots/11-add-inline.png differ diff --git a/screenshots/11-add.png b/screenshots/11-add.png new file mode 100644 index 0000000..9b5b24d Binary files /dev/null and b/screenshots/11-add.png differ diff --git a/screenshots/12-localization.png b/screenshots/12-localization.png new file mode 100644 index 0000000..3696dcb Binary files /dev/null and b/screenshots/12-localization.png differ diff --git a/screenshots/13-cdn.png b/screenshots/13-cdn.png new file mode 100644 index 0000000..94cf5b0 Binary files /dev/null and b/screenshots/13-cdn.png differ diff --git a/screenshots/14-no-pagination.png b/screenshots/14-no-pagination.png new file mode 100644 index 0000000..ecf04ea Binary files /dev/null and b/screenshots/14-no-pagination.png differ diff --git a/screenshots/15-sorting.png b/screenshots/15-sorting.png new file mode 100644 index 0000000..127bbfa Binary files /dev/null and b/screenshots/15-sorting.png differ diff --git a/screenshots/16-columns-summary.png b/screenshots/16-columns-summary.png new file mode 100644 index 0000000..7b8418e Binary files /dev/null and b/screenshots/16-columns-summary.png differ diff --git a/screenshots/17-array-datasource.png b/screenshots/17-array-datasource.png new file mode 100644 index 0000000..89f982f Binary files /dev/null and b/screenshots/17-array-datasource.png differ diff --git a/screenshots/18-state-storage.png b/screenshots/18-state-storage.png new file mode 100644 index 0000000..22c0569 Binary files /dev/null and b/screenshots/18-state-storage.png differ diff --git a/screenshots/19-events.png b/screenshots/19-events.png new file mode 100644 index 0000000..f6304aa Binary files /dev/null and b/screenshots/19-events.png differ