Skip to content

Commit

Permalink
Added a "code editor" form type
Browse files Browse the repository at this point in the history
  • Loading branch information
javiereguiluz committed Jun 26, 2019
1 parent 151b83c commit caa3a36
Show file tree
Hide file tree
Showing 21 changed files with 293 additions and 5 deletions.
5 changes: 5 additions & 0 deletions assets/css/easyadmin-theme/forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -468,3 +468,8 @@ form .invalid-feedback > .d-block + .d-block {
text-align: right;
top: 0;
}

// EasyAdminCodeType
.field-easyadmin_code_editor .form-widget {
flex-basis: 65%;
}
24 changes: 24 additions & 0 deletions assets/css/form-type-code-editor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@import '~codemirror/lib/codemirror.css';

.CodeMirror {
font: 13px/1.5 SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Ubuntu Mono", "Courier New", monospace;
height: auto;
min-height: 45px;
}

.CodeMirror-wrap {
box-shadow: 0 0 0 1px rgba(43, 45, 80, .16), 0 0 0 1px rgba(6, 122, 184, 0), 0 0 0 2px rgba(6, 122, 184, 0), 0 1px 1px rgba(0, 0, 0, .08);;
border-radius: var(--border-radius);
}

.CodeMirror-gutters {
background: var(--gray-100);
}

.CodeMirror-linenumber {
color: var(--text-muted);
}

.CodeMirror-lines {
padding-bottom: 5px;
}
21 changes: 21 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ window.addEventListener('load', function() {
$(document).on('easyadmin.collection.item-added', createAutoCompleteFields);
createContentResizer();
createNavigationToggler();
createCodeEditorFields();
});

function createNullableControls() {
Expand Down Expand Up @@ -138,3 +139,23 @@ function createNavigationToggler() {
}
});
}

// Code editor fields require extra JavaScript dependencies, which are loaded
// dynamically only when there are code editor fields in the page
function createCodeEditorFields()
{
const codeEditorElements = document.querySelectorAll('[data-easyadmin-code-editor]');
if (codeEditorElements.length === 0) {
return;
}

const codeEditorJs = document.createElement('script');
codeEditorJs.setAttribute('src', codeEditorElements[0].dataset.jsUrl);

const codeEditorCss = document.createElement('link');
codeEditorCss.setAttribute('rel', 'stylesheet');
codeEditorCss.setAttribute('href', codeEditorElements[0].dataset.cssUrl);

document.querySelector('head').appendChild(codeEditorCss);
document.querySelector('body').appendChild(codeEditorJs);
}
31 changes: 31 additions & 0 deletions assets/js/form-type-code-editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require('../css/form-type-code-editor.css');

import CodeMirror from 'codemirror';

import 'codemirror/mode/css/css';
import 'codemirror/mode/dockerfile/dockerfile';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/mode/nginx/nginx';
import 'codemirror/mode/php/php';
import 'codemirror/mode/shell/shell';
import 'codemirror/mode/sql/sql';
import 'codemirror/mode/twig/twig';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/yaml-frontmatter/yaml-frontmatter';
import 'codemirror/mode/yaml/yaml';

document.querySelectorAll('[data-easyadmin-code-editor]').forEach(function(codeBlock) {
CodeMirror.fromTextArea(codeBlock, {
autocapitalize: false,
autocorrect: false,
indentWithTabs: codeBlock.dataset.indentWithTabs,
lineNumbers: true,
lineWrapping: true,
mode: codeBlock.dataset.language,
scrollbarStyle: 'native',
spellcheck: false,
tabSize: codeBlock.dataset.tabSize,
theme: 'default',
});
});
54 changes: 52 additions & 2 deletions doc/book/edit-new-configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,8 @@ These are the options that you can define for each field:
done internally by the bundle). The allowed values are:

* Any of the `Symfony Form types`_.
* Any of the custom EasyAdmin form types: ``easyadmin_autocomplete`` (they are
explained later in this chapter).
* Any of the custom EasyAdmin form types: ``code_editor``, ``easyadmin_autocomplete``
(they are explained later in this chapter).
* ``type_options`` (optional), a hash with the options passed to the Symfony
Form type used to render the field.

Expand Down Expand Up @@ -555,6 +555,56 @@ change this value (globally or per entity):
max_results: 5
# ...
Code Editor
-----------

It displays a JavaScript-based editor for source code. It provides advanced
features such as code highlighting and smart indenting.

.. code-block:: yaml
# config/packages/easy_admin.yaml
easy_admin:
entities:
Server:
class: App\Entity\Server
form:
fields:
- { property: 'config', type: 'code_editor', language: 'nginx' }
# ...
# ...
This type defines the following configuration options:

* ``height``: the initial height of code blocks is the same as their contents
and it grows automatically as you add more contents. This option, which must
be an integer, sets the height of the code block element in pixels. If
contents don't fit, a scrollbar is displayed.
* ``language``: sets the programming language used for the syntax highlighting
of the code (the language can't be autodetected from the contents). The available
languages are: ``css``, ``dockerfile``, ``js`` (equivalent to ``javascript``),
``markdown``, ``nginx``, ``php``, ``shell``, ``sql``, ``twig``, ``xml``,
``yaml-frontmatter`` (used in some blogs, CMS systems and static site
generators), ``yaml``.
* ``tab_size``: an integer (default: ``4``) that defines the indention size (no
matter if the code uses white spaces or tabs).
* ``indent_with_tabs``: if this boolean option is set to ``true``, code is
indented with real tabs instead of white spaces (default: ``false``).

.. code-block:: yaml
# config/packages/easy_admin.yaml
easy_admin:
entities:
ExamQuestion:
class: App\Entity\ExamQuestion
form:
fields:
- { property: 'question', type: 'code_editor', language: 'yaml', height: 150, tab_size: 4 }
- { property: 'codeSample', type: 'code_editor', language: 'php', height: 600, tab_size: 2 }
# ...
# ...
.. _edit-new-advanced-form-design:

Advanced Form Design
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"@symfony/webpack-encore": "^0.21",
"bootstrap": "^4.1.0",
"bootstrap-rtl": "^3.3.4",
"codemirror": "^5.47.0",
"cssnano": "^4.1.7",
"featherlight": "^1.7.13",
"jquery": "^3.3.1",
Expand Down
72 changes: 72 additions & 0 deletions src/Form/Type/CodeEditorType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace EasyCorp\Bundle\EasyAdminBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* @author Yonel Ceruto <yonelceruto@gmail.com>
*/
class CodeEditorType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options): void
{
$view->vars['height'] = $options['height'];
$view->vars['tabSize'] = $options['tab_size'];
$view->vars['indentWithTabs'] = $options['indent_with_tabs'];
$view->vars['language'] = $options['language'];
}

/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'height' => null,
'tab_size' => 4,
'indent_with_tabs' => false,
// the code editor can't autodetect the language, so 'markdown' is used when
// no language is selected explicitly (because it's the most similar to regular text)
'language' => 'markdown',
]);
$resolver->setAllowedTypes('height', ['null', 'int']);
$resolver->setAllowedTypes('tab_size', 'int');
$resolver->setAllowedTypes('indent_with_tabs', 'bool');
$resolver->setAllowedTypes('language', 'string');
$resolver->setAllowedValues('language', ['css', 'dockerfile', 'js', 'javascript', 'markdown', 'nginx', 'php', 'shell', 'sql', 'twig', 'xml', 'yaml-frontmatter', 'yaml']);

// define some programming language shortcuts for better UX (e.g. 'js' === 'javascript')
$resolver->setNormalizer('language', static function (Options $options, $language) {
if ('js' === $language) {
$language = 'javascript';
}

return $language;
});
}

/**
* {@inheritdoc}
*/
public function getParent(): string
{
return TextareaType::class;
}

/**
* {@inheritdoc}
*/
public function getBlockPrefix(): string
{
return 'easyadmin_code_editor';
}
}
43 changes: 43 additions & 0 deletions src/Form/Type/Configurator/CodeEditorTypeConfigurator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace EasyCorp\Bundle\EasyAdminBundle\Form\Type\Configurator;

use EasyCorp\Bundle\EasyAdminBundle\Form\Type\CodeEditorType;
use Symfony\Component\Form\FormConfigInterface;

/**
* This configurator is applied to any form field of type 'code_editor'.
*
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
*/
class CodeEditorTypeConfigurator implements TypeConfiguratorInterface
{
/**
* {@inheritdoc}
*/
public function configure($name, array $options, array $metadata, FormConfigInterface $parentConfig): array
{
if (isset($metadata['height'])) {
$options['height'] = $metadata['height'];
}
if (isset($metadata['tab_size'])) {
$options['tab_size'] = $metadata['tab_size'];
}
if (isset($metadata['indent_with_tabs'])) {
$options['indent_with_tabs'] = $metadata['indent_with_tabs'];
}
if (isset($metadata['language'])) {
$options['language'] = $metadata['language'];
}

return $options;
}

/**
* {@inheritdoc}
*/
public function supports($type, array $options, array $metadata): bool
{
return \in_array($type, ['code_editor', CodeEditorType::class], true);
}
}
4 changes: 4 additions & 0 deletions src/Form/Util/FormTypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace EasyCorp\Bundle\EasyAdminBundle\Form\Util;

use EasyCorp\Bundle\EasyAdminBundle\Form\Type\CodeEditorType;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\EasyAdminAutocompleteType;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\EasyAdminDividerType;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\EasyAdminFormType;
Expand Down Expand Up @@ -59,6 +60,9 @@ final class FormTypeHelper
'button' => ButtonType::class,
'checkbox' => CheckboxType::class,
'choice' => ChoiceType::class,
// allow using underscore and dashes to improve DX
'code-editor' => CodeEditorType::class,
'code_editor' => CodeEditorType::class,
'collection' => CollectionType::class,
'color' => ColorType::class,
'country' => CountryType::class,
Expand Down
5 changes: 5 additions & 0 deletions src/Resources/config/form.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@

<!-- Type Configurators -->

<service id="easyadmin.form.type.configurator.code_editor" public="false"
class="EasyCorp\Bundle\EasyAdminBundle\Form\Type\Configurator\CodeEditorTypeConfigurator">
<tag name="easyadmin.form.type.configurator" priority="50" />
</service>

<service id="easyadmin.form.type.configurator.textarea" public="false"
class="EasyCorp\Bundle\EasyAdminBundle\Form\Type\Configurator\TextareaTypeConfigurator">
<tag name="easyadmin.form.type.configurator" priority="40" />
Expand Down
2 changes: 1 addition & 1 deletion src/Resources/public/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Resources/public/app.css.map

Large diffs are not rendered by default.

0 comments on commit caa3a36

Please sign in to comment.