diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 65e3f1e4..9712d370 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -134,6 +134,12 @@ export default defineConfig({ { text: 'Link', link: '/reference/types/action/link' }, { text: 'Button', link: '/reference/types/action/button' }, { text: 'Form', link: '/reference/types/action/form' }, + { + text: 'Dropdown', link: '/reference/types/action/dropdown', + items: [ + { text: 'LinkDropdownItem', link: '/reference/types/action/link-dropdown-item' }, + ], + }, { text: 'Action', link: '/reference/types/action/action' }, ], }, diff --git a/docs/src/docs/components/actions.md b/docs/src/docs/components/actions.md index 4cc1704f..af02e63e 100644 --- a/docs/src/docs/components/actions.md +++ b/docs/src/docs/components/actions.md @@ -569,3 +569,32 @@ If `FormActionType` is used, the scripts will append hidden inputs with selected ``` + +## Dropdown actions + +In some cases, it may be useful to group multiple actions under a single dropdown. + +To do so, define an action using the [`DropdownActionType`](../../reference/types/action/dropdown.md) type: +Then, define child actions under its `actions` array. Each child action can be created using the builder's `createAction`, `createRowAction` or `createBatchAction` method, depending on the context: + +```php +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\DropdownActionType; +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\LinkDropdownItemActionType; + +$builder + ->addRowAction('advanced', DropdownActionType::class, [ + 'actions' => [ + $builder->createRowAction('update', LinkDropdownItemActionType::class, [ + 'href' => fn (Post $post) => $this->urlGenerator->generate('post_delete', [ + 'id' => $post->getId(), + ]), + ]), + ], + ]) +; +``` + +> [!TIP] +> Although any action type can be used, rendering forms and buttons inside a dropdown may look weird. +> Therefore, it is recommended to use [`LinkDropdownItemActionType`](../../reference/types/action/link-dropdown-item.md) for dropdown items, +> so it will be rendered properly as a simple link. \ No newline at end of file diff --git a/docs/src/reference/types/action/dropdown.md b/docs/src/reference/types/action/dropdown.md new file mode 100644 index 00000000..c0546fc4 --- /dev/null +++ b/docs/src/reference/types/action/dropdown.md @@ -0,0 +1,61 @@ + + +# DropdownActionType + +The [`DropdownActionType`](https://github.com/Kreyu/data-table-bundle/blob/main/src/Action/Type/DropdownActionType.php) represents an action rendered as a dropdown, where each item corresponds to separate action. + +## Options + +### `actions` + +- **type**: `array` or `callable` (if using as a row action) +- **default**: `[]` + +An array of actions that will be rendered as dropdown items. +Each action can be created using `createAction`, `createRowAction` or `createBatchAction` method, depending on the context: + +```php +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\DropdownActionType; +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\LinkDropdownItemActionType; + +$builder + ->addAction('advanced', DropdownActionType::class, [ + 'actions' => [ + $builder->createAction('update', LinkDropdownItemActionType::class, [ + 'href' => '#' + ]), + ], + ]) +; +``` + +When using the `DropdownActionType` as a [row action](../../../docs/components/actions.md), you can provide a callable +that will receive the row data as an argument and should return an array of actions. + +```php +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\DropdownActionType; +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\LinkDropdownItemActionType; + +$builder + ->addRowAction('advanced', DropdownActionType::class, [ + 'actions' => fn (Post $post) => [ + $builder->createRowAction('update', LinkDropdownItemActionType::class, [ + 'href' => $this->urlGenerator->generate('post_update', [ + 'id' => $post->getId(), + ]), + ]), + ], + ]) +; +``` + +> [!TIP] +> Although any action type can be used, rendering forms and buttons inside a dropdown may look weird. +> Therefore, it is recommended to use [`LinkDropdownItemActionType`](link-dropdown-item.md) for dropdown items, +> so it will be rendered properly as a simple link. + +## Inherited options + + diff --git a/docs/src/reference/types/action/link-dropdown-item.md b/docs/src/reference/types/action/link-dropdown-item.md new file mode 100644 index 00000000..da18ae7f --- /dev/null +++ b/docs/src/reference/types/action/link-dropdown-item.md @@ -0,0 +1,81 @@ + + +# LinkDropdownItemActionType + +The [`LinkDropdownItemActionType`](https://github.com/Kreyu/data-table-bundle/blob/main/src/Action/Type/Dropdown/LinkDropdownItemActionType.php) +represents an action rendered as dropdown item with a simple link. It is meant to be used as a child of the [`DropdownActionType`](dropdown.md). + +## Options + +### `href` + +- **type**: `string` or `callable` (if using as a row action) +- **default**: `'#'` + +A value used as an action link [href attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-href). + +```php +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\DropdownActionType; +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\LinkDropdownItemActionType; + +$builder + ->addAction('advanced', DropdownActionType::class, [ + 'actions' => [ + $builder->createAction('update', LinkDropdownItemActionType::class, [ + 'href' => fn (Post $post) => $this->urlGenerator->generate('post_update', [ + 'id' => $post->getId(), + ]), + ]), + ], + ]) +; +``` + +When using the `LinkDropdownItemActionType` as a [row action](../../../docs/components/actions.md), you can provide a callable +that will receive the row data as an argument and should return an array of actions. + +```php +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\DropdownActionType; +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\LinkDropdownItemActionType; + +$builder + ->addRowAction('advanced', DropdownActionType::class, [ + 'actions' => [ + $builder->createRowAction('update', LinkDropdownItemActionType::class, [ + 'href' => fn (Post $post) => $this->urlGenerator->generate('post_update', [ + 'id' => $post->getId(), + ]), + ]), + ], + ]) +; +``` + +### `target` + +- **type**: `string` or `callable` +- **default**: `'_self'` + +Sets the value that will be used as an anchor [target attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target). + +```php +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\DropdownActionType; +use Kreyu\Bundle\DataTableBundle\Action\Type\Dropdown\LinkDropdownItemActionType; + +$builder + ->addAction('preview', DropdownActionType::class, [ + 'actions' => [ + $builder->createAction('render', LinkDropdownItemActionType::class, [ + 'href' => '#', + 'target' => '_blank', + ]), + ], + ]) +; +``` + +## Inherited options + + diff --git a/src/Action/Type/Dropdown/DropdownActionType.php b/src/Action/Type/Dropdown/DropdownActionType.php new file mode 100644 index 00000000..2d1d027a --- /dev/null +++ b/src/Action/Type/Dropdown/DropdownActionType.php @@ -0,0 +1,48 @@ +parent instanceof ColumnValueView) { + $options['actions'] = $options['actions']($view->parent->value); + } + + foreach ($options['actions'] as $itemActionBuilder) { + if (!$itemActionBuilder instanceof ActionBuilderInterface) { + throw new UnexpectedTypeException($itemActionBuilder, ActionBuilderInterface::class); + } + + $itemActionBuilder->setContext($action->getConfig()->getContext()); + + $itemAction = $itemActionBuilder->getAction(); + $itemAction->setDataTable($action->getDataTable()); + + $itemActions[] = $itemAction->createView($view->parent); + } + + $view->vars['actions'] = $itemActions; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->define('actions') + ->allowedTypes(ActionBuilderInterface::class.'[]', 'callable') + ->required() + ; + } +} diff --git a/src/Action/Type/Dropdown/LinkDropdownItemActionType.php b/src/Action/Type/Dropdown/LinkDropdownItemActionType.php new file mode 100644 index 00000000..ef94012c --- /dev/null +++ b/src/Action/Type/Dropdown/LinkDropdownItemActionType.php @@ -0,0 +1,16 @@ +set('kreyu_data_table.action.type.form', FormActionType::class) ->tag('kreyu_data_table.action.type') ; + + $services + ->set('kreyu_data_table.action.type.dropdown', DropdownActionType::class) + ->tag('kreyu_data_table.action.type') + ; + + $services + ->set('kreyu_data_table.action.type.link_dropdown_item', LinkDropdownItemActionType::class) + ->tag('kreyu_data_table.action.type') + ; }; diff --git a/src/Resources/views/themes/base.html.twig b/src/Resources/views/themes/base.html.twig index fafa635e..cd46160d 100755 --- a/src/Resources/views/themes/base.html.twig +++ b/src/Resources/views/themes/base.html.twig @@ -632,6 +632,24 @@ {% endblock %} +{% block action_dropdown_control %} + {# Themes that extend base theme should provide their own implementation of dropdown #} + + +{% endblock %} + +{% block action_link_dropdown_item_control %} + {% set attr = { href, target }|filter(v => v != null)|merge(attr|default({})) %} + + + {% with { attr: {} } %}{{- block('action_control', theme, _context) -}}{% endwith %} + +{% endblock %} + {% block sort_arrow_none %}{% endblock %} {% block sort_arrow_asc %}↑{% endblock %} diff --git a/src/Resources/views/themes/bootstrap_5.html.twig b/src/Resources/views/themes/bootstrap_5.html.twig index 6767e24f..775cfd6c 100755 --- a/src/Resources/views/themes/bootstrap_5.html.twig +++ b/src/Resources/views/themes/bootstrap_5.html.twig @@ -723,6 +723,58 @@ {% endif %} {% endblock %} +{% block action_dropdown_control %} + +{% endblock %} + +{% block action_link_dropdown_item_control %} + {% set attr = { class: 'dropdown-item' }|merge(attr) %} + + {% if confirmation %} + {% set attr = { + 'data-bs-toggle': 'modal', + 'data-bs-target': '#' ~ confirmation.identifier, + }|merge(attr) %} + {% endif %} + + {{ parent() }} +{% endblock %} + {% block sort_arrow_none %}