From 7fe1acd47da082451d7c9f47d27c51a598c2f7a8 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sat, 11 May 2024 17:45:57 +0200 Subject: [PATCH 1/6] add base of the block builder --- src/core/Entity/Site/Page/BuilderBlock.php | 20 +++ src/core/Entity/Site/Page/ChoiceBlock.php | 11 +- src/core/Entity/Site/Page/JsonBlock.php | 19 +++ src/core/Form/Site/Page/BuilderBlockType.php | 32 ++++ src/core/Form/Type/BuilderType.php | 40 +++++ src/core/Resources/assets/css/admin.scss | 48 ++++++ src/core/Resources/assets/js/admin.js | 1 + .../components/builder-block/BuilderBlock.vue | 145 ++++++++++++++++++ .../builder-block/BuilderBlockCreate.vue | 64 ++++++++ .../builder-block/BuilderBlockForm.vue | 29 ++++ .../builder-block/BuilderBlockItem.vue | 108 +++++++++++++ .../assets/js/modules/builder-block.js | 28 ++++ .../Resources/assets/js/modules/tinymce.js | 9 ++ .../form/bootstrap_4_form_theme.html.twig | 9 ++ 14 files changed, 553 insertions(+), 10 deletions(-) create mode 100644 src/core/Entity/Site/Page/BuilderBlock.php create mode 100644 src/core/Entity/Site/Page/JsonBlock.php create mode 100644 src/core/Form/Site/Page/BuilderBlockType.php create mode 100644 src/core/Form/Type/BuilderType.php create mode 100644 src/core/Resources/assets/js/components/builder-block/BuilderBlock.vue create mode 100644 src/core/Resources/assets/js/components/builder-block/BuilderBlockCreate.vue create mode 100644 src/core/Resources/assets/js/components/builder-block/BuilderBlockForm.vue create mode 100644 src/core/Resources/assets/js/components/builder-block/BuilderBlockItem.vue create mode 100644 src/core/Resources/assets/js/modules/builder-block.js diff --git a/src/core/Entity/Site/Page/BuilderBlock.php b/src/core/Entity/Site/Page/BuilderBlock.php new file mode 100644 index 0000000..7f63bb8 --- /dev/null +++ b/src/core/Entity/Site/Page/BuilderBlock.php @@ -0,0 +1,20 @@ +add( + 'value', + BuilderType::class, + array_merge([ + 'required' => false, + 'label' => false, + ], $options['options']), + ); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => BuilderBlock::class, + 'options' => [], + ]); + } +} diff --git a/src/core/Form/Type/BuilderType.php b/src/core/Form/Type/BuilderType.php new file mode 100644 index 0000000..f817b26 --- /dev/null +++ b/src/core/Form/Type/BuilderType.php @@ -0,0 +1,40 @@ +vars = array_replace($view->vars, [ + ]); + } + + public function configureOptions(OptionsResolver $resolver) + { + parent::configureOptions($resolver); + + $resolver->setDefaults([ + 'allow_add' => true, + 'allow_remove' => true, + 'compound' => false, + ]); + } + + public function getBlockPrefix() + { + return 'builder'; + } +} diff --git a/src/core/Resources/assets/css/admin.scss b/src/core/Resources/assets/css/admin.scss index d3b2414..dd958bc 100644 --- a/src/core/Resources/assets/css/admin.scss +++ b/src/core/Resources/assets/css/admin.scss @@ -759,3 +759,51 @@ label.required::after { color: #49555b; font-size: 20px; } + +.builder-widget { + .block { + border: 1px solid rgba(map-get($theme-colors, 'dark-blue'), 0.3); + padding: 10px; + border-radius: 4px; + margin-bottom: 10px; + background: rgba(map-get($theme-colors, 'dark-blue'), 0.02); + } + + .block-header { + .block-header-item { + font-size: 12px; + display: inline-block; + margin-bottom: 3px; + padding: 2px 6px; + border-radius: 4px; + margin-bottom: 5px; + margin-right: 2px; + cursor: pointer; + } + } + + .block-label { + background: map-get($theme-colors, 'dark-blue'); + border: 1px solid map-get($theme-colors, 'dark-blue'); + color: lighten(map-get($theme-colors, 'dark-blue'), 100%); + } + + .block-settings-toggler { + background: none; + border: 1px solid map-get($theme-colors, 'dark-blue'); + color: map-get($theme-colors, 'dark-blue'); + } + + .block-settings { + padding: 4px; + } + + .builder-add { + text-align: center; + margin-bottom: -5px; + + .picker { + text-align: left; + } + } +} diff --git a/src/core/Resources/assets/js/admin.js b/src/core/Resources/assets/js/admin.js index 58cb77c..c1c988d 100644 --- a/src/core/Resources/assets/js/admin.js +++ b/src/core/Resources/assets/js/admin.js @@ -29,3 +29,4 @@ require('./modules/file-picker.js')() require('./modules/analytics.js')() require('./modules/page.js')() require('./modules/node.js')() +require('./modules/builder-block.js')() diff --git a/src/core/Resources/assets/js/components/builder-block/BuilderBlock.vue b/src/core/Resources/assets/js/components/builder-block/BuilderBlock.vue new file mode 100644 index 0000000..e61e7a5 --- /dev/null +++ b/src/core/Resources/assets/js/components/builder-block/BuilderBlock.vue @@ -0,0 +1,145 @@ + + + diff --git a/src/core/Resources/assets/js/components/builder-block/BuilderBlockCreate.vue b/src/core/Resources/assets/js/components/builder-block/BuilderBlockCreate.vue new file mode 100644 index 0000000..3a4b9ee --- /dev/null +++ b/src/core/Resources/assets/js/components/builder-block/BuilderBlockCreate.vue @@ -0,0 +1,64 @@ + + + diff --git a/src/core/Resources/assets/js/components/builder-block/BuilderBlockForm.vue b/src/core/Resources/assets/js/components/builder-block/BuilderBlockForm.vue new file mode 100644 index 0000000..4aa215c --- /dev/null +++ b/src/core/Resources/assets/js/components/builder-block/BuilderBlockForm.vue @@ -0,0 +1,29 @@ + + + diff --git a/src/core/Resources/assets/js/components/builder-block/BuilderBlockItem.vue b/src/core/Resources/assets/js/components/builder-block/BuilderBlockItem.vue new file mode 100644 index 0000000..e885b87 --- /dev/null +++ b/src/core/Resources/assets/js/components/builder-block/BuilderBlockItem.vue @@ -0,0 +1,108 @@ + + + diff --git a/src/core/Resources/assets/js/modules/builder-block.js b/src/core/Resources/assets/js/modules/builder-block.js new file mode 100644 index 0000000..5c87bfc --- /dev/null +++ b/src/core/Resources/assets/js/modules/builder-block.js @@ -0,0 +1,28 @@ +const Vue = require('vue').default + +const BuilderBlock = require('../components/builder-block/BuilderBlock').default + +module.exports = () => { + const wrappers = document.querySelectorAll('.builder-widget') + + wrappers.forEach((wrapper) => { + const component = wrapper.querySelector('.builder-widget-component') + + return new Vue({ + el: component, + template: ``, + data() { + return { + value: JSON.parse(component.getAttribute('data-value')) + } + }, + components: { + BuilderBlock + } + }) + }) +} diff --git a/src/core/Resources/assets/js/modules/tinymce.js b/src/core/Resources/assets/js/modules/tinymce.js index d85ac09..1f6ab2c 100644 --- a/src/core/Resources/assets/js/modules/tinymce.js +++ b/src/core/Resources/assets/js/modules/tinymce.js @@ -74,6 +74,8 @@ const createTinymceConfig = function () { }) editor.on('Change', () => { + editor.save(); + editor.getElement().dispatchEvent(new Event('input')); window.tinymce.triggerSave(false, true) }) } @@ -618,6 +620,13 @@ const doInitEditor = () => { } module.exports = function () { + document.querySelector('body').addEventListener('builder_block.update', () => { + window.setTimeout(() => { + createTinymceConfig() + doInitEditor() + }, 500) + }) + $(() => { createTinymceConfig() doInitEditor() diff --git a/src/core/Resources/views/form/bootstrap_4_form_theme.html.twig b/src/core/Resources/views/form/bootstrap_4_form_theme.html.twig index 05895ce..fc86bb8 100644 --- a/src/core/Resources/views/form/bootstrap_4_form_theme.html.twig +++ b/src/core/Resources/views/form/bootstrap_4_form_theme.html.twig @@ -1,5 +1,14 @@ {% extends 'bootstrap_4_layout.html.twig' %} +{% block builder_widget %} + {% set row_attr = row_attr|merge({class: 'builder-widget ' ~ (row_attr.class ?? '')}) %} + +
+
+
+
+{% endblock %} + {% block grapesjs_widget %}
From 78965bbf10da0f814a108962eab4d8b595236b88 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 12 May 2024 14:30:28 +0200 Subject: [PATCH 2/6] allow to move blocks update builder view --- src/core/Resources/assets/css/admin.scss | 11 +- .../components/builder-block/BuilderBlock.vue | 174 +++++++++++------- .../builder-block/BuilderBlockCreate.vue | 106 +++++++++-- .../builder-block/BuilderBlockForm.vue | 29 --- .../builder-block/BuilderBlockItem.vue | 88 +++++++-- .../builder-block/BuilderBlockSetting.vue | 57 ++++++ 6 files changed, 332 insertions(+), 133 deletions(-) delete mode 100644 src/core/Resources/assets/js/components/builder-block/BuilderBlockForm.vue create mode 100644 src/core/Resources/assets/js/components/builder-block/BuilderBlockSetting.vue diff --git a/src/core/Resources/assets/css/admin.scss b/src/core/Resources/assets/css/admin.scss index dd958bc..099dfe7 100644 --- a/src/core/Resources/assets/css/admin.scss +++ b/src/core/Resources/assets/css/admin.scss @@ -773,10 +773,9 @@ label.required::after { .block-header-item { font-size: 12px; display: inline-block; - margin-bottom: 3px; + margin-bottom: 10px; padding: 2px 6px; border-radius: 4px; - margin-bottom: 5px; margin-right: 2px; cursor: pointer; } @@ -788,7 +787,7 @@ label.required::after { color: lighten(map-get($theme-colors, 'dark-blue'), 100%); } - .block-settings-toggler { + .block-settings-inverse { background: none; border: 1px solid map-get($theme-colors, 'dark-blue'); color: map-get($theme-colors, 'dark-blue'); @@ -798,9 +797,13 @@ label.required::after { padding: 4px; } + .block-id { + font-size: 12px; + margin-right: 5px; + } + .builder-add { text-align: center; - margin-bottom: -5px; .picker { text-align: left; diff --git a/src/core/Resources/assets/js/components/builder-block/BuilderBlock.vue b/src/core/Resources/assets/js/components/builder-block/BuilderBlock.vue index e61e7a5..9bacbdd 100644 --- a/src/core/Resources/assets/js/components/builder-block/BuilderBlock.vue +++ b/src/core/Resources/assets/js/components/builder-block/BuilderBlock.vue @@ -4,7 +4,11 @@ v-for="(block, key) in value" :item="block" :widgets="widgets" + :isFirst="key === 0" + :isLast="key == Object.keys(value)[Object.keys(value).length -1]" @remove-item="removeBlock(key)" + @move-item-up="moveBlockUp(key)" + @move-item-down="moveBlockDown(key)" />
import BuilderBlockItem from './BuilderBlockItem' import BuilderBlockCreate from './BuilderBlockCreate' -import BuilderBlockForm from './BuilderBlockForm' const widgets = { - row: { - label: 'Bootstrap row', + bsContainer: { + category: 'Boostrap', + label: 'Container', settings: { }, isContainer: true, - widgets: ['column'], + widgets: [], }, - column: { - label: 'Bootstrap column', + bsRow: { + category: 'Boostrap', + label: 'Row', + settings: { + }, + isContainer: true, + widgets: ['bsColumn'], + }, + bsColumn: { + category: 'Boostrap', + label: 'Column', settings: { size: {label: 'Size', type: 'input', attr: {type: 'number'}}, sizeMd: {label: 'Size MD', type: 'input', attr: {type: 'number'}}, @@ -40,15 +53,102 @@ const widgets = { widgets: [], }, tinymce: { + category: 'Editor', label: 'TinyMCE', settings: { value: {label: 'Value', type: 'textarea', attr: {'data-tinymce': ''}}, }, isContainer: false, widgets: [], + }, + select: { + category: null, + label: 'Item with Select', + settings: { + value: {label: 'Value', type: 'select', attr: {}, options: [ + {value: 'a', text: 'A'}, + {value: 'b', text: 'B'}, + ]} + }, } } +export default { + name: 'BuilderBlock', + props: { + id: { + type: String, + required: true, + }, + name: { + type: String, + required: true, + }, + initialValue: { + type: Array, + required: false, + } + }, + data() { + return { + value: this.initialValue, + widgets, + blockKey: 0 + } + }, + methods: { + toJson(value) { + return JSON.stringify(value) + }, + triggerBuilderBlockEvent() { + document.querySelector('body').dispatchEvent(new Event('builder_block.update')) + }, + moveBlockUp(key) { + const prev = this.value[key-1] + const current = this.value[key] + + this.value[key-1] = current + this.value[key] = prev + ++this.blockKey + }, + moveBlockDown(key) { + const next = this.value[key+1] + const current = this.value[key] + + this.value[key+1] = current + this.value[key] = next + ++this.blockKey + }, + removeBlock(key) { + let newValue = [] + + this.value.forEach((v, k) => { + if (k !== key) { + newValue.push(v) + } + }) + + this.value = newValue + ++this.blockKey + }, + }, + components: { + BuilderBlockItem, + BuilderBlockCreate, + }, + mounted() { + this.triggerBuilderBlockEvent() + }, + created() { + this.triggerBuilderBlockEvent() + }, + updated() { + this.triggerBuilderBlockEvent() + } +} + +/* + const blocks = [ { widget: 'row', @@ -81,65 +181,5 @@ const blocks = [ ] } ] - -export default { - name: 'BuilderBlock', - props: { - id: { - type: String, - required: true, - }, - name: { - type: String, - required: true, - }, - initialValue: { - type: Array, - required: false, - } - }, - data() { - return { - value: this.initialValue, - widgets, - blockKey: 0 - } - }, - methods: { - toJson(value) { - return JSON.stringify(value) - }, - triggerBuilderBlockEvent() { - document.querySelector('body').dispatchEvent(new Event('builder_block.update')) - }, - removeBlock(key) { - let newValue = [] - - this.value.forEach((v, k) => { - if (k !== key) { - newValue.push(v) - } - }) - - console.log(newValue) - - this.value = newValue - ++this.blockKey - } - }, - components: { - BuilderBlockItem, - BuilderBlockCreate, - BuilderBlockForm, - }, - mounted() { - this.triggerBuilderBlockEvent() - }, - created() { - this.triggerBuilderBlockEvent() - }, - updated() { - this.triggerBuilderBlockEvent() - } -} +*/ diff --git a/src/core/Resources/assets/js/components/builder-block/BuilderBlockCreate.vue b/src/core/Resources/assets/js/components/builder-block/BuilderBlockCreate.vue index 3a4b9ee..11588af 100644 --- a/src/core/Resources/assets/js/components/builder-block/BuilderBlockCreate.vue +++ b/src/core/Resources/assets/js/components/builder-block/BuilderBlockCreate.vue @@ -1,18 +1,66 @@ + +