View source
<?php
declare (strict_types=1);
namespace Drupal\ckeditor5\Plugin\CKEditor5Plugin;
use Drupal\ckeditor5\HTMLRestrictions;
use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait;
use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface;
use Drupal\ckeditor5\Plugin\CKEditor5PluginElementsSubsetInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\editor\EditorInterface;
class Style extends CKEditor5PluginDefault implements CKEditor5PluginConfigurableInterface, CKEditor5PluginElementsSubsetInterface {
use CKEditor5PluginConfigurableTrait;
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['styles'] = [
'#title' => $this
->t('Styles'),
'#type' => 'textarea',
'#description' => $this
->t('A list of classes that will be provided in the "Style" dropdown. Enter one or more classes on each line in the format: element.classA.classB|Label. Example: h1.title|Title. Advanced example: h1.fancy.title|Fancy title.<br />These styles should be available in your theme\'s CSS file.'),
];
if (!empty($this->configuration['styles'])) {
$as_selectors = '';
foreach ($this->configuration['styles'] as $style) {
[
$tag,
$classes,
] = self::getTagAndClasses(HTMLRestrictions::fromString($style['element']));
$as_selectors .= sprintf("%s.%s|%s\n", $tag, implode('.', $classes), $style['label']);
}
$form['styles']['#default_value'] = $as_selectors;
}
return $form;
}
public static function getTagAndClasses(HTMLRestrictions $style_element) : array {
$tag = array_keys($style_element
->getAllowedElements())[0];
$classes = array_keys($style_element
->getAllowedElements()[$tag]['class']);
return [
$tag,
$classes,
];
}
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
$form_value = $form_state
->getValue('styles');
[
$styles,
$unparseable_lines,
] = self::parseStylesFormValue($form_value);
if (!empty($unparseable_lines)) {
$line_numbers = array_keys($unparseable_lines);
$form_state
->setError($form['styles'], $this
->formatPlural(count($unparseable_lines), 'Line @line-number does not contain a valid value. Enter a valid CSS selector containing one or more classes, followed by a pipe symbol and a label.', 'Lines @line-numbers do not contain a valid value. Enter a valid CSS selector containing one or more classes, followed by a pipe symbol and a label.', [
'@line-number' => reset($line_numbers),
'@line-numbers' => implode(', ', $line_numbers),
]));
}
$form_state
->setValue('styles', $styles);
}
public static function parseStylesFormValue(string $form_value) : array {
$unparseable_lines = [];
$lines = explode("\n", $form_value);
$styles = [];
foreach ($lines as $index => $line) {
if (empty(trim($line))) {
continue;
}
[
$selector,
$label,
] = array_map('trim', explode('|', $line));
$selector_matches = [];
if (!preg_match('/^([a-z][0-9a-zA-Z\\-]*)((\\.[a-zA-Z0-9\\x{00A0}-\\x{FFFF}\\-_]+)+)$/u', $selector, $selector_matches)) {
$unparseable_lines[$index + 1] = $line;
continue;
}
$tag = $selector_matches[1];
$classes = array_filter(explode('.', $selector_matches[2]));
$normalized = HTMLRestrictions::fromString(sprintf('<%s class="%s">', $tag, implode(' ', $classes)));
$styles[] = [
'label' => $label,
'element' => $normalized
->toCKEditor5ElementsArray()[0],
];
}
return [
$styles,
$unparseable_lines,
];
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['styles'] = $form_state
->getValue('styles');
}
public function defaultConfiguration() {
return [
'styles' => [],
];
}
public function getElementsSubset() : array {
return array_column($this->configuration['styles'], 'element');
}
public function getDynamicPluginConfig(array $static_plugin_config, EditorInterface $editor) : array {
$definitions = [];
foreach ($this->configuration['styles'] as $style) {
[
$tag,
$classes,
] = self::getTagAndClasses(HTMLRestrictions::fromString($style['element']));
$definitions[] = [
'name' => $style['label'],
'element' => $tag,
'classes' => $classes,
];
}
return [
'style' => [
'definitions' => $definitions,
],
];
}
}