diff --git a/src/LiveComponent/src/Form/Type/LiveCollectionType.php b/src/LiveComponent/src/Form/Type/LiveCollectionType.php index 2fc3aea95f3..7c34d7e6e38 100644 --- a/src/LiveComponent/src/Form/Type/LiveCollectionType.php +++ b/src/LiveComponent/src/Form/Type/LiveCollectionType.php @@ -35,6 +35,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $prototype = $builder->create('delete', $options['button_delete_type'], $options['button_delete_options']); $builder->setAttribute('button_delete_prototype', $prototype->getForm()); } + + if ($options['allow_sort']) { + $moveUpPrototype = $builder->create('move_up', $options['button_move_up_type'], $options['button_move_up_options']); + $builder->setAttribute('button_move_up_prototype', $moveUpPrototype->getForm()); + + $moveDownPrototype = $builder->create('move_down', $options['button_move_down_type'], $options['button_move_down_options']); + $builder->setAttribute('button_move_down_prototype', $moveDownPrototype->getForm()); + } } public function buildView(FormView $view, FormInterface $form, array $options): void @@ -92,6 +100,53 @@ public function finishView(FormView $view, FormInterface $form, array $options): array_splice($entryView->vars['button_delete']->vars['block_prefixes'], 1, 0, 'live_collection_button_delete'); } } + + // Add move up and move down buttons + if ($form->getConfig()->hasAttribute('button_move_up_prototype')) { + $prototype = $form->getConfig()->getAttribute('button_move_up_prototype'); + + $prototypes = []; + foreach ($form as $k => $entry) { + $prototypes[$k] = clone $prototype; + $prototypes[$k]->setParent($entry); + } + + foreach ($view as $k => $entryView) { + $entryView->vars['button_move_up'] = $prototypes[$k]->createView($entryView); + + $attr = $entryView->vars['button_move_up']->vars['attr']; + $attr['data-action'] ??= 'live#action'; + $attr['data-live-action-param'] ??= 'moveCollectionItemUp'; + $attr['data-live-name-param'] ??= $view->vars['full_name']; + $attr['data-live-index-param'] ??= $k; + $entryView->vars['button_move_up']->vars['attr'] = $attr; + + array_splice($entryView->vars['button_move_up']->vars['block_prefixes'], 1, 0, 'live_collection_button_move_up'); + } + } + + if ($form->getConfig()->hasAttribute('button_move_down_prototype')) { + $prototype = $form->getConfig()->getAttribute('button_move_down_prototype'); + + $prototypes = []; + foreach ($form as $k => $entry) { + $prototypes[$k] = clone $prototype; + $prototypes[$k]->setParent($entry); + } + + foreach ($view as $k => $entryView) { + $entryView->vars['button_move_down'] = $prototypes[$k]->createView($entryView); + + $attr = $entryView->vars['button_move_down']->vars['attr']; + $attr['data-action'] ??= 'live#action'; + $attr['data-live-action-param'] ??= 'moveCollectionItemDown'; + $attr['data-live-name-param'] ??= $view->vars['full_name']; + $attr['data-live-index-param'] ??= $k; + $entryView->vars['button_move_down']->vars['attr'] = $attr; + + array_splice($entryView->vars['button_move_down']->vars['block_prefixes'], 1, 0, 'live_collection_button_move_down'); + } + } } public function configureOptions(OptionsResolver $resolver): void @@ -105,8 +160,13 @@ public function configureOptions(OptionsResolver $resolver): void 'button_add_options' => [], 'button_delete_type' => ButtonType::class, 'button_delete_options' => [], + 'button_move_up_type' => ButtonType::class, + 'button_move_up_options' => [], + 'button_move_down_type' => ButtonType::class, + 'button_move_down_options' => [], 'allow_add' => true, 'allow_delete' => true, + 'allow_sort' => true, 'by_reference' => false, ]); } diff --git a/src/LiveComponent/src/LiveCollectionTrait.php b/src/LiveComponent/src/LiveCollectionTrait.php index 70f11f23b8b..354e0b0ec98 100644 --- a/src/LiveComponent/src/LiveCollectionTrait.php +++ b/src/LiveComponent/src/LiveCollectionTrait.php @@ -46,6 +46,46 @@ public function removeCollectionItem(PropertyAccessorInterface $propertyAccessor $propertyAccessor->setValue($this->formValues, $propertyPath, $data); } + #[LiveAction] + public function moveCollectionItemUp(PropertyAccessorInterface $propertyAccessor, #[LiveArg] string $name, #[LiveArg] int $index): void + { + if ($index <= 0) { + return; + } + + $propertyPath = $this->fieldNameToPropertyPath($name, $this->formName); + $data = $propertyAccessor->getValue($this->formValues, $propertyPath); + + if (!\is_array($data) || !isset($data[$index]) || !isset($data[$index - 1])) { + return; + } + + // Swap the current item with the one above it + $temp = $data[$index - 1]; + $data[$index - 1] = $data[$index]; + $data[$index] = $temp; + + $propertyAccessor->setValue($this->formValues, $propertyPath, $data); + } + + #[LiveAction] + public function moveCollectionItemDown(PropertyAccessorInterface $propertyAccessor, #[LiveArg] string $name, #[LiveArg] int $index): void + { + $propertyPath = $this->fieldNameToPropertyPath($name, $this->formName); + $data = $propertyAccessor->getValue($this->formValues, $propertyPath); + + if (!\is_array($data) || !isset($data[$index]) || !isset($data[$index + 1])) { + return; + } + + // Swap the current item with the one below it + $temp = $data[$index + 1]; + $data[$index + 1] = $data[$index]; + $data[$index] = $temp; + + $propertyAccessor->setValue($this->formValues, $propertyPath, $data); + } + private function fieldNameToPropertyPath(string $collectionFieldName, string $rootFormName): string { $propertyPath = $collectionFieldName; diff --git a/src/LiveComponent/templates/form_theme.html.twig b/src/LiveComponent/templates/form_theme.html.twig index 1042510f2f9..4349883469a 100644 --- a/src/LiveComponent/templates/form_theme.html.twig +++ b/src/LiveComponent/templates/form_theme.html.twig @@ -10,4 +10,10 @@ {%- if button_delete is defined and not button_delete.rendered -%} {{ form_row(button_delete) }} {%- endif -%} + {%- if button_move_up is defined and not button_move_up.rendered -%} + {{ form_row(button_move_up) }} + {%- endif -%} + {%- if button_move_down is defined and not button_move_down.rendered -%} + {{ form_row(button_move_down) }} + {%- endif -%} {%- endblock live_collection_entry_row -%}