diff --git a/features/admin/page/inserting_content_elements_on_page.feature b/features/admin/page/inserting_content_elements_on_page.feature new file mode 100644 index 00000000..3154617d --- /dev/null +++ b/features/admin/page/inserting_content_elements_on_page.feature @@ -0,0 +1,35 @@ +@managing_pages +Feature: Inserting content elements between existing elements on a page + In order to manage the structure of content on a page + As an Administrator + I want to be able to insert content elements between existing ones + + Background: + Given I am logged in as an administrator + And the store operates on a single channel in "United States" + + @ui @javascript + Scenario: Inserting a content element between two existing elements + When I go to the create page page + And I fill the code with "insert-test-page" + And I fill the name with "Insert Test Page" + And I fill the slug with "insert-test-page" + And I add a heading content element with type "h1" and "My Title" content + And I add a textarea content element with "My body text" content + When I insert a textarea content element after the 1st content element + Then the 1st content element should be a "Heading" element + And the 2nd content element should be a "Textarea" element + And the 3rd content element should be a "Textarea" element + + @ui @javascript + Scenario: Inserting a content element before the first element + When I go to the create page page + And I fill the code with "insert-test-page" + And I fill the name with "Insert Test Page" + And I fill the slug with "insert-test-page" + And I add a heading content element with type "h1" and "My Title" content + And I add a textarea content element with "My body text" content + When I insert a textarea content element before the 1st content element + Then the 1st content element should be a "Textarea" element + And the 2nd content element should be a "Heading" element + And the 3rd content element should be a "Textarea" element diff --git a/src/Twig/Component/Trait/ContentElementsCollectionFormComponentTrait.php b/src/Twig/Component/Trait/ContentElementsCollectionFormComponentTrait.php index e52001f0..7d602b90 100644 --- a/src/Twig/Component/Trait/ContentElementsCollectionFormComponentTrait.php +++ b/src/Twig/Component/Trait/ContentElementsCollectionFormComponentTrait.php @@ -13,15 +13,18 @@ namespace Sylius\CmsPlugin\Twig\Component\Trait; +use Sylius\Bundle\UiBundle\Twig\Component\LiveCollectionTrait; use Sylius\CmsPlugin\Entity\TemplateInterface; use Sylius\CmsPlugin\Form\Type\Translation\ContentConfigurationTranslationsType; use Sylius\CmsPlugin\Repository\TemplateRepositoryInterface; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\UX\LiveComponent\Attribute\LiveAction; use Symfony\UX\LiveComponent\Attribute\LiveArg; use Symfony\UX\LiveComponent\ComponentWithFormTrait; /** * @mixin ComponentWithFormTrait + * @mixin LiveCollectionTrait * * @see ContentConfigurationTranslationsType */ @@ -45,6 +48,41 @@ public function applyContentTemplate(#[LiveArg] string $localeCode): void $this->populateElements($localeCode, $template); } + #[LiveAction] + public function insertCollectionItem( + PropertyAccessorInterface $propertyAccessor, + #[LiveArg] + string $name, + #[LiveArg] + ?string $type = null, + #[LiveArg] + ?int $insertAfterIndex = null, + ): void { + $propertyPath = $this->fieldNameToPropertyPath($name, $this->formName ?? ''); + $data = $propertyAccessor->getValue($this->formValues, $propertyPath); + + if (!\is_array($data)) { + $data = []; + } + + ksort($data); + $values = array_values($data); + $newItem = null === $type ? [] : ['type' => $type]; + + if (null === $insertAfterIndex) { + $values[] = $newItem; + } elseif ($insertAfterIndex < 0) { + array_unshift($values, $newItem); + } else { + $keys = array_keys($data); + $pos = array_search($insertAfterIndex, $keys, true); + $insertPosition = false !== $pos ? $pos + 1 : count($values); + array_splice($values, $insertPosition, 0, [$newItem]); + } + + $propertyAccessor->setValue($this->formValues, $propertyPath, $values); + } + /** @param TemplateRepositoryInterface $templateRepository */ protected function initializeTemplateRepository(TemplateRepositoryInterface $templateRepository): void { diff --git a/templates/admin/macros/insert_element_divider.html.twig b/templates/admin/macros/insert_element_divider.html.twig new file mode 100644 index 00000000..2e84df50 --- /dev/null +++ b/templates/admin/macros/insert_element_divider.html.twig @@ -0,0 +1,21 @@ +{% macro insert_element_divider(collection_types, collection_name, insert_after_index) %} +
+
+ +
+
+{% endmacro %} diff --git a/templates/admin/shared/component_elements/form_theme.html.twig b/templates/admin/shared/component_elements/form_theme.html.twig index 5eea20f9..69ba5252 100644 --- a/templates/admin/shared/component_elements/form_theme.html.twig +++ b/templates/admin/shared/component_elements/form_theme.html.twig @@ -1,7 +1,24 @@ {% extends '@SyliusAdmin/shared/form_theme.html.twig' %} {%- block live_collection_widget -%} - {{ block('form_widget') }} + {%- import '@SyliusCmsPlugin/admin/macros/insert_element_divider.html.twig' as InsertElementButton -%} + + {%- set collection_types = button_add is defined ? button_add.vars.types : {} -%} + {%- set collection_name = button_add is defined ? button_add.vars.attr['data-live-name-param'] : '' -%} + +
+ {%- for child in form -%} + {%- if loop.first and collection_types is not empty -%} + {{ InsertElementButton.insert_element_divider(collection_types, collection_name, -1) }} + {%- endif -%} + + {{ form_row(child) }} + + {%- if not loop.last and collection_types is not empty -%} + {{ InsertElementButton.insert_element_divider(collection_types, collection_name, child.vars.name) }} + {%- endif -%} + {%- endfor -%} +
{%- endblock live_collection_widget -%} {%- block live_collection_entry_row -%} @@ -24,12 +41,17 @@ {% block add_button_row %} {% if types is not empty %} -