Merge branch 'develop'
This commit is contained in:
commit
92eaaba699
|
@ -1,5 +1,9 @@
|
|||
## [Unreleased]
|
||||
|
||||
## [v1.25.1] - 2024-05-13
|
||||
### Added
|
||||
* add drag&drop in the block builder
|
||||
|
||||
## [v1.25.0] - 2024-05-12
|
||||
### Added
|
||||
* add block builder widget
|
||||
|
|
47
src/core/BuilderBlock/Block/Bootstrap/AlertBlock.php
Normal file
47
src/core/BuilderBlock/Block/Bootstrap/AlertBlock.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\BuilderBlock\Block\Bootstrap;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
#[AutoconfigureTag('builder_block.widget')]
|
||||
class AlertBlock extends BootstrapBlock
|
||||
{
|
||||
public function __construct(protected TranslatorInterface $translator)
|
||||
{
|
||||
}
|
||||
|
||||
public function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$options = [];
|
||||
|
||||
foreach ([
|
||||
'Primary' => 'primary',
|
||||
'Secondary' => 'secondary',
|
||||
'Info' => 'info',
|
||||
'Success' => 'success',
|
||||
'Danger' => 'danger',
|
||||
'Warning' => 'warning',
|
||||
'Light' => 'light',
|
||||
'Dark' => 'dark',
|
||||
] as $k => $v) {
|
||||
$options[] = [
|
||||
'text' => $this->translator->trans($k),
|
||||
'value' => $v,
|
||||
];
|
||||
}
|
||||
|
||||
$this
|
||||
->setName('bsAlert')
|
||||
->setLabel('Alert')
|
||||
->setOrder(4)
|
||||
->setIsContainer(true)
|
||||
->setIcon('<i class="fas fa-exclamation-circle"></i>')
|
||||
->setTemplate('@Core/builder_block/bootstrap/alert.html.twig')
|
||||
->addSetting(name: 'level', label: 'Level', type: 'select', extraOptions: ['options' => $options])
|
||||
;
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ use App\Core\BuilderBlock\BuilderBlock;
|
|||
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
|
||||
|
||||
#[AutoconfigureTag('builder_block.widget')]
|
||||
class ColumnBuilderBlock extends BootstrapBlock
|
||||
class ColumnBlock extends BootstrapBlock
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
|
@ -16,7 +16,8 @@ class ColumnBuilderBlock extends BootstrapBlock
|
|||
->setName('bsColumn')
|
||||
->setLabel('Column')
|
||||
->setIsContainer(true)
|
||||
->setClass('col-12 col-md-2 pr-md-1')
|
||||
->setOrder(3)
|
||||
->setClass('col-12 col-lg-2 pr-md-1')
|
||||
->setTemplate('@Core/builder_block/bootstrap/column.html.twig')
|
||||
->setIcon('<i class="fas fa-columns"></i>')
|
||||
->addSetting(name: 'size', label: 'Extra small', type: 'number', attributes: ['min' => 0, 'max' => 12])
|
|
@ -6,7 +6,7 @@ use App\Core\BuilderBlock\BuilderBlock;
|
|||
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
|
||||
|
||||
#[AutoconfigureTag('builder_block.widget')]
|
||||
class ContainerBuilderBlock extends BootstrapBlock
|
||||
class ContainerBlock extends BootstrapBlock
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
|
@ -16,6 +16,7 @@ class ContainerBuilderBlock extends BootstrapBlock
|
|||
->setName('bsContainer')
|
||||
->setLabel('Container')
|
||||
->setIsContainer(true)
|
||||
->setOrder(1)
|
||||
->setTemplate('@Core/builder_block/bootstrap/container.html.twig')
|
||||
->setIcon('<i class="fas fa-th"></i>')
|
||||
->addSetting(name: 'isFluid', label: 'Fluid', type: 'checkbox')
|
|
@ -6,7 +6,7 @@ use App\Core\BuilderBlock\BuilderBlock;
|
|||
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
|
||||
|
||||
#[AutoconfigureTag('builder_block.widget')]
|
||||
class RowBuilderBlock extends BootstrapBlock
|
||||
class RowBlock extends BootstrapBlock
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
|
@ -15,6 +15,7 @@ class RowBuilderBlock extends BootstrapBlock
|
|||
$this
|
||||
->setName('bsRow')
|
||||
->setLabel('Row')
|
||||
->setOrder(2)
|
||||
->setIsContainer(true)
|
||||
->setIcon('<i class="fas fa-align-justify"></i>')
|
||||
->setTemplate('@Core/builder_block/bootstrap/row.html.twig')
|
26
src/core/BuilderBlock/Block/Editor/TextareaBlock.php
Normal file
26
src/core/BuilderBlock/Block/Editor/TextareaBlock.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\BuilderBlock\Block\Editor;
|
||||
|
||||
use App\Core\BuilderBlock\BuilderBlock;
|
||||
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
|
||||
|
||||
#[AutoconfigureTag('builder_block.widget')]
|
||||
class TextareaBlock extends EditorBlock
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('textarea')
|
||||
->setLabel('Text')
|
||||
->setIsContainer(false)
|
||||
->setIcon('<i class="fas fa-pencil-alt"></i>')
|
||||
->setTemplate('@Core/builder_block/editor/textarea.html.twig')
|
||||
->addSetting(name: 'nl2br', label: 'Insert line breaks', type: 'checkbox', default: true)
|
||||
->addSetting(name: 'allowHtml', label: 'Allow HTML', type: 'checkbox', default: false)
|
||||
->addSetting(name: 'value', type: 'textarea')
|
||||
;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ abstract class BuilderBlock
|
|||
protected string $template = '';
|
||||
protected bool $isContainer = false;
|
||||
protected ?string $icon = null;
|
||||
protected int $order = 1;
|
||||
|
||||
abstract public function configure();
|
||||
|
||||
|
@ -109,7 +110,7 @@ abstract class BuilderBlock
|
|||
];
|
||||
|
||||
foreach ($extraOptions as $key => $value) {
|
||||
if (in_array($key, array_keys($this->settings[$name]))) {
|
||||
if (!in_array($key, array_keys($this->settings[$name]))) {
|
||||
$this->settings[$name][$key] = $value;
|
||||
}
|
||||
}
|
||||
|
@ -171,6 +172,18 @@ abstract class BuilderBlock
|
|||
return $this->icon;
|
||||
}
|
||||
|
||||
public function setOrder(int $order): self
|
||||
{
|
||||
$this->order = $order;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOrder(): int
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
public function buildVars(array $data)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ class BuilderBlockContainer
|
|||
|
||||
public function getWidgets(): array
|
||||
{
|
||||
usort($this->widgets, fn(BuilderBlock $a, BuilderBlock $b) => $a->getOrder() <=> $b->getOrder());
|
||||
|
||||
return $this->widgets;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Core;
|
||||
|
||||
if (!defined('MURPH_VERSION')) {
|
||||
define('MURPH_VERSION', 'v1.25.0');
|
||||
define('MURPH_VERSION', 'v1.25.1');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -799,10 +799,17 @@ label.required::after {
|
|||
|
||||
.block-settings {
|
||||
padding: 4px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.block-id {
|
||||
font-size: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.block-show-dropzone {
|
||||
.block-dropzone {
|
||||
min-height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
<template>
|
||||
<div class="block" :key="blockKey" v-if="Object.keys(widgets).length">
|
||||
<Draggable
|
||||
v-if="Object.keys(widgets).length"
|
||||
v-model="value"
|
||||
:key="blockKey"
|
||||
:animation="200"
|
||||
group="children"
|
||||
ghost-class="ghost"
|
||||
@start="dragStart"
|
||||
@end="dragEnd"
|
||||
handle=".dragger"
|
||||
:class="{'block-show-dropzone': showDragDrop}"
|
||||
class="block"
|
||||
>
|
||||
<BuilderBlockItem
|
||||
v-for="(block, key) in value"
|
||||
:key="block.id + '-' + key"
|
||||
: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)"
|
||||
@drag-start="dragStart"
|
||||
@drag-end="dragEnd"
|
||||
/>
|
||||
<div class="container">
|
||||
<BuilderBlockCreate
|
||||
|
@ -19,13 +29,14 @@
|
|||
/>
|
||||
</div>
|
||||
<textarea :name="name" class="d-none">{{ toJson(value) }}</textarea>
|
||||
</div>
|
||||
</Draggable>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BuilderBlockItem from './BuilderBlockItem'
|
||||
import BuilderBlockCreate from './BuilderBlockCreate'
|
||||
import Routing from '../../../../../../../../../friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js'
|
||||
import Draggable from 'vuedraggable'
|
||||
|
||||
const axios = require('axios').default
|
||||
const routes = require('../../../../../../../../../../public/js/fos_js_routes.json')
|
||||
|
@ -51,7 +62,8 @@ export default {
|
|||
return {
|
||||
value: this.initialValue,
|
||||
widgets: {},
|
||||
blockKey: 0
|
||||
blockKey: 0,
|
||||
showDragDrop: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -61,26 +73,6 @@ export default {
|
|||
triggerBuilderBlockEvent() {
|
||||
document.querySelector('body').dispatchEvent(new Event('builder_block.update'))
|
||||
},
|
||||
moveBlockUp(key) {
|
||||
let newValue = this.value.map((x) => x)
|
||||
|
||||
newValue[key-1] = this.value[key]
|
||||
newValue[key] = this.value[key-1]
|
||||
|
||||
this.value = newValue
|
||||
|
||||
++this.blockKey
|
||||
},
|
||||
moveBlockDown(key) {
|
||||
let newValue = this.value.map((x) => x)
|
||||
|
||||
newValue[key+1] = this.value[key]
|
||||
newValue[key] = this.value[key+1]
|
||||
|
||||
this.value = newValue
|
||||
|
||||
++this.blockKey
|
||||
},
|
||||
removeBlock(key) {
|
||||
let newValue = []
|
||||
|
||||
|
@ -92,12 +84,21 @@ export default {
|
|||
|
||||
this.value = newValue
|
||||
|
||||
// ++this.blockKey
|
||||
++this.blockKey
|
||||
},
|
||||
dragStart() {
|
||||
this.showDragDrop = true
|
||||
},
|
||||
dragEnd() {
|
||||
this.showDragDrop = false
|
||||
|
||||
++this.blockKey
|
||||
},
|
||||
},
|
||||
components: {
|
||||
BuilderBlockItem,
|
||||
BuilderBlockCreate,
|
||||
Draggable,
|
||||
},
|
||||
mounted() {
|
||||
this.triggerBuilderBlockEvent()
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
}
|
||||
|
||||
.widget {
|
||||
display: inline-block;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.widget-content {
|
||||
|
@ -48,22 +48,22 @@
|
|||
<span class="fa fa-plus"></span>
|
||||
</span>
|
||||
|
||||
<div class="categories mt-2 list-group" :class="{'d-none': !showPicker}">
|
||||
<div class="categories mt-2" :class="{'d-none': !showPicker}">
|
||||
<div
|
||||
v-for="category in categories()"
|
||||
v-if="Object.keys(category.widgets).length"
|
||||
class="category"
|
||||
class="category row"
|
||||
>
|
||||
<div
|
||||
v-if="category.label != 'none'"
|
||||
v-text="category.label"
|
||||
class="category-label row"
|
||||
class="category-label col-12"
|
||||
></div>
|
||||
|
||||
<div
|
||||
v-for="(widget, name) in category.widgets"
|
||||
v-on:click="add(name, widget)"
|
||||
class="widget col-3"
|
||||
class="widget col-auto"
|
||||
>
|
||||
<div class="widget-content">
|
||||
<div class="widget-label">
|
||||
|
|
|
@ -22,26 +22,17 @@
|
|||
>
|
||||
<span class="fa fa-cog"></span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!isFirst"
|
||||
v-on:click="moveMeUp"
|
||||
class="block-header-item block-settings-inverse"
|
||||
class="block-header-item block-settings-inverse dragger"
|
||||
>
|
||||
<span class="fas fa-arrow-up"></span>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isLast"
|
||||
v-on:click="moveMeDown"
|
||||
class="block-header-item block-settings-inverse"
|
||||
>
|
||||
<span class="fas fa-arrow-down"></span>
|
||||
<span class="fa fa-arrows-alt"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block-settings" v-if="Object.keys(widget.settings).length" :class="{'d-none': !showSettings}">
|
||||
<div class="row">
|
||||
<BuilderBlockSetting
|
||||
class="mb-0"
|
||||
v-for="(params, setting) in widget.settings"
|
||||
:key="item.id + '-' + setting"
|
||||
:class="widget.class"
|
||||
|
@ -52,18 +43,28 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<BuilderBlockItem
|
||||
v-if="item.children !== null && item.children.length > 0"
|
||||
v-for="(child, key) in item.children"
|
||||
:key="child.id"
|
||||
:item="child"
|
||||
:widgets="widgets"
|
||||
:isFirst="key === 0"
|
||||
:isLast="key == Object.keys(item.children)[Object.keys(item.children).length -1]"
|
||||
@remove-item="removeBlock(key)"
|
||||
@move-item-up="moveBlockUp(key)"
|
||||
@move-item-down="moveBlockDown(key)"
|
||||
/>
|
||||
<Draggable
|
||||
v-if="widget.isContainer"
|
||||
v-model="item.children"
|
||||
ghost-class="ghost"
|
||||
group="children"
|
||||
@start="dragStart"
|
||||
@end="dragEnd"
|
||||
:animation="200"
|
||||
handle=".dragger"
|
||||
class="block-dropzone"
|
||||
>
|
||||
<BuilderBlockItem
|
||||
v-if="item.children !== null && item.children.length > 0"
|
||||
v-for="(child, key) in item.children"
|
||||
:key="child.id"
|
||||
:item="child"
|
||||
:widgets="widgets"
|
||||
@remove-item="removeBlock(key)"
|
||||
@drag-start="dragStart"
|
||||
@drag-end="dragEnd"
|
||||
/>
|
||||
</Draggable>
|
||||
|
||||
<div v-if="widget.isContainer" class="container">
|
||||
<BuilderBlockCreate
|
||||
|
@ -78,6 +79,7 @@
|
|||
<script>
|
||||
import BuilderBlockCreate from './BuilderBlockCreate'
|
||||
import BuilderBlockSetting from './BuilderBlockSetting'
|
||||
import Draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
name: 'BuilderBlockItem',
|
||||
|
@ -90,20 +92,12 @@ export default {
|
|||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isFirst: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
isLast: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
widget: null,
|
||||
showSettings: false,
|
||||
blockKey: 0
|
||||
blockKey: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -113,32 +107,6 @@ export default {
|
|||
removeMe() {
|
||||
this.$emit('remove-item')
|
||||
},
|
||||
moveMeUp() {
|
||||
this.$emit('move-item-up')
|
||||
},
|
||||
moveMeDown() {
|
||||
this.$emit('move-item-down')
|
||||
},
|
||||
moveBlockUp(key) {
|
||||
let newValue = this.item.children.map((x) => x)
|
||||
|
||||
newValue[key-1] = this.item.children[key]
|
||||
newValue[key] = this.item.children[key-1]
|
||||
|
||||
this.item.children = newValue
|
||||
|
||||
++this.blockKey
|
||||
},
|
||||
moveBlockDown(key) {
|
||||
let newValue = this.item.children.map((x) => x)
|
||||
|
||||
newValue[key+1] = this.item.children[key]
|
||||
newValue[key] = this.item.children[key+1]
|
||||
|
||||
this.item.children = newValue
|
||||
|
||||
++this.blockKey
|
||||
},
|
||||
removeBlock(key) {
|
||||
let children = []
|
||||
|
||||
|
@ -151,10 +119,18 @@ export default {
|
|||
this.item.children = children
|
||||
++this.blockKey
|
||||
},
|
||||
dragStart() {
|
||||
this.$emit('drag-start')
|
||||
},
|
||||
dragEnd() {
|
||||
this.$emit('drag-end')
|
||||
++this.blockKey
|
||||
},
|
||||
},
|
||||
components: {
|
||||
BuilderBlockCreate,
|
||||
BuilderBlockSetting,
|
||||
Draggable,
|
||||
},
|
||||
mounted() {
|
||||
this.widget = this.widgets[this.item.widget]
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
<template>
|
||||
<div class="form-group">
|
||||
<label
|
||||
v-if="params.label && params.type !== 'checkbox'"
|
||||
v-text="params.label"
|
||||
>
|
||||
</label>
|
||||
<label class="form-group mb-2">
|
||||
<span v-if="params.label && params.type !== 'checkbox'" v-text="params.label"></span>
|
||||
|
||||
<input
|
||||
v-if="['number', 'checkbox', 'text'].includes(params.type)"
|
||||
|
@ -14,11 +10,7 @@
|
|||
:class="{'form-control': params.type !== 'checkbox'}"
|
||||
/>
|
||||
|
||||
<label
|
||||
v-if="params.label && params.type == 'checkbox'"
|
||||
v-text="params.label"
|
||||
>
|
||||
</label>
|
||||
<span v-if="params.label && params.type == 'checkbox'" v-text="params.label"></span>
|
||||
|
||||
<textarea
|
||||
v-if="params.type == 'textarea'"
|
||||
|
@ -37,7 +29,7 @@
|
|||
{{ v.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -59,3 +51,9 @@ export default {
|
|||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
label > span {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -229,3 +229,6 @@
|
|||
"Medium": "Moyen"
|
||||
"Large": "Large"
|
||||
"Extra large": "Très large"
|
||||
"Level": "Niveau"
|
||||
"Insert line breaks": "Ajouter les retours chariot"
|
||||
'Allow HTML': "Autoriser l'HTML"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<div class="alert {% if settings.level|default(null) %}alert-{{ settings.level }}{% endif %}" id="{{ id }}">
|
||||
{% for item in children %}
|
||||
{{ item|block_to_html }}
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
{%- if settings.nl2br|default(null) -%}
|
||||
{% if settings.allowHtml|default(null) %}
|
||||
{{- settings.value|default(null)|raw|nl2br -}}
|
||||
{%- else -%}
|
||||
{{- settings.value|default(null)|nl2br -}}
|
||||
{%- endif -%}
|
||||
{%- else -%}
|
||||
{% if settings.allowHtml|default(null) %}
|
||||
{{- settings.value|default(null)|raw -}}
|
||||
{%- else -%}
|
||||
{{- settings.value|default(null) -}}
|
||||
{%- endif -%}
|
||||
{% endif %}
|
Loading…
Reference in a new issue