Merge branch 'feature/grapejs' into develop

This commit is contained in:
Simon Vieille 2022-04-12 22:42:18 +02:00
commit f48041eacd
12 changed files with 278 additions and 9 deletions

View file

@ -101,6 +101,7 @@ class PageAdminController extends CrudController
->setForm('edit', Type::class, [])
->setForm('filter', FilterType::class)
->setView('form', '@Core/site/page_admin/_form.html.twig')
->setView('edit', '@Core/site/page_admin/edit.html.twig')
->setAction('index', 'new', false)
->setAction('index', 'show', false)

View file

@ -0,0 +1,21 @@
<?php
namespace App\Core\Form\Site\Page;
use App\Core\Form\Type\GrapesJsType;
use Symfony\Component\Form\FormBuilderInterface;
class GrapesJsBlockType extends TextareaBlockType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'value',
GrapesJsType::class,
array_merge([
'required' => false,
'label' => false,
], $options['options']),
);
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Core\Form\Type;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
class GrapesJsType extends TextareaType
{
/**
* {@inheritdoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
if (!isset($view->vars['attr']['data-grapesjs'])) {
$view->vars['attr']['data-grapesjs'] = '';
}
return parent::buildView($view, $form, $options);
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'grapesjs';
}
}

View file

@ -15,6 +15,7 @@ $pagination-active-bg: #343a40 !default;
@import "~@fortawesome/fontawesome-free/css/all.css";
@import "~flag-icon-css/sass/flag-icon.scss";
@import "~flag-icon-css/sass/flag-icon.scss";
@import '~grapesjs/dist/css/grapes.min.css';
@for $i from 1 through 100 {
.miw-#{$i*5} {
@ -575,3 +576,12 @@ form {
}
}
}
.gjs-editor-cont {
border-radius: 10px;
overflow: hidden !important;
}
.gjs-one-bg {
background: map-get($theme-colors, 'dark-blue');
}

View file

@ -14,6 +14,7 @@ require('./modules/password.js')()
require('./modules/tooltip.js')()
require('./modules/tinymce.js')()
require('./modules/editorjs.js')()
require('./modules/grapesjs.js')()
require('./modules/panel.js')()
require('./modules/choices.js')()
require('./modules/checkbox-checker.js')()
@ -25,3 +26,4 @@ require('./modules/batch.js')()
require('./modules/file-manager.js')()
require('./modules/file-picker.js')()
require('./modules/analytics.js')()
require('./modules/page.js')()

View file

@ -1,7 +1,7 @@
const $ = require('jquery')
const EditorJS = require('@editorjs/editorjs')
const InlineTools = require('editorjs-inline-tool')
const Routing = require('../../../../../../../../friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js')
const Routing = require('../../../../../../../../friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js')
const routes = require('../../../../../../../../../public/js/fos_js_routes.json')
const UnderlineInlineTool = InlineTools.UnderlineInlineTool
@ -84,24 +84,24 @@ const tools = {
bold: {
class: createGenericInlineTool({
sanitize: {
strong: {},
strong: {}
},
shortcut: 'CMD+B',
tagName: 'STRONG',
toolboxIcon:
'<svg class="icon icon--bold" width="12px" height="14px"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#bold"></use></svg>',
}),
'<svg class="icon icon--bold" width="12px" height="14px"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#bold"></use></svg>'
})
},
italic: {
class: createGenericInlineTool({
sanitize: {
em: {},
em: {}
},
shortcut: 'CMD+I',
tagName: 'EM',
toolboxIcon:
'<svg class="icon icon--italic" width="12px" height="14px"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#italic"></use></svg>',
}),
'<svg class="icon icon--italic" width="12px" height="14px"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#italic"></use></svg>'
})
},
underline: UnderlineInlineTool
}

View file

@ -0,0 +1,106 @@
const $ = require('jquery')
const GrapesJs = require('grapesjs')
const bootstrap4 = require('grapesjs-blocks-bootstrap4').default
const makeId = () => {
let result = ''
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'
const charactersLength = characters.length
for (let i = 0; i < 20; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength))
}
return 'grapesjs-' + result
}
const doInitEditor = () => {
$('textarea[data-grapesjs]').each((i, v) => {
const textarea = $(v)
const element = textarea.parent().prev()
const id = element.attr('id') ? element.attr('id') : makeId()
element.attr('id', id)
const editor = GrapesJs.init({
container: '#' + id,
fromElement: false,
height: '900px',
width: 'auto',
storageManager: {
autoload: false
},
noticeOnUnload: 0,
showOffsets: 1,
showDevices: false,
plugins: ['grapesjs-blocks-bootstrap4'],
colorPicker: {
appendTo: 'parent',
offset: {
top: 26,
left: -166
}
},
pluginsOpts: {
'grapesjs-blocks-bootstrap4': {
blocks: {},
blockCategories: {},
labels: {},
gridDevicesPanel: true,
formPredefinedActions: [
]
}
},
canvas: {
styles: [
'https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css'
],
scripts: [
'https://code.jquery.com/jquery-3.5.1.slim.min.js',
'https://unpkg.com/@popperjs/core@2',
'https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js'
]
}
})
const deviceManager = editor.DeviceManager
const devices = [
'Extra Small',
'Small',
'Medium',
'Large',
'Extra Large',
'Desktop',
'Tablet',
'mobileLandscape',
'mobilePortrait'
]
for (const device of devices) {
deviceManager.remove(device)
}
deviceManager.add('All', '100%')
editor.Panels.getPanels().remove('devices-buttons')
editor.on('update', () => {
textarea.val(JSON.stringify(editor.storeData()))
})
console.log(textarea.val())
try {
editor.loadData(JSON.parse(textarea.val()))
} catch (e) {
editor.loadData({ html: '' })
}
})
}
module.exports = () => {
$(() => {
doInitEditor()
})
}

View file

@ -0,0 +1,41 @@
const $ = require('jquery')
const doExpandCollapse = (stmt) => {
stmt = (stmt == 1)
const button = $('#page-form-expand')
const mainForm = $('#page-main-form')
const metasForm = $('#page-metas-form')
mainForm
.toggleClass('col-md-8', !stmt)
.toggleClass('col-md-12', stmt)
metasForm
.toggleClass('d-none', stmt)
button
.children()
.toggleClass('fa-expand-arrows-alt', !stmt)
.toggleClass('fa-compress-arrows-alt', stmt)
localStorage.setItem('pageFormExpandStmt', stmt ? 1 : null)
}
const initExpander = () => {
const button = $('#page-form-expand')
if (button.length) {
doExpandCollapse(localStorage.getItem('pageFormExpandStmt'))
button.click(() => {
doExpandCollapse(button.children().hasClass('fa-expand-arrows-alt'))
})
}
}
module.exports = () => {
$(() => {
initExpander()
})
}

View file

@ -1,5 +1,12 @@
{% extends 'bootstrap_4_layout.html.twig' %}
{% block grapesjs_widget %}
<div class="gjs"></div>
<div class="d-none">
<textarea {{ block('widget_attributes') }}>{{ value }}</textarea>
</div>
{% endblock %}
{% block file_widget -%}
<div class="row">
<div class="col-12">

View file

@ -20,10 +20,10 @@
{% endset %}
<div class="row">
<div class="col-md-8 p-2">
<div class="col-md-8 p-2" id="page-main-form">
{{ form_widget(form, {attr: {class: 'row'}}) }}
</div>
<div class="col-md-4 p-3">
<div class="col-md-4 p-3" id="page-metas-form">
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#form-page-metas">{{ 'Meta datas'|trans }}</a>

View file

@ -0,0 +1,11 @@
{% extends '@Core/admin/crud/edit.html.twig' %}
{% block header %}
{{ parent() }}
<div class="float-right">
<button class="btn btn-sm btn-light mr-2 mt-2" id="page-form-expand">
<span class="fa fa-expand-arrows-alt"></span>
</button>
</div>
{% endblock %}

View file

@ -0,0 +1,40 @@
<?php
namespace App\Core\Twig\Extension;
use App\Core\String\StringBuilder;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\Environment;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class GrapesJsExtension extends AbstractExtension
{
/**
* {@inheritdoc}
*/
public function getFilters()
{
return [
new TwigFilter('grapesjs_html', [$this, 'getHtml']),
new TwigFilter('grapesjs_css', [$this, 'getCss']),
];
}
public function getHtml(?string $value): ?string
{
return $this->extractData($value, 'html');
}
public function getCss(?string $value): ?string
{
return $this->extractData($value, 'css');
}
protected function extractData(?string $value, string $key): ?string
{
$data = json_decode($value, true);
return $data[$key] ?? '';
}
}