View source
<?php
namespace Drupal\charts_chartjs\Plugin\chart\Library;
use Drupal\charts\Plugin\chart\Library\ChartBase;
use Drupal\Component\Utility\Color;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
class Chartjs extends ChartBase {
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$form['placeholder'] = [
'#title' => $this
->t('Placeholder'),
'#type' => 'fieldset',
'#description' => $this
->t('This is a placeholder for Chart.js-specific library options. If you would like to help build this out, please work from <a href="@issue_link">this issue</a>.', [
'@issue_link' => Url::fromUri('https://www.drupal.org/project/charts/issues/3046984')
->toString(),
]),
];
$xaxis_configuration = $this->configuration['xaxis'] ?? [];
$yaxis_configuration = $this->configuration['yaxis'] ?? [];
$form['xaxis'] = [
'#title' => $this
->t('X-Axis Settings'),
'#type' => 'fieldset',
'#tree' => TRUE,
];
$form['xaxis']['autoskip'] = [
'#title' => $this
->t('Enable autoskip'),
'#type' => 'checkbox',
'#default_value' => $xaxis_configuration['autoskip'] ?? 1,
];
$form['xaxis']['horizontal_axis_title_align'] = [
'#title' => $this
->t('Align horizontal axis title'),
'#type' => 'select',
'#options' => [
'start' => $this
->t('Start'),
'center' => $this
->t('Center'),
'end' => $this
->t('End'),
],
'#default_value' => $xaxis_configuration['horizontal_axis_title_align'] ?? '',
];
$form['yaxis'] = [
'#title' => $this
->t('Y-Axis Settings'),
'#type' => 'fieldset',
'#tree' => TRUE,
];
$form['yaxis']['vertical_axis_title_align'] = [
'#title' => $this
->t('Align vertical axis title'),
'#type' => 'select',
'#options' => [
'start' => $this
->t('Start'),
'center' => $this
->t('Center'),
'end' => $this
->t('End'),
],
'#default_value' => $yaxis_configuration['vertical_axis_title_align'] ?? '',
];
return $form;
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::submitConfigurationForm($form, $form_state);
if (!$form_state
->getErrors()) {
$values = $form_state
->getValue($form['#parents']);
$this->configuration['xaxis'] = $values['xaxis'];
$this->configuration['yaxis'] = $values['yaxis'];
}
}
public function preRender(array $element) {
$chart_definition = [];
if (!isset($element['#id'])) {
$element['#id'] = Html::getUniqueId('chartjs-render');
}
$chart_definition = $this
->populateCategories($element, $chart_definition);
$chart_definition = $this
->populateDatasets($element, $chart_definition);
$chart_definition = $this
->populateOptions($element, $chart_definition);
$element['#attached']['library'][] = 'charts_chartjs/chartjs';
$element['#attributes']['class'][] = 'charts-chartjs';
$element['#chart_definition'] = $chart_definition;
return $element;
}
private function populateOptions(array $element, array $chart_definition) {
$chart_type = $this
->populateChartType($element);
$chart_definition['type'] = $chart_type;
$children = Element::children($element);
$x_axis_key = 'xaxis';
$y_axis_key = 'yaxis';
foreach ($children as $child) {
$type = $element[$child]['#type'];
if ($type === 'chart_xaxis') {
$x_axis_key = $child;
}
if ($type === 'chart_yaxis') {
$y_axis_key = $child;
}
}
$xaxis_configuration = $this->configuration['xaxis'] ?? [];
$yaxis_configuration = $this->configuration['yaxis'] ?? [];
if (!in_array($chart_type, [
'pie',
'doughnut',
])) {
if (!empty($element['#stacking']) && $element['#stacking'] == 1) {
$stacking = TRUE;
}
else {
$stacking = FALSE;
}
if ($chart_type !== 'radar') {
$chart_definition['options']['scales']['x'] = [
'stacked' => $stacking,
'ticks' => [
'autoSkip' => $xaxis_configuration['autoskip'] ?? 1,
'maxRotation' => $element[$x_axis_key]['#labels_rotation'] ?? 0,
'minRotation' => $element[$x_axis_key]['#labels_rotation'] ?? 0,
],
];
$chart_definition['options']['scales']['y'] = [
'ticks' => [
'beginAtZero' => NULL,
'maxRotation' => $element[$y_axis_key]['#labels_rotation'] ?? 0,
'minRotation' => $element[$y_axis_key]['#labels_rotation'] ?? 0,
],
'maxTicksLimit' => 11,
'precision' => NULL,
'stepSize' => NULL,
'suggestedMax' => NULL,
'suggestedMin' => NULL,
'stacked' => $stacking,
];
if (!empty($element[$y_axis_key]['#min'])) {
$chart_definition['options']['scales']['y']['min'] = $element[$y_axis_key]['#min'];
}
if (!empty($element[$y_axis_key]['#max'])) {
$chart_definition['options']['scales']['y']['max'] = $element[$y_axis_key]['#max'];
}
if (!empty($element[$y_axis_key]['#title'])) {
$chart_definition['options']['scales']['y']['title']['display'] = TRUE;
$chart_definition['options']['scales']['y']['title']['text'] = $element[$y_axis_key]['#title'];
$chart_definition['options']['scales']['y']['title']['align'] = $yaxis_configuration['vertical_axis_title_align'];
}
if (!empty($element[$x_axis_key]['#title'])) {
$chart_definition['options']['scales']['x']['title']['display'] = TRUE;
$chart_definition['options']['scales']['x']['title']['text'] = $element[$x_axis_key]['#title'];
$chart_definition['options']['scales']['x']['title']['align'] = $xaxis_configuration['horizontal_axis_title_align'];
}
}
}
if ($element['#chart_type'] === 'bar') {
$chart_definition['options']['indexAxis'] = 'y';
}
$chart_definition['options']['plugins']['title'] = $this
->buildTitle($element);
$chart_definition['options']['plugins']['tooltip']['enabled'] = $element['#tooltips'];
$chart_definition['options']['plugins']['legend'] = $this
->buildLegend($element);
if (!empty($element['#raw_options'])) {
$chart_definition = NestedArray::mergeDeepArray([
$chart_definition,
$element['#raw_options'],
]);
}
return $chart_definition;
}
private function populateCategories(array $element, array $chart_definition) {
$children = Element::children($element);
$categories = [];
foreach ($children as $child) {
$type = $element[$child]['#type'];
if ($type === 'chart_xaxis') {
$categories = array_map('strip_tags', $element[$child]['#labels']);
if (!empty($element[$child]['#raw_options'])) {
$categories = NestedArray::mergeDeepArray([
$element[$child]['#raw_options'],
$categories,
]);
}
}
}
$chart_definition['data']['labels'] = $categories;
return $chart_definition;
}
private function populateDatasets(array $element, array $chart_definition) {
$chart_type = $this
->populateChartType($element);
$datasets = [];
foreach (Element::children($element) as $key) {
if ($element[$key]['#type'] === 'chart_data') {
$series_data = [];
$dataset = new \stdClass();
foreach ($element[$key]['#data'] as $data_index => $data) {
if (isset($series_data[$data_index])) {
$series_data[$data_index][] = $data;
}
else {
if ($chart_type === 'scatter') {
$data = [
'y' => $data[1],
'x' => $data[0],
];
}
if (in_array($chart_type, [
'pie',
'doughnut',
]) && !empty($data[1])) {
$data = $data[1];
}
$series_data[$data_index] = $data;
}
}
$dataset->label = $element[$key]['#title'];
$dataset->data = $series_data;
if (!in_array($chart_type, [
'pie',
'doughnut',
])) {
$dataset->borderColor = $element[$key]['#color'];
}
$dataset->backgroundColor = $element[$key]['#color'];
$series_type = isset($element[$key]['#chart_type']) ? $this
->populateChartType($element[$key]) : $chart_type;
$dataset->type = $series_type;
if (!empty($element[$key]['#chart_type']) && $element[$key]['#chart_type'] === 'area') {
$dataset->fill = 'origin';
$dataset->backgroundColor = $this
->getTranslucentColor($element[$key]['#color']);
}
elseif ($element['#chart_type'] === 'area') {
$dataset->fill = 'origin';
$dataset->backgroundColor = $this
->getTranslucentColor($element[$key]['#color']);
}
else {
$dataset->fill = FALSE;
}
$datasets[] = $dataset;
}
if (!empty($element[$key]['#raw_options'])) {
$datasets = NestedArray::mergeDeepArray([
$datasets,
$element[$key]['#raw_options'],
]);
}
}
$chart_definition['data']['datasets'] = $datasets;
return $chart_definition;
}
protected function populateChartType(array $element) {
switch ($element['#chart_type']) {
case 'bar':
case 'column':
$type = 'bar';
break;
case 'area':
case 'spline':
$type = 'line';
break;
case 'donut':
$type = 'doughnut';
break;
case 'gauge':
$type = 'donut';
break;
default:
$type = $element['#chart_type'];
break;
}
if (isset($element['#polar']) && $element['#polar'] == 1) {
$type = 'radar';
}
return $type;
}
protected function buildLegend(array $element) {
$legend = [];
$legend['display'] = (bool) $element['#legend'];
if (!empty($element['#legend_position'])) {
$legend['position'] = $element['#legend_position'];
if (!empty($element['#legend_font_weight'])) {
$legend['labels']['font']['weight'] = $element['#legend_font_weight'];
}
if (!empty($element['#legend_font_style'])) {
$legend['labels']['font']['style'] = $element['#legend_font_style'];
}
if (!empty($element['#legend_font_size'])) {
$legend['labels']['font']['size'] = $element['#legend_font_size'];
}
}
return $legend;
}
protected function buildTitle(array $element) {
$title = [];
if (!empty($element['#title'])) {
$title = [
'display' => TRUE,
'text' => $element['#title'],
];
if (!empty($element['#title_position'])) {
if (in_array($element['#title_position'], [
'in',
'out',
])) {
$title['position'] = 'top';
}
else {
$title['position'] = $element['#title_position'];
}
}
if (!empty($element['#title_color'])) {
$title['color'] = $element['#title_color'];
}
if (!empty($element['#title_font_weight'])) {
$title['font']['weight'] = $element['#title_font_weight'];
}
if (!empty($element['#title_font_style'])) {
$title['font']['style'] = $element['#title_font_style'];
}
if (!empty($element['#title_font_size'])) {
$title['font']['size'] = $element['#title_font_size'];
}
}
return $title;
}
protected function getTranslucentColor($color) {
if (!$color) {
return '';
}
$rgb = Color::hexToRgb($color);
return 'rgba(' . implode(",", $rgb) . ',' . 0.5 . ')';
}
}