Added support for front-end rendering
This commit is contained in:
@ -3,12 +3,13 @@
|
||||
"apiVersion": 2,
|
||||
"name": "lcp/bar-graph",
|
||||
"version": "0.1.0",
|
||||
"title": "LCP Bar Graph",
|
||||
"category": "widgets",
|
||||
"title": "Bar Graph",
|
||||
"category": "lcp-blocks",
|
||||
"icon": "chart-bar",
|
||||
"description": "Display data in a bar graph format.",
|
||||
"description": "Display data as a bar graph",
|
||||
"supports": {
|
||||
"html": false
|
||||
"html": false,
|
||||
"anchor": true
|
||||
},
|
||||
"textdomain": "lcp",
|
||||
"editorScript": "file:./index.js",
|
||||
@ -24,6 +25,14 @@
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"chartData": {
|
||||
"type": "object",
|
||||
"default": {}
|
||||
},
|
||||
"dataSource": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"showBarValues": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
@ -60,14 +69,6 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"chartData": {
|
||||
"type": "object",
|
||||
"default": {}
|
||||
},
|
||||
"dataSource": {
|
||||
"type": "string",
|
||||
"default": "manual_json"
|
||||
},
|
||||
"barColor": {
|
||||
"type": "string",
|
||||
"default": "#0073aa"
|
||||
|
||||
@ -1 +1 @@
|
||||
<?php return array('dependencies' => array(), 'version' => '7ca23fe6db2e4bfa97aa');
|
||||
<?php return array('dependencies' => array(), 'version' => '70f08b60c296a36b16fe');
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -51,24 +51,96 @@ function lcp_render_bar_graph_block($attributes, $content) {
|
||||
}
|
||||
|
||||
// Get attributes with defaults
|
||||
$chart_height = $attributes['chartHeight'] ?? '400px';
|
||||
$chart_height = intval($attributes['chartHeight'] ?? 400);
|
||||
$chart_width = intval($attributes['chartWidth'] ?? 600);
|
||||
$background_color = $attributes['backgroundColor'] ?? '#ffffff';
|
||||
$bar_color = $attributes['barColor'] ?? '#0073aa';
|
||||
$bar_opacity = $attributes['barOpacity'] ?? 1;
|
||||
$bar_opacity = floatval($attributes['barOpacity'] ?? 0.8);
|
||||
$show_grid_y = $attributes['showGridY'] ?? true;
|
||||
$grid_color = $attributes['gridColor'] ?? '#e0e0e0';
|
||||
$show_grid_x = $attributes['showGridX'] ?? true;
|
||||
$x_grid_color = $attributes['xGridColor'] ?? '#e0e0e0';
|
||||
$y_grid_color = $attributes['yGridColor'] ?? '#e0e0e0';
|
||||
$x_grid_width = intval($attributes['xGridWidth'] ?? 1);
|
||||
$y_grid_width = intval($attributes['yGridWidth'] ?? 1);
|
||||
$show_bar_values = $attributes['showBarValues'] ?? false;
|
||||
$display_chart_title = $attributes['displayChartTitle'] ?? false;
|
||||
$chart_title = $attributes['chartTitle'] ?? '';
|
||||
$x_axis_label = $attributes['xAxisLabel'] ?? '';
|
||||
$y_axis_label = $attributes['yAxisLabel'] ?? '';
|
||||
$chart_color_source = $attributes['chartColorSource'] ?? 'default';
|
||||
$chart_data = $attributes['chartData'] ?? array();
|
||||
$chart_custom_colors = $attributes['chartCustomColors'] ?? array();
|
||||
|
||||
// Ensure chart data is properly formatted
|
||||
if (is_string($chart_data)) {
|
||||
$chart_data = json_decode($chart_data, true) ?? array();
|
||||
}
|
||||
|
||||
if (is_string($chart_custom_colors)) {
|
||||
$chart_custom_colors = json_decode($chart_custom_colors, true) ?? array();
|
||||
}
|
||||
|
||||
// Create unique ID for this graph instance
|
||||
$block_id = 'bar-graph-' . uniqid();
|
||||
|
||||
// Prepare data for JavaScript
|
||||
$graph_data = array(
|
||||
'blockId' => $block_id,
|
||||
'attributes' => array(
|
||||
'chartHeight' => $chart_height,
|
||||
'chartWidth' => $chart_width,
|
||||
'backgroundColor' => $background_color,
|
||||
'barColor' => $bar_color,
|
||||
'barOpacity' => $bar_opacity,
|
||||
'showGridY' => $show_grid_y,
|
||||
'showGridX' => $show_grid_x,
|
||||
'xGridColor' => $x_grid_color,
|
||||
'yGridColor' => $y_grid_color,
|
||||
'xGridWidth' => $x_grid_width,
|
||||
'yGridWidth' => $y_grid_width,
|
||||
'showBarValues' => $show_bar_values,
|
||||
'displayChartTitle' => $display_chart_title,
|
||||
'chartTitle' => $chart_title,
|
||||
'xAxisLabel' => $x_axis_label,
|
||||
'yAxisLabel' => $y_axis_label,
|
||||
'chartColorSource' => $chart_color_source,
|
||||
'chartData' => $chart_data,
|
||||
'chartCustomColors' => $chart_custom_colors
|
||||
)
|
||||
);
|
||||
|
||||
// Enqueue D3.js
|
||||
wp_enqueue_script('d3');
|
||||
wp_enqueue_script(
|
||||
'lcp-d3',
|
||||
'https://d3js.org/d3.v7.min.js',
|
||||
array(),
|
||||
'7.0.0',
|
||||
true
|
||||
);
|
||||
|
||||
// Return the block's container
|
||||
// Enqueue our view script
|
||||
wp_enqueue_script(
|
||||
'lcp-bar-graph-view',
|
||||
plugins_url('build/view.js', __FILE__),
|
||||
array('lcp-d3'),
|
||||
filemtime(plugin_dir_path(__FILE__) . 'build/view.js'),
|
||||
true
|
||||
);
|
||||
|
||||
// Pass data to JavaScript
|
||||
wp_add_inline_script(
|
||||
'lcp-bar-graph-view',
|
||||
'window.lcpBarGraphData = window.lcpBarGraphData || {};' .
|
||||
'window.lcpBarGraphData["' . $block_id . '"] = ' . wp_json_encode($graph_data) . ';',
|
||||
'before'
|
||||
);
|
||||
|
||||
// Return the block's container with responsive width
|
||||
return sprintf(
|
||||
'<div class="wp-block-lcp-bar-graph"><div class="lcp-bar-graph" style="height: %s; background-color: %s;" data-bar-color="%s" data-bar-opacity="%f" data-show-grid="%s" data-grid-color="%s"></div></div>',
|
||||
esc_attr($chart_height),
|
||||
esc_attr($background_color),
|
||||
esc_attr($bar_color),
|
||||
esc_attr($bar_opacity),
|
||||
esc_attr($show_grid_y ? 'true' : 'false'),
|
||||
esc_attr($grid_color)
|
||||
'<div class="wp-block-lcp-bar-graph" id="%s">
|
||||
<div class="bar-graph-container" style="width: 100%%; height: %dpx;"></div>
|
||||
</div>',
|
||||
esc_attr($block_id),
|
||||
esc_attr($chart_height)
|
||||
);
|
||||
}
|
||||
|
||||
82
blocks/bar-graph/render.php
Normal file
82
blocks/bar-graph/render.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
function render_bar_graph_block($attributes, $content) {
|
||||
// Ensure all required attributes have default values
|
||||
$defaults = array(
|
||||
'chartHeight' => 400,
|
||||
'chartWidth' => 600,
|
||||
'barColor' => '#FF6384',
|
||||
'barOpacity' => 0.8,
|
||||
'backgroundColor' => '#FFFFFF',
|
||||
'showGridY' => true,
|
||||
'showGridX' => true,
|
||||
'yGridColor' => '#e0e0e0',
|
||||
'xGridColor' => '#e0e0e0',
|
||||
'xGridWidth' => 1,
|
||||
'yGridWidth' => 1,
|
||||
'chartData' => array(),
|
||||
'displayChartTitle' => false,
|
||||
'chartTitle' => '',
|
||||
'showBarValues' => false,
|
||||
'xAxisLabel' => '',
|
||||
'yAxisLabel' => '',
|
||||
'chartColorSource' => 'default',
|
||||
'chartCustomColors' => array()
|
||||
);
|
||||
|
||||
// Merge provided attributes with defaults
|
||||
$attributes = wp_parse_args($attributes, $defaults);
|
||||
|
||||
// If chartData is a string (JSON), decode it
|
||||
if (is_string($attributes['chartData'])) {
|
||||
$attributes['chartData'] = json_decode($attributes['chartData'], true);
|
||||
}
|
||||
|
||||
// If chartCustomColors is a string (JSON), decode it
|
||||
if (is_string($attributes['chartCustomColors'])) {
|
||||
$attributes['chartCustomColors'] = json_decode($attributes['chartCustomColors'], true);
|
||||
}
|
||||
|
||||
// Create a unique ID for this instance of the graph
|
||||
$block_id = 'bar-graph-' . uniqid();
|
||||
|
||||
// Prepare the data for JavaScript
|
||||
$js_data = array(
|
||||
'attributes' => $attributes,
|
||||
'blockId' => $block_id
|
||||
);
|
||||
|
||||
// Enqueue D3.js if not already enqueued
|
||||
wp_enqueue_script(
|
||||
'lcp-d3',
|
||||
'https://d3js.org/d3.v7.min.js',
|
||||
array(),
|
||||
'7.0.0',
|
||||
true
|
||||
);
|
||||
|
||||
// Enqueue the view script if not already enqueued
|
||||
wp_enqueue_script(
|
||||
'lcp-bar-graph-view',
|
||||
plugins_url('build/view.js', __FILE__),
|
||||
array('lcp-d3'),
|
||||
filemtime(plugin_dir_path(__FILE__) . 'build/view.js'),
|
||||
true
|
||||
);
|
||||
|
||||
// Pass the data to JavaScript
|
||||
wp_add_inline_script(
|
||||
'lcp-bar-graph-view',
|
||||
'window.lcpBarGraphData = window.lcpBarGraphData || {};' .
|
||||
'window.lcpBarGraphData["' . $block_id . '"] = ' . wp_json_encode($js_data) . ';',
|
||||
'before'
|
||||
);
|
||||
|
||||
// Return the container for the graph
|
||||
return sprintf(
|
||||
'<div class="wp-block-lcp-bar-graph" id="%s">
|
||||
<div class="bar-graph-container" style="min-height:%dpx"></div>
|
||||
</div>',
|
||||
esc_attr($block_id),
|
||||
esc_attr($attributes['chartHeight'])
|
||||
);
|
||||
}
|
||||
@ -3,12 +3,13 @@
|
||||
"apiVersion": 2,
|
||||
"name": "lcp/bar-graph",
|
||||
"version": "0.1.0",
|
||||
"title": "LCP Bar Graph",
|
||||
"category": "widgets",
|
||||
"title": "Bar Graph",
|
||||
"category": "lcp-blocks",
|
||||
"icon": "chart-bar",
|
||||
"description": "Display data in a bar graph format.",
|
||||
"description": "Display data as a bar graph",
|
||||
"supports": {
|
||||
"html": false
|
||||
"html": false,
|
||||
"anchor": true
|
||||
},
|
||||
"textdomain": "lcp",
|
||||
"editorScript": "file:./index.js",
|
||||
@ -24,6 +25,14 @@
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"chartData": {
|
||||
"type": "object",
|
||||
"default": {}
|
||||
},
|
||||
"dataSource": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"showBarValues": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
@ -60,14 +69,6 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"chartData": {
|
||||
"type": "object",
|
||||
"default": {}
|
||||
},
|
||||
"dataSource": {
|
||||
"type": "string",
|
||||
"default": "manual_json"
|
||||
},
|
||||
"barColor": {
|
||||
"type": "string",
|
||||
"default": "#0073aa"
|
||||
|
||||
@ -20,6 +20,192 @@
|
||||
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#view-script
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
console.log( 'Hello World! (from create-block-todo-list block)' );
|
||||
/* eslint-enable no-console */
|
||||
import * as d3 from 'd3';
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
// Check if we have any bar graphs to render
|
||||
if (!window.lcpBarGraphData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Render each bar graph
|
||||
Object.entries(window.lcpBarGraphData).forEach(([blockId, data]) => {
|
||||
const { attributes } = data;
|
||||
const container = document.getElementById(blockId);
|
||||
if (!container) return;
|
||||
|
||||
renderBarGraph(container, attributes);
|
||||
});
|
||||
});
|
||||
|
||||
function renderBarGraph(container, attrs) {
|
||||
// Clear any existing content
|
||||
const graphContainer = container.querySelector('.bar-graph-container');
|
||||
if (!graphContainer) return;
|
||||
graphContainer.innerHTML = '';
|
||||
|
||||
// Get the actual width of the container
|
||||
const containerWidth = graphContainer.clientWidth;
|
||||
|
||||
// Set up dimensions
|
||||
const margin = { top: 40, right: 20, bottom: 60, left: 60 };
|
||||
const width = containerWidth - margin.left - margin.right;
|
||||
const height = attrs.chartHeight - margin.top - margin.bottom;
|
||||
|
||||
// Convert data to array format
|
||||
const chartDataArray = [];
|
||||
if (attrs.chartData && typeof attrs.chartData === 'object') {
|
||||
Object.entries(attrs.chartData).forEach(([dataset, items]) => {
|
||||
if (Array.isArray(items)) {
|
||||
items.forEach(item => {
|
||||
chartDataArray.push({
|
||||
dataset,
|
||||
label: item.label || '',
|
||||
value: parseFloat(item.value) || 0,
|
||||
color: item.color || attrs.barColor
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create SVG
|
||||
const svg = d3.select(graphContainer)
|
||||
.append('svg')
|
||||
.attr('width', containerWidth)
|
||||
.attr('height', attrs.chartHeight)
|
||||
.style('background-color', attrs.backgroundColor);
|
||||
|
||||
const g = svg.append('g')
|
||||
.attr('transform', `translate(${margin.left},${margin.top})`);
|
||||
|
||||
// Create scales
|
||||
const xScale = d3.scaleBand()
|
||||
.domain(chartDataArray.map(d => d.label))
|
||||
.range([0, width])
|
||||
.padding(0.1);
|
||||
|
||||
const yScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(chartDataArray, d => d.value) || 100])
|
||||
.range([height, 0]);
|
||||
|
||||
// Get color for a datapoint
|
||||
const getColor = (datapoint) => {
|
||||
switch (attrs.chartColorSource) {
|
||||
case 'singleColor':
|
||||
return attrs.barColor;
|
||||
case 'customColors':
|
||||
const customColor = attrs.chartCustomColors?.find(
|
||||
c => c.dataset === datapoint.dataset && c.label === datapoint.label
|
||||
);
|
||||
return customColor ? customColor.color : datapoint.color;
|
||||
case 'default':
|
||||
default:
|
||||
return datapoint.color;
|
||||
}
|
||||
};
|
||||
|
||||
// Add X grid
|
||||
if (attrs.showGridX) {
|
||||
g.append('g')
|
||||
.attr('class', 'grid x-grid')
|
||||
.attr('transform', `translate(0,${height})`)
|
||||
.style('color', attrs.xGridColor)
|
||||
.style('stroke-width', attrs.xGridWidth)
|
||||
.call(d3.axisBottom(xScale)
|
||||
.tickSize(-height)
|
||||
.tickFormat(''));
|
||||
}
|
||||
|
||||
// Add Y grid
|
||||
if (attrs.showGridY) {
|
||||
g.append('g')
|
||||
.attr('class', 'grid y-grid')
|
||||
.style('color', attrs.yGridColor)
|
||||
.style('stroke-width', attrs.yGridWidth)
|
||||
.call(d3.axisLeft(yScale)
|
||||
.tickSize(-width)
|
||||
.tickFormat(''));
|
||||
}
|
||||
|
||||
// Add bars
|
||||
g.selectAll('rect')
|
||||
.data(chartDataArray)
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('x', d => xScale(d.label))
|
||||
.attr('y', d => yScale(d.value))
|
||||
.attr('width', xScale.bandwidth())
|
||||
.attr('height', d => height - yScale(d.value))
|
||||
.attr('fill', d => getColor(d))
|
||||
.style('opacity', attrs.barOpacity);
|
||||
|
||||
// Add bar values
|
||||
if (attrs.showBarValues) {
|
||||
g.selectAll('.bar-value')
|
||||
.data(chartDataArray)
|
||||
.enter()
|
||||
.append('text')
|
||||
.attr('class', 'bar-value')
|
||||
.attr('x', d => xScale(d.label) + xScale.bandwidth() / 2)
|
||||
.attr('y', d => yScale(d.value) - 5)
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(d => d.value);
|
||||
}
|
||||
|
||||
// Add X axis
|
||||
g.append('g')
|
||||
.attr('class', 'x-axis')
|
||||
.attr('transform', `translate(0,${height})`)
|
||||
.call(d3.axisBottom(xScale))
|
||||
.selectAll('text')
|
||||
.style('text-anchor', 'end')
|
||||
.attr('dx', '-.8em')
|
||||
.attr('dy', '.15em')
|
||||
.attr('transform', 'rotate(-45)');
|
||||
|
||||
// Add Y axis
|
||||
g.append('g')
|
||||
.attr('class', 'y-axis')
|
||||
.call(d3.axisLeft(yScale));
|
||||
|
||||
// Add title
|
||||
if (attrs.displayChartTitle && attrs.chartTitle) {
|
||||
svg.append('text')
|
||||
.attr('x', containerWidth / 2)
|
||||
.attr('y', margin.top / 2)
|
||||
.attr('text-anchor', 'middle')
|
||||
.style('font-size', '16px')
|
||||
.text(attrs.chartTitle);
|
||||
}
|
||||
|
||||
// Add X axis label
|
||||
if (attrs.xAxisLabel) {
|
||||
svg.append('text')
|
||||
.attr('x', containerWidth / 2)
|
||||
.attr('y', attrs.chartHeight - 10)
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(attrs.xAxisLabel);
|
||||
}
|
||||
|
||||
// Add Y axis label
|
||||
if (attrs.yAxisLabel) {
|
||||
svg.append('text')
|
||||
.attr('transform', 'rotate(-90)')
|
||||
.attr('x', -(attrs.chartHeight / 2))
|
||||
.attr('y', 20)
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(attrs.yAxisLabel);
|
||||
}
|
||||
|
||||
// Add resize handler
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
const newWidth = graphContainer.clientWidth;
|
||||
if (newWidth !== containerWidth) {
|
||||
// Clear and redraw
|
||||
graphContainer.innerHTML = '';
|
||||
renderBarGraph(container, attrs);
|
||||
}
|
||||
});
|
||||
resizeObserver.observe(graphContainer);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user