Skip to content
Snippets Groups Projects
Commit b54398e1 authored by Steve Wirt's avatar Steve Wirt
Browse files

Issue #3392473 by swirt: Create field, field widget, field formatter and template

parent 81e8ae2e
No related branches found
No related tags found
No related merge requests found
/* Stylesheet for Mermaid Diagram field */
/* Default mermaid elements to hidden, then show with JS to prevent FOUC. */
.mermaid {
/* opacity: 0; @todo Fix that JS is not unhiding this.*/
}
/**
* @file
* Support mermaid rendering.
*/
(function (Drupal, mermaid, $) {
"use strict";
Drupal.behaviors.diagramDisplay = {
attach: function (context) {
// Display mermaid containers after they're rendered.
const mermaids = context.querySelectorAll('.mermaid');
$(mermaids).once('diagram-processed').each(function () {
// Initialize mermaid only if mermaids exist.
if (mermaids.length > 0) {
mermaid.initialize(
{
startOnLoad: true,
mermaid: {
callback: (function() {
return function(id) {
for (let index = 0; index < mermaids.length; index++) {
mermaids[index].style.opacity = '100%';
}
}
})()
}
});
}
});
}
};
})(Drupal, mermaid, jQuery);
name: 'Mermaid Diagram field'
description: 'Adds a Mermaid diagram field capable of rendering Mermaid'
type: module
configure: entity.cm_document.config_form
core_version_requirement: ^9 || ^10
package: 'Field types'
diagram:
version: VERSION
css:
theme:
css/diagram.css: {}
js:
js/diagram.js: {}
dependencies:
- core/drupal
- core/jquery
- mermaid_diagram_field/mermaid
mermaid:
version: 9.1.3
remote: https://github.com/mermaid-js/mermaid
license:
name: MIT
gpl-compatible: false
js:
https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js: { type: external, minified: true }
# /libraries/mermaid/mermaid.min.js: { minified: true }
<?php
/**
* @file
* Drupal hooks for supporting Mermaid Diagram field.
*/
/**
* Implements hook_theme().
*/
function mermaid_diagram_field_theme() {
return [
'mermaid_diagram_field' => [
'render element' => 'elements',
],
'mermaid_diagram' => [
'template' => 'mermaid_diagram',
'variables' => [
'preface' => '',
'title' => t('Diagram'),
'mermaid' => '',
'caption' => '',
'key' => '',
'show_code' => '',
'postface' => '',
],
],
];
}
<?php
namespace Drupal\mermaid_diagram_field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
/**
* Plugin implementation of the Mermaid Diagram formatter.
*
* @FieldFormatter(
* id = "mermaid_diagram_formatter",
* label = @Translation("Mermaid diagram"),
* field_types = {
* "mermaid_diagram"
* }
* )
*/
class MermaidDiagramFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$element = [];
foreach ($items as $delta => $item) {
// Render each element as markup.
$element[$delta] = [
'#theme' => 'mermaid_diagram',
'#mermaid' => $item->diagram,
'#title' => $item->title,
'#caption' => $item->caption,
'#attached' => ['library' => 'mermaid_diagram_field/diagram'],
'#key' => $item->key,
'#show_code' => $item->show_code,
];
}
return $element;
}
}
<?php
namespace Drupal\mermaid_diagram_field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Provides a field type of MermaidDiagram.
*
* @FieldType(
* id = "mermaid_diagram",
* label = @Translation("Mermaid diagram"),
* default_formatter = "mermaid_diagram_formatter",
* default_widget = "mermaid_diagram_widget",
* )
*/
class MermaidDiagramItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return [
'columns' => [
// List the values that the field will save.
'title' => [
'type' => 'varchar',
'length' => '255',
'not null' => FALSE,
],
'diagram' => [
'type' => 'text',
'size' => 'medium',
'not null' => FALSE,
],
'caption' => [
'type' => 'text',
'size' => 'medium',
'not null' => FALSE,
],
'key' => [
'type' => 'text',
'size' => 'small',
'not null' => FALSE,
],
// Seems wrong to have to use int in place of boolean.
'show_code' => [
'type' => 'int',
'size' => 'tiny',
'not null' => FALSE,
],
],
];
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = [];
$properties['title'] = DataDefinition::create('string')
->setLabel(t('Title'))
->setRequired(TRUE);
$properties['caption'] = DataDefinition::create('string')
->setLabel(t('Caption'))
->setRequired(TRUE);
$properties['diagram'] = DataDefinition::create('string')
->setLabel(t('Mermaid code'))
->setRequired(TRUE);
$properties['show_code'] = DataDefinition::create('string')
->setLabel(t('Expose the code'))
->setRequired(FALSE);
return $properties;
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
$title = $this->get('title')->getValue();
$caption = $this->get('caption')->getValue();
$diagram = $this->get('diagram')->getValue();
// Whether is has show_code or not should not determine emptiness.
return (empty($title)) && (empty($caption)) && (empty($diagram));
}
}
<?php
namespace Drupal\mermaid_diagram_field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* A Mermaid diagram widget.
*
* @FieldWidget(
* id = "mermaid_diagram_widget",
* label = @Translation("Mermaid diagram widget"),
* field_types = {
* "mermaid_diagram",
* }
* )
*/
class MermaidDiagramWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['title'] = [
'#type' => 'textfield',
'#size' => 60,
'#title' => $this->t('Title'),
'#default_value' => $items[$delta]->title ?? NULL,
];
//@todo workout required states.
$element['diagram'] = [
'#type' => 'textarea',
'#title' => $this->t('Mermaid code'),
'#description' => t('Must be valid Mermaid code. <a href="@mermaid-live" target="_blank">Mermaid Live editor (opens in new tab)</a>.', array('@mermaid-live' => 'https://mermaid.live/')),
'#default_value' => $items[$delta]->diagram ?? NULL,
];
$element['key'] = [
'#type' => 'textarea',
'#title' => $this->t('Key'),
'#description' => t('Must be valid Mermaid code. <a href="@mermaid-live" target="_blank">Mermaid Live editor (opens in new tab)</a>.', array('@mermaid-live' => 'https://mermaid.live/')),
'#default_value' => $items[$delta]->key ?? NULL,
];
$element['caption'] = [
'#type' => 'textarea',
'#title' => $this->t('The caption used to describe the diagram.'),
'#default_value' => $items[$delta]->caption ?? NULL,
];
$element['show_code'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show the raw Mermaid code.'),
'#default_value' => $items[$delta]->show_code ?? NULL,
];
return $element;
}
}
{{ preface }}
{% if mermaid %}
<figure class="mermaid-diagram">
<h2>{{ title }}</h2>
<div class="mermaid">
{{ mermaid }}
</div>
{% if caption %}
<figcaption>{{ caption }}</figcaption>
{% endif %}
{% if key %}
<h3>Key</h3>
<div class="mermaid">
{{ key }}
</div>
{% endif %}
</figure>
{% endif %}
{% if show_code and mermaid %}
<details class="mermaid-code">
<summary role="button">Mermaid code</summary>
<pre>
<code>
## Diagram code ##
{{ mermaid }}
## Key code ##
{{ key }}
</code>
</pre>
</details>
{% endif %}
{{ postface }}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment