Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/src/docs/features/column-visibility-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Column Visibility Groups

Column Visibility Groups allow you to organize table columns into different "views." This is useful when you have a lot of information to display in a single row and want to separate it into multiple, easily switchable groups. Users can select which group of columns to display using a dropdown in the table UI.

[[toc]]

## Basic Usage

By default, a data table has a single visibility group. You can define additional groups and assign columns to them.

```php
use Kreyu\Bundle\DataTableBundle\DataTableBuilderInterface;
use Kreyu\Bundle\DataTableBundle\Type\AbstractDataTableType;
use Kreyu\Bundle\DataTableBundle\Column\Type\NumberColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Type\TextColumnType;

class ExampleDataTableType extends AbstractDataTableType
{
public function buildDataTable(DataTableBuilderInterface $builder, array $options): void
{
// Define visibility groups
$builder->addColumnVisibilityGroup('default');
$builder->addColumnVisibilityGroup('address', [
// By default, the group label is the group name, but you can override it:
'label' => 'Address related content',
// By default, the first defined group is the default one, but you can override it:
'is_default' => true,
]);

// Assign groups to columns
$builder
->addColumn('id', NumberColumnType::class, [
'sort' => true,
// Will always be displayed as it does not have any group assigned
])
->addColumn('name', TextColumnType::class, [
'label' => 'Full name',
'sort' => true,
])
->addColumn('streetName', TextColumnType::class, [
'sort' => true,
// This column will only be visible when the "address" group is selected
'column_visibility_groups' => ['address'],
])
;
}
}
```

## How It Works

- **Defining Groups:** Use `$builder->addColumnVisibilityGroup($name, $options)` to define one or more groups. The `label` option is used as the display name in the UI.
- **Assigning Columns:** Use the `column_visibility_groups` option in `addColumn()` to assign a column to one or more groups. If omitted or set to `null`/`[]`, the column will always be visible.
- **Switching Views:** A select dropdown appears in the table, allowing users to switch between the different column visibility groups.

## Notes

- You can define as many visibility groups as needed.
- A column can belong to multiple groups by specifying multiple group names in the `column_visibility_groups` array.
- If `column_visibility_groups` is `null` or an empty array, the column is shown in the "default" group.
- Creating a default group is optional but recommended for better user experience : it ensures that the user can go back to the base view.

## UI

When multiple visibility groups are present, a select dropdown is rendered above the table, allowing users to choose which group of columns to display.
6 changes: 6 additions & 0 deletions src/Column/Type/ColumnType.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,12 @@ public function configureOptions(OptionsResolver $resolver): void
->allowedTypes('bool')
->info('Defines whether the column can be personalized by the user in personalization feature.')
;

$resolver->define('column_visibility_groups')
->default(null)
->allowedTypes('null', 'string', 'array')
->info('Defines the visibility groups of a column.')
;
}

public function getBlockPrefix(): string
Expand Down
52 changes: 52 additions & 0 deletions src/ColumnVisibilityGroup/ColumnVisibilityGroup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace Kreyu\Bundle\DataTableBundle\ColumnVisibilityGroup;

class ColumnVisibilityGroup implements ColumnVisibilityGroupInterface
{
private string $name;
private string $label;
private bool $isDefault;

public function __construct()
{
}

public function getName(): string
{
return $this->name;
}

public function setName(string $name): self
{
$this->name = $name;

return $this;
}

public function getLabel(): string
{
return $this->label;
}

public function setLabel(string $label): self
{
$this->label = $label;

return $this;
}

public function isDefault(): bool
{
return $this->isDefault;
}

public function setIsDefault(bool $isDefault): self
{
$this->isDefault = $isDefault;

return $this;
}
}
39 changes: 39 additions & 0 deletions src/ColumnVisibilityGroup/ColumnVisibilityGroupBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Kreyu\Bundle\DataTableBundle\ColumnVisibilityGroup;

use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;

class ColumnVisibilityGroupBuilder implements ColumnVisibilityGroupBuilderInterface
{
public function __construct(
private TranslatorInterface $translator,
) {
}

public function getColumnVisibilityGroup(string $name, array $options = []): ColumnVisibilityGroupInterface
{
$optionResolver = new OptionsResolver();
$this->configureOptions($optionResolver);

$resolvedOptions = $optionResolver->resolve($options);

$columnVisibilityGroup = new ColumnVisibilityGroup();
$columnVisibilityGroup->setName($name);
$columnVisibilityGroup->setLabel($this->translator->trans($resolvedOptions['label'] ?? $name));
$columnVisibilityGroup->setIsDefault($resolvedOptions['is_default']);

return $columnVisibilityGroup;
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'label' => null,
'is_default' => false,
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Kreyu\Bundle\DataTableBundle\ColumnVisibilityGroup;

interface ColumnVisibilityGroupBuilderInterface
{
public function getColumnVisibilityGroup(string $name, array $options = []): ColumnVisibilityGroupInterface;
}
16 changes: 16 additions & 0 deletions src/ColumnVisibilityGroup/ColumnVisibilityGroupInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Kreyu\Bundle\DataTableBundle\ColumnVisibilityGroup;

interface ColumnVisibilityGroupInterface
{
public function getName(): string;

public function getLabel(): string;

public function isDefault(): bool;

public function setIsDefault(bool $isDefault): self;
}
32 changes: 32 additions & 0 deletions src/DataTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Kreyu\Bundle\DataTableBundle\Action\Type\ActionType;
use Kreyu\Bundle\DataTableBundle\Column\ColumnInterface;
use Kreyu\Bundle\DataTableBundle\Column\Type\ColumnType;
use Kreyu\Bundle\DataTableBundle\ColumnVisibilityGroup\ColumnVisibilityGroupInterface;
use Kreyu\Bundle\DataTableBundle\Event\DataTableEvent;
use Kreyu\Bundle\DataTableBundle\Event\DataTableEvents;
use Kreyu\Bundle\DataTableBundle\Event\DataTableExportEvent;
Expand Down Expand Up @@ -74,6 +75,11 @@ class DataTable implements DataTableInterface
*/
private array $exporters = [];

/**
* @var array<string, ColumnVisibilityGroupInterface>
*/
private array $columnVisibilityGroups = [];

/**
* The sorting data currently applied to the data table.
*/
Expand Down Expand Up @@ -113,6 +119,7 @@ class DataTable implements DataTableInterface

private bool $initialized = false;
private ?string $turboFrameId = null;
private ?string $requestedColumnVisibilityGroup = null;

public function __construct(
private ProxyQueryInterface $query,
Expand Down Expand Up @@ -202,6 +209,12 @@ public function getVisibleColumns(): array
$visible = $this->personalizationData?->getColumn($column)?->isVisible() ?? $visible;
}

if ($visible && $column->getConfig()->getOption('column_visibility_groups')) {
return
null === $this->requestedColumnVisibilityGroup
|| in_array($this->requestedColumnVisibilityGroup, (array) $column->getConfig()->getOption('column_visibility_groups'), true);
}

return $visible;
});
}
Expand Down Expand Up @@ -508,6 +521,18 @@ public function removeExporter(string $name): static
return $this;
}

public function addColumnVisibilityGroup(ColumnVisibilityGroupInterface $columnVisibilityGroup): static
{
$this->columnVisibilityGroups[$columnVisibilityGroup->getName()] = $columnVisibilityGroup;

return $this;
}

public function getColumnVisibilityGroups(): array
{
return $this->columnVisibilityGroups;
}

public function paginate(PaginationData $data, bool $persistence = true): void
{
if (!$this->config->isPaginationEnabled()) {
Expand Down Expand Up @@ -880,6 +905,13 @@ public function isRequestFromTurboFrame(): bool
return null !== $this->turboFrameId && 'kreyu_data_table_'.$this->getName() === $this->turboFrameId;
}

public function setRequestedColumnVisibilityGroup(?string $requestedColumnVisibilityGroup): self
{
$this->requestedColumnVisibilityGroup = $requestedColumnVisibilityGroup;

return $this;
}

private function dispatch(string $eventName, DataTableEvent $event): void
{
$dispatcher = $this->config->getEventDispatcher();
Expand Down
88 changes: 88 additions & 0 deletions src/DataTableBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Kreyu\Bundle\DataTableBundle\Column\Type\CheckboxColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Type\ColumnTypeInterface;
use Kreyu\Bundle\DataTableBundle\Column\Type\TextColumnType;
use Kreyu\Bundle\DataTableBundle\ColumnVisibilityGroup\ColumnVisibilityGroupInterface;
use Kreyu\Bundle\DataTableBundle\Exception\BadMethodCallException;
use Kreyu\Bundle\DataTableBundle\Exception\InvalidArgumentException;
use Kreyu\Bundle\DataTableBundle\Exporter\ExporterBuilderInterface;
Expand Down Expand Up @@ -136,6 +137,20 @@ class DataTableBuilder extends DataTableConfigBuilder implements DataTableBuilde
*/
private array $unresolvedExporters = [];

/**
* The column visibility groups defined for the data table.
*
* @var array<ColumnVisibilityGroupInterface>
*/
private array $columnVisibilityGroups = [];

/**
* The data of column visibility groups that haven't been converted to column visibility group yet.
*
* @var array<array>
*/
private array $unresolvedColumnVisibilityGroups = [];

public function __construct(
string $name,
ResolvedDataTableTypeInterface $type,
Expand Down Expand Up @@ -715,6 +730,55 @@ public function removeExporter(string $name): static
return $this;
}

public function addColumnVisibilityGroup(ColumnVisibilityGroupInterface|string $columnVisibilityGroup, array $options = []): static
{
if ($this->locked) {
throw $this->createBuilderLockedException();
}

if ($columnVisibilityGroup instanceof ColumnVisibilityGroupInterface) {
$this->columns[$columnVisibilityGroup->getName()] = $columnVisibilityGroup;

unset($this->unresolvedColumns[$columnVisibilityGroup->getName()]);

return $this;
}

$this->columnVisibilityGroups[$columnVisibilityGroup] = null;
$this->unresolvedColumnVisibilityGroups[$columnVisibilityGroup] = $options;

return $this;
}

public function hasColumnVisibilityGroup(string $name): bool
{
if ($this->locked) {
throw $this->createBuilderLockedException();
}

return isset($this->columnVisibilityGroups[$name]) || isset($this->unresolvedColumnVisibilityGroups[$name]);
}

public function removeColumnVisibilityGroup(string $name): static
{
if ($this->locked) {
throw $this->createBuilderLockedException();
}

unset($this->unresolvedColumnVisibilityGroups[$name], $this->columnVisibilityGroups[$name]);

return $this;
}

public function createColumnVisibilityGroup(string $name, array $options = []): ColumnVisibilityGroupInterface
{
if ($this->locked) {
throw $this->createBuilderLockedException();
}

return $this->getColumnVisibilityGroupBuilder()->getColumnVisibilityGroup($name, $options);
}

public function getDataTable(): DataTableInterface
{
if ($this->locked) {
Expand Down Expand Up @@ -745,6 +809,12 @@ public function getDataTable(): DataTableInterface
$dataTable->addColumn($column->getColumn());
}

$this->resolveColumnVisibilityGroups();

foreach ($this->columnVisibilityGroups as $columnVisibilityGroup) {
$dataTable->addColumnVisibilityGroup($columnVisibilityGroup);
}

$this->resolveFilters();

foreach ($this->filters as $filter) {
Expand Down Expand Up @@ -883,6 +953,24 @@ private function resolveExporters(): void
}
}

private function resolveColumnVisibilityGroups(): void
{
foreach (array_keys($this->unresolvedColumnVisibilityGroups) as $columnVisibilityGroups) {
$this->resolveColumnVisibilityGroup($columnVisibilityGroups);
}
}

private function resolveColumnVisibilityGroup(string $name): ColumnVisibilityGroupInterface
{
$options = $this->unresolvedColumnVisibilityGroups[$name];

unset($this->unresolvedColumnVisibilityGroups[$name]);

$columnVisibilityGroup = $this->getColumnVisibilityGroupBuilder()->getColumnVisibilityGroup($name, $options);

return $this->columnVisibilityGroups[$name] = $columnVisibilityGroup;
}

private function shouldPrependBatchCheckboxColumn(): bool
{
return $this->isAutoAddingBatchCheckboxColumn()
Expand Down
Loading