From ce5ae81ead65c5cdc35645ea9017c5d15b2b5d64 Mon Sep 17 00:00:00 2001 From: "Javi H. Gil" Date: Thu, 26 Mar 2026 16:53:51 +0100 Subject: [PATCH 1/3] Support preview color to custom attributes --- assets/scripts/admin-cms.js | 3 +- .../content-edit/preview-color-attribute.js | 73 +++++++++++++++++++ assets/scripts/types/color-type.js | 31 +++++++- src/Form/Type/ColorType.php | 16 ++++ src/Twig/Extension/EditFormExtension.php | 16 +++- templates/forms/types_theme.html.twig | 16 ++-- templates/macros/modules_edit.html.twig | 13 +++- 7 files changed, 152 insertions(+), 16 deletions(-) create mode 100644 assets/scripts/admin/content-edit/preview-color-attribute.js diff --git a/assets/scripts/admin-cms.js b/assets/scripts/admin-cms.js index 594a8470..daf9ea57 100644 --- a/assets/scripts/admin-cms.js +++ b/assets/scripts/admin-cms.js @@ -14,6 +14,7 @@ import './admin/content-edit/preview-background-color'; import './admin/content-edit/preview-block'; import './admin/content-edit/preview-class'; import './admin/content-edit/preview-collection-node-class'; +import './admin/content-edit/preview-color-attribute'; import './admin/content-edit/contenteditable'; import './admin/content-edit/contenteditable-focus'; import './admin/content-edit/preview-fill'; @@ -30,7 +31,7 @@ import './admin/content-forms'; import './admin/routes-forms'; import './admin/fields-visibility'; import './admin/locales-widgets'; -import './admin/versions-diff'; +import './admin/versions-diff'; import './types/block-type'; import './types/color-type'; diff --git a/assets/scripts/admin/content-edit/preview-color-attribute.js b/assets/scripts/admin/content-edit/preview-color-attribute.js new file mode 100644 index 00000000..8babc9d2 --- /dev/null +++ b/assets/scripts/admin/content-edit/preview-color-attribute.js @@ -0,0 +1,73 @@ +import {registerFeature} from '@softspring/cms-bundle/scripts/tools'; + +registerFeature('admin_content_edit_preview_color_attribute', _init); + +/** + * Init behaviour + * @private + */ +function _init() { + document.addEventListener('input', onEditColorAttribute); + document.addEventListener('change', onEditColorAttribute); +} + +/** + * Sets a color field to target element attribute + * + * The preview target element must have the "data-edit-color-attribute-{attributeName}-target" attribute + * The input field must have the "data-edit-color-attribute-{attributeName}-input" + * Both data attributes must have the same value (as identificator) + */ +function onEditColorAttribute(event) { + if (!event.target) { + return; + } + + let moduleEdit = event.target.closest('.cms-module-edit'); + if (!moduleEdit) { + return; + } + + let preview = moduleEdit.querySelector('.module-preview'); + if (!preview) { + return; + } + + const kebabize = (str) => str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? "-" : "") + $.toLowerCase()); + const escapeAttrValue = (value) => typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(value) : value.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + + Object.keys(event.target.dataset).forEach((dataAttribute) => { + if (!dataAttribute.startsWith('editColorAttribute') || !dataAttribute.endsWith('Input')) { + return; + } + + const targetHashes = event.target.dataset[dataAttribute] + .split(',') + .map((hash) => hash.trim()) + .filter(Boolean); + + if (!targetHashes.length) { + return; + } + + const targetAttributeName = dataAttribute + .replace(/^editColorAttribute/, '') + .replace(/Input$/, ''); + + if (!targetAttributeName) { + return; + } + + const attributeName = kebabize(targetAttributeName); + targetHashes.forEach((targetHash) => { + const htmlTargetElements = preview.querySelectorAll("[data-edit-color-attribute-" + attributeName + "-target=\"" + escapeAttrValue(targetHash) + "\"]"); + if (!htmlTargetElements.length) { + return; + } + + htmlTargetElements.forEach((htmlTargetElement) => { + htmlTargetElement.setAttribute(attributeName, event.target.value); + }); + }); + }); +} diff --git a/assets/scripts/types/color-type.js b/assets/scripts/types/color-type.js index 6a29a555..c979aa03 100644 --- a/assets/scripts/types/color-type.js +++ b/assets/scripts/types/color-type.js @@ -25,7 +25,12 @@ function _init() { } let widget = event.target; - let toggler = widget.closest('.input-group').querySelector('[data-color-type=toggler]'); + let inputGroup = widget.closest('.input-group'); + if (!inputGroup) { + return; + } + + let toggler = inputGroup.querySelector('[data-color-type=toggler]'); if (!toggler) { return; @@ -40,7 +45,16 @@ function _init() { } function colorDatePicker(toggler) { - let widget = toggler.closest('.input-group').querySelector('[data-color-type=widget]'); + if (!toggler) { + return; + } + + let inputGroup = toggler.closest('.input-group'); + if (!inputGroup) { + return; + } + + let widget = inputGroup.querySelector('[data-color-type=widget]'); if (!widget) { return; @@ -56,7 +70,16 @@ function colorDatePicker(toggler) { if (!widget.hasAttribute('data-edit-bgcolor-input')) return; - let modulePreview = widget.closest('.cms-module-edit').querySelector('.module-preview'); + let moduleEdit = widget.closest('.cms-module-edit'); + if (!moduleEdit) { + return; + } + + let modulePreview = moduleEdit.querySelector('.module-preview'); + if (!modulePreview) { + return; + } + let htmlTargetElements = modulePreview.querySelectorAll("[data-edit-bgcolor-target='" + widget.dataset.editBgcolorInput + "']"); if (htmlTargetElements.length) { @@ -66,4 +89,4 @@ function colorDatePicker(toggler) { htmlTargetElements.forEach((htmlTargetElement) => htmlTargetElement.style.backgroundColor = widget.value); } } -} \ No newline at end of file +} diff --git a/src/Form/Type/ColorType.php b/src/Form/Type/ColorType.php index 9b901cae..84d87775 100644 --- a/src/Form/Type/ColorType.php +++ b/src/Form/Type/ColorType.php @@ -4,6 +4,9 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ColorType as SymfonyColorType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolver; class ColorType extends AbstractType { @@ -12,8 +15,21 @@ public function getBlockPrefix(): string return 'cms_color'; } + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'show_toggler' => true, + ]); + $resolver->setAllowedTypes('show_toggler', 'bool'); + } + public function getParent(): string { return SymfonyColorType::class; } + + public function buildView(FormView $view, FormInterface $form, array $options): void + { + $view->vars['show_toggler'] = $options['show_toggler']; + } } diff --git a/src/Twig/Extension/EditFormExtension.php b/src/Twig/Extension/EditFormExtension.php index 27797680..166958e9 100644 --- a/src/Twig/Extension/EditFormExtension.php +++ b/src/Twig/Extension/EditFormExtension.php @@ -23,9 +23,21 @@ public function getFilters(): array ]; } - public function formViewSetAttr(FormView $formView, string $name, string $value): void + public function formViewSetAttr(FormView $formView, string $name, string $value, bool $allowMultiple = false): void { - $formView->vars['attr'][$name] = $value; + $current = (string)($formView->vars['attr'][$name] ?? ''); + + if (!$allowMultiple || $current === '') { + $formView->vars['attr'][$name] = $value; + return; + } + + $values = array_filter(array_map('trim', explode(',', $current))); + if (!in_array($value, $values, true)) { + $values[] = $value; + } + + $formView->vars['attr'][$name] = implode(',', $values); } public function sha1($value): string diff --git a/templates/forms/types_theme.html.twig b/templates/forms/types_theme.html.twig index f78e3b6f..678ca6e5 100644 --- a/templates/forms/types_theme.html.twig +++ b/templates/forms/types_theme.html.twig @@ -37,14 +37,18 @@ {% endblock html_class_widget %} {% block cms_color_widget %} -
-
-
- + {% if form.vars.show_toggler %} +
+
+
+ +
+ {{ form_widget(form, {'attr': {'data-color-type': 'widget'}, 'disabled': not form.vars.data}) }}
- {{ form_widget(form, {'attr': {'data-color-type': 'widget'}, 'disabled': not form.vars.data}) }} -
+ {% else %} + {{ form_widget(form) }} + {% endif %} {% endblock cms_color_widget %} {% block html_id_widget %} diff --git a/templates/macros/modules_edit.html.twig b/templates/macros/modules_edit.html.twig index 69f5c376..a769203d 100644 --- a/templates/macros/modules_edit.html.twig +++ b/templates/macros/modules_edit.html.twig @@ -117,6 +117,13 @@ {% endif %} {%- endmacro -%} +{%- macro link_color_attribute (formField, attributeName, defaultValue = null) -%} + {% set value_hash = (formField.vars.name~'_'~random())|sfs_cms_sha1|slice(0,8) %} + {{ _self._update_link_value(formField, 'data-edit-color-attribute-'~attributeName~'-input', value_hash, true) }} + data-edit-color-attribute-{{ attributeName }}-target="{{ value_hash }}" + {{ attributeName }}="{{ formField.vars.data|default(defaultValue)|default('#000000') }}" +{%- endmacro -%} + {%- macro bg_color_style(formField) -%} {{ formField.vars.data ? 'background-color:'~formField.vars.data~';' : '' }} {%- endmacro -%} @@ -184,8 +191,8 @@ {% endif %} {%- endmacro _module_name -%} -{%- macro _update_link_value (formField, dataField, value = false) -%} - {% if not formField.vars.attr[dataField]|default(false) %} - {{ sfs_cms_form_view_set_attr(formField, dataField, value == false ? (formField.vars.name~'_'~random())|sfs_cms_sha1|slice(0,8) : value) }} +{%- macro _update_link_value (formField, dataField, value = false, allowMultiple = false) -%} + {% if not formField.vars.attr[dataField]|default(false) or allowMultiple %} + {{ sfs_cms_form_view_set_attr(formField, dataField, value == false ? (formField.vars.name~'_'~random())|sfs_cms_sha1|slice(0,8) : value, allowMultiple) }} {% endif %} {%- endmacro _update_link_value -%} From 50865f23fbf82d9f04123122dcfef0d2c4f3f985 Mon Sep 17 00:00:00 2001 From: "Javi H. Gil" Date: Fri, 27 Mar 2026 08:08:47 +0100 Subject: [PATCH 2/3] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- assets/scripts/admin/content-edit/preview-color-attribute.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/scripts/admin/content-edit/preview-color-attribute.js b/assets/scripts/admin/content-edit/preview-color-attribute.js index 8babc9d2..8b062640 100644 --- a/assets/scripts/admin/content-edit/preview-color-attribute.js +++ b/assets/scripts/admin/content-edit/preview-color-attribute.js @@ -16,7 +16,7 @@ function _init() { * * The preview target element must have the "data-edit-color-attribute-{attributeName}-target" attribute * The input field must have the "data-edit-color-attribute-{attributeName}-input" - * Both data attributes must have the same value (as identificator) + * Both data attributes must have the same value (as identifier) */ function onEditColorAttribute(event) { if (!event.target) { From 744347afc24d4c3480b0983318f1e06dd6119557 Mon Sep 17 00:00:00 2001 From: "Javi H. Gil" Date: Fri, 27 Mar 2026 09:17:51 +0100 Subject: [PATCH 3/3] Fix code style --- src/Twig/Extension/EditFormExtension.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Twig/Extension/EditFormExtension.php b/src/Twig/Extension/EditFormExtension.php index 166958e9..b1862033 100644 --- a/src/Twig/Extension/EditFormExtension.php +++ b/src/Twig/Extension/EditFormExtension.php @@ -25,10 +25,11 @@ public function getFilters(): array public function formViewSetAttr(FormView $formView, string $name, string $value, bool $allowMultiple = false): void { - $current = (string)($formView->vars['attr'][$name] ?? ''); + $current = (string) ($formView->vars['attr'][$name] ?? ''); - if (!$allowMultiple || $current === '') { + if (!$allowMultiple || '' === $current) { $formView->vars['attr'][$name] = $value; + return; }