diff --git a/src/core/Form/Site/Page/GrapesJsBlockType.php b/src/core/Form/Site/Page/GrapesJsBlockType.php new file mode 100644 index 0000000..bc16e93 --- /dev/null +++ b/src/core/Form/Site/Page/GrapesJsBlockType.php @@ -0,0 +1,21 @@ +add( + 'value', + GrapesJsType::class, + array_merge([ + 'required' => false, + 'label' => false, + ], $options['options']), + ); + } +} diff --git a/src/core/Form/Type/GrapesJsType.php b/src/core/Form/Type/GrapesJsType.php new file mode 100644 index 0000000..1e8b9d6 --- /dev/null +++ b/src/core/Form/Type/GrapesJsType.php @@ -0,0 +1,30 @@ +vars['attr']['data-grapesjs'])) { + $view->vars['attr']['data-grapesjs'] = ''; + } + + return parent::buildView($view, $form, $options); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() + { + return 'grapesjs'; + } +} diff --git a/src/core/Resources/assets/css/admin.scss b/src/core/Resources/assets/css/admin.scss index bf8a81f..04fd863 100644 --- a/src/core/Resources/assets/css/admin.scss +++ b/src/core/Resources/assets/css/admin.scss @@ -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'); +} diff --git a/src/core/Resources/assets/js/modules/editorjs.js b/src/core/Resources/assets/js/modules/editorjs.js index 2f5e225..a5e9dcf 100644 --- a/src/core/Resources/assets/js/modules/editorjs.js +++ b/src/core/Resources/assets/js/modules/editorjs.js @@ -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: - '', - }), + '' + }) }, italic: { class: createGenericInlineTool({ sanitize: { - em: {}, + em: {} }, shortcut: 'CMD+I', tagName: 'EM', toolboxIcon: - '', - }), + '' + }) }, underline: UnderlineInlineTool } diff --git a/src/core/Resources/assets/js/modules/grapesjs.js b/src/core/Resources/assets/js/modules/grapesjs.js new file mode 100644 index 0000000..1113167 --- /dev/null +++ b/src/core/Resources/assets/js/modules/grapesjs.js @@ -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() + }) +} 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 870f728..d7210b0 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,12 @@ {% extends 'bootstrap_4_layout.html.twig' %} +{% block grapesjs_widget %} +
+
+ +
+{% endblock %} + {% block file_widget -%}
diff --git a/src/core/Twig/Extension/GrapesJsExtension.php b/src/core/Twig/Extension/GrapesJsExtension.php new file mode 100644 index 0000000..6e95def --- /dev/null +++ b/src/core/Twig/Extension/GrapesJsExtension.php @@ -0,0 +1,40 @@ +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] ?? ''; + } +}