Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grid view #5990

Open
wants to merge 44 commits into
base: 6.2
Choose a base branch
from
Open

Grid view #5990

wants to merge 44 commits into from

Conversation

BurntimeX
Copy link
Member

@BurntimeX BurntimeX commented Sep 9, 2024

Grid view is a generic solution for the creation of listings, as they occur again and again in the software. In addition to rendering, the grid view also take care of sorting, filtering and pagination and ensure that a lot of boilerplating becomes obsolete.

The implementation essentially offers the following advantages:

  1. A uniform appearance and usability for the user.
  2. An easy way for developers to create their own grid views.
  3. An easy way for developers to extend existing grid views using plugins.

See #5967

Usage

CronjobLogGridView is available as a reference implementation.

DatabaseObjectListGridView

DatabaseObjectListGridView allows to use a DatabaseObjectList as the data source for the grid view.

Example:

final class FooGridView extends DatabaseObjectListGridView
{
    public function __construct()
    {
        $this->addColumns([
            GridViewColumn::for('id')
                ->label('wcf.global.objectID'),
            GridViewColumn::for('name')
                ->label('wcf.global.name'),
        ]);
    }

    #[\Override]
    protected function createObjectList(): DatabaseObjectList
    {
        return new FooList();
    }
}

DataSourceGridView

DataSourceGridView use an array as the data source for the grid view.

Example:

final class FooGridView extends DataSourceGridView
{
    public function __construct()
    {
        $this->addColumns([
            GridViewColumn::for('id')
                ->label('wcf.global.objectID'),
            GridViewColumn::for('name')
                ->label('wcf.global.name'),
        ]);
    }

    #[\Override]
    public function getObjectID(mixed $row): mixed
    {
        return $row['id'];
    }

    #[\Override]
    protected function loadDataSource(): array
    {
        return [
            ['id' => 1, 'name' => 'Name 1'],
            ['id' => 2, 'name' => 'Name 2'],
        ];
    }
}

AbstractGridViewPage

With the help of AbstractGridViewPage, grid views can be easily integrated into pages.

Example:

class FooListPage extends AbstractGridViewPage
{
    #[\Override]
    protected function createGridViewController(): AbstractGridView
    {
        return new FooGridView();
    }
}

Template:

{include file='header' pageTitle='foo.page.title'}

<div class="section">
	{unsafe:$gridView->render()}
</div>

{include file='footer'}

Columns

Columns can be created using the GridViewColumn::for method. This expects a unique string as a parameter, which is equivalent to the corresponding key in the data source.

The label method can be used to give the column a human readable label.

Example:

final class FooGridView extends DatabaseObjectListGridView
{
    public function __construct()
    {
        $this->addColumns([
            GridViewColumn::for('id')
                ->label('wcf.global.objectID'),
            GridViewColumn::for('name')
                ->label('wcf.global.name'),
        ]);
    }
}

Renderer

Renderers can be applied to columns to format the output. A column can have multiple renderers. The renderers are applied in the order in which they were set.

CurrencyColumnRenderer

CurrencyColumnRenderer formats the content of a column as a currency. Expects the content of the column to be a decimal.

Example:

GridViewColumn::for('foo')
    ->renderer(new CurrencyColumnRenderer('EUR'))

DefaultColumnRenderer

The DefaultColumnRenderer is automatically applied to all columns if no other renderers have been set. It converts special characters to HTML entities.

LinkColumnRenderer

LinkColumnRenderer allows the setting of a link to a column.

Example:

GridViewColumn::for('foo')
    ->renderer(new LinkColumnRenderer(FooEditForm::class))

NumberColumnRenderer

NumberColumnRenderer formats the content of a column as a number using StringUtil::formatNumeric().

Example:

GridViewColumn::for('foo')
    ->renderer(new NumberColumnRenderer())

PhraseColumnRenderer

PhraseColumnRenderer formats the content of a column as a phrase.

Example:

GridViewColumn::for('foo')
    ->renderer(new PhraseColumnRenderer())

TimeColumnRenderer

TimeColumnRenderer renders a unix timestamp into a human readable format.

Example:

GridViewColumn::for('foo')
    ->renderer(new TimeColumnRenderer())

TitleColumnRenderer

TitleColumnRenderer formats the content of a column as a title.

Example:

GridViewColumn::for('foo')
    ->renderer(new TitleColumnRenderer())

Row Link

A row link applies a link to every column in the grid.

final class FooGridView extends DatabaseObjectListGridView
{
    public function __construct()
    {
        $this->addRowLink(new GridViewRowLink(FooEditForm::class));
    }
}

The constructor supports 3 optional parameters:

  1. string $controllerClass: The controller to which the link should refer.
  2. array $parameters: Additional parameters for the controller.
  3. string $cssClass: CSS class for the link.

Sorting

Columns can be marked as sortable so that the user has the option of sorting according to the content of the column.

GridViewColumn::for('foo')
    ->sortable()

Default Sort Field

The default sort field and sort order can be set when building the grid view:

final class FooGridView extends DatabaseObjectListGridView
{
    public function __construct()
    {
        $this->setSortField('showOrder');
        $this->setSortOrder('DESC');
    }
}

Filtering

Filters can be defined for columns so that the user has the option of filtering according to the content of the column.

I18nTextFilter

I18nTextFilter is a filter for text columns that are using i18n phrases

Example:

GridViewColumn::for('foo')
    ->filter(new I18nTextFilter())

SelectFilter

SelectFilter allows a column to be filtered on the basis of a select dropdown.

Example:

GridViewColumn::for('foo')
    ->filter(new SelectFilter([
        1 => 'value 1',
        0 => 'value 0',
    ]));

TextFilter

TextFilter is a filter for text columns.

Example:

GridViewColumn::for('foo')
    ->filter(new TextFilter())

TimeFilter

TimeFilter is a filter for columns that contain unix timestamps.

Example:

GridViewColumn::for('foo')
    ->filter(new TimeFilter())

Actions

Actions are actions that the user can apply to a row in the grid view. Typical use cases are deleting and editing.
By default, actions are made accessible via a dropdown. Special actions are so-called “quick actions”, which can be controlled directly via their own icons.

DeleteAction

DeleteAction allows the user to delete a row of the grid view. The constructor expects the RPC API endpoint for deletion as a parameter.

Example:

final class FooGridView extends DatabaseObjectListGridView
{
    public function __construct()
    {
        $this->addActions([
            new DeleteAction('core/foo/%s'),
        ]);
    }
}

EditAction

EditAction adds a link to the corresponding edit form. The constructor expects the name of the controller as a parameter.

Example:

final class FooGridView extends DatabaseObjectListGridView
{
    public function __construct()
    {
        $this->addActions([
            new EditAction(FooEditForm::class),
        ]);
    }
}

ToggleAction

ToggleAction allows the user to enable/disable a row of the grid view. The constructor expects the RPC API endpoints for enabling and disabling as parameters.

Example:

final class FooGridView extends DatabaseObjectListGridView
{
    public function __construct()
    {
        $this->addActions([
            new ToggleAction('core/foo/%s/enable', 'core/foo/%s/disable'),
        ]);
    }
}

Events

Existing grid views can be modified using events. Example of adding an additional column:

$eventHandler->register(
    \wcf\event\gridView\UserRankGridViewInitialized::class,
    static function (\wcf\event\gridView\UserRankGridViewInitialized $event) {
        $event->gridView->addColumnBefore(GridViewColumn::for('hideTitle')
            ->label('hideTitle')
            ->renderer(new NumberColumnRenderer())
            ->sortable(), 'requiredPoints');
    }
);

Planned Functions

  1. Clipboard support: We are planning a general modernization of the clipboard before we integrate it into the grid views.
  2. Possibility for the user to configure the displayed columns and to change the order and hide columns individually.
  3. Support for using grid views in dialogs.
  4. More generic renderers, filters and actions depending on your feedback.

@dtdesign dtdesign changed the base branch from master to 6.2 October 15, 2024 10:00
@BurntimeX BurntimeX marked this pull request as ready for review November 14, 2024 15:17
@BurntimeX BurntimeX changed the title Grid view (PoC) Grid view Nov 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

2 participants