Merge branch 'develop'

This commit is contained in:
Simon Vieille 2024-05-13 17:58:11 +02:00
commit 92eaaba699
17 changed files with 208 additions and 110 deletions

View file

@ -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

View 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])
;
}
}

View file

@ -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])

View file

@ -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')

View file

@ -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')

View 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')
;
}
}

View file

@ -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)
{
}

View file

@ -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;
}

View file

@ -3,7 +3,7 @@
namespace App\Core;
if (!defined('MURPH_VERSION')) {
define('MURPH_VERSION', 'v1.25.0');
define('MURPH_VERSION', 'v1.25.1');
}
/**

View file

@ -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;
}
}
}

View file

@ -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()

View file

@ -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">

View file

@ -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]

View file

@ -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>

View file

@ -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"

View file

@ -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>

View file

@ -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 %}