Changes to color controls

This commit is contained in:
Jeremy Rangel
2025-01-15 03:00:49 -08:00
parent 7df4855b60
commit ea5e69f0fd
14 changed files with 383 additions and 15 deletions

1
.gitignore vendored
View File

@ -9,7 +9,6 @@ npm-debug.log*
# Ignore build files # Ignore build files
dist/ dist/
build/
# Ignore .env files (for sensitive data like API keys) # Ignore .env files (for sensitive data like API keys)
.env .env

View File

@ -0,0 +1,112 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "lcp/bar-graph",
"version": "0.1.0",
"title": "LCP Bar Graph",
"category": "widgets",
"icon": "chart-bar",
"description": "Display data in a bar graph format.",
"supports": {
"html": false
},
"textdomain": "lcp",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js",
"attributes": {
"chartColorSource": {
"type": "string",
"default": "default"
},
"chartCustomColors": {
"type": "array",
"default": []
},
"showBarValues": {
"type": "boolean",
"default": true
},
"chartHeight": {
"type": "string",
"default": "400px"
},
"chartWidth": {
"type": "string",
"default": "100%"
},
"chartTitle": {
"type": "string",
"default": ""
},
"displayChartTitle": {
"type": "boolean",
"default": false
},
"allowDownload": {
"type": "boolean",
"default": false
},
"downloadMaxWidth": {
"type": "string",
"default": "2000px"
},
"showSorting": {
"type": "boolean",
"default": false
},
"showFiltering": {
"type": "boolean",
"default": false
},
"chartData": {
"type": "object",
"default": {}
},
"dataSource": {
"type": "string",
"default": "manual_json"
},
"barColor": {
"type": "string",
"default": "#0073aa"
},
"barOpacity": {
"type": "number",
"default": 1
},
"backgroundColor": {
"type": "string",
"default": "#ffffff"
},
"showGridX": {
"type": "boolean",
"default": false
},
"showGridY": {
"type": "boolean",
"default": true
},
"gridColor": {
"type": "string",
"default": "#e0e0e0"
},
"gridWidth": {
"type": "number",
"default": 1
},
"xAxisLabel": {
"type": "string",
"default": ""
},
"yAxisLabel": {
"type": "string",
"default": ""
},
"gridOpacity": {
"type": "number",
"default": 0.5
}
}
}

View File

@ -0,0 +1 @@
.wp-block-create-block-todo-list{border:1px dotted red}.wp-block-lcp-bar-graph{margin:1em 0}.wp-block-lcp-bar-graph .lcp-bar-graph-container{overflow:visible;width:100%}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg{overflow:visible}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .bar{transition:opacity .2s ease}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .bar:hover{cursor:pointer}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .grid line{stroke-opacity:.1}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .domain,.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .tick line{stroke:#666}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .tick text{fill:#666;font-size:12px}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .tooltip{pointer-events:none}.wp-block-lcp-bar-graph .lcp-bar-graph-placeholder{font-size:1.2em;padding:2em;text-align:center}.lcp-data-selector .components-base-control{margin-bottom:1.5em}.lcp-data-selector .lcp-data-selector-error{background-color:#f8d7da;border-right:4px solid #cc1818;margin-top:.5em;padding:.5em}

View File

@ -0,0 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => '62449dbdc80e118c522c');

View File

@ -0,0 +1 @@
.wp-block-create-block-todo-list{border:1px dotted red}.wp-block-lcp-bar-graph{margin:1em 0}.wp-block-lcp-bar-graph .lcp-bar-graph-container{overflow:visible;width:100%}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg{overflow:visible}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .bar{transition:opacity .2s ease}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .bar:hover{cursor:pointer}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .grid line{stroke-opacity:.1}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .domain,.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .tick line{stroke:#666}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .tick text{fill:#666;font-size:12px}.wp-block-lcp-bar-graph .lcp-bar-graph-container svg .tooltip{pointer-events:none}.wp-block-lcp-bar-graph .lcp-bar-graph-placeholder{font-size:1.2em;padding:2em;text-align:center}.lcp-data-selector .components-base-control{margin-bottom:1.5em}.lcp-data-selector .lcp-data-selector-error{background-color:#f8d7da;border-left:4px solid #cc1818;margin-top:.5em;padding:.5em}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.wp-block-create-block-todo-list{background-color:#21759b;color:#fff;padding:2px}

View File

@ -0,0 +1 @@
.wp-block-create-block-todo-list{background-color:#21759b;color:#fff;padding:2px}

View File

@ -0,0 +1 @@
<?php return array('dependencies' => array(), 'version' => '7ca23fe6db2e4bfa97aa');

View File

@ -0,0 +1 @@
console.log("Hello World! (from create-block-todo-list block)");

View File

@ -16,6 +16,14 @@
"style": "file:./style-index.css", "style": "file:./style-index.css",
"viewScript": "file:./view.js", "viewScript": "file:./view.js",
"attributes": { "attributes": {
"chartColorSource": {
"type": "string",
"default": "default"
},
"chartCustomColors": {
"type": "array",
"default": []
},
"showBarValues": { "showBarValues": {
"type": "boolean", "type": "boolean",
"default": true "default": true

View File

@ -13,7 +13,11 @@ const BarGraph = ({
gridWidth, gridWidth,
showBarValues, showBarValues,
xAxisLabel, xAxisLabel,
yAxisLabel yAxisLabel,
colorSource,
defaultBarColor,
customColors,
barOpacity
}) => { }) => {
const svgRef = useRef(); const svgRef = useRef();
const containerRef = useRef(); const containerRef = useRef();
@ -80,6 +84,22 @@ const BarGraph = ({
.domain([0, d3.max(chartData, d => d.value)]) .domain([0, d3.max(chartData, d => d.value)])
.range([innerHeight, 0]); .range([innerHeight, 0]);
// Get color for a datapoint based on color source
const getColor = (datapoint) => {
switch (colorSource) {
case 'singleColor':
return defaultBarColor;
case 'customColors':
const customColor = customColors.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 // Add X grid
if (showGridX) { if (showGridX) {
g.append('g') g.append('g')
@ -116,7 +136,8 @@ const BarGraph = ({
.attr('y', d => yScale(d.value)) .attr('y', d => yScale(d.value))
.attr('width', xScale.bandwidth()) .attr('width', xScale.bandwidth())
.attr('height', d => innerHeight - yScale(d.value)) .attr('height', d => innerHeight - yScale(d.value))
.attr('fill', d => d.color); .attr('fill', d => getColor(d))
.style('opacity', barOpacity);
// Add bar values // Add bar values
if (showBarValues) { if (showBarValues) {
@ -163,7 +184,7 @@ const BarGraph = ({
.text(yAxisLabel); .text(yAxisLabel);
} }
}, [data, width, height, title, showGridX, showGridY, gridColor, gridWidth, showBarValues, xAxisLabel, yAxisLabel]); }, [data, width, height, title, showGridX, showGridY, gridColor, gridWidth, showBarValues, xAxisLabel, yAxisLabel, colorSource, defaultBarColor, customColors, barOpacity]);
return ( return (
<div <div

View File

@ -0,0 +1,116 @@
import { __ } from '@wordpress/i18n';
import {
ColorPicker,
SelectControl,
Button,
Popover
} from '@wordpress/components';
import { useState } from '@wordpress/element';
const ColorControls = ({
data,
colorSource,
singleColor,
customColors,
onColorSourceChange,
onSingleColorChange,
onCustomColorChange
}) => {
const [activePopover, setActivePopover] = useState(null);
// Get all datapoints from all datasets
const getAllDatapoints = () => {
const datapoints = [];
Object.entries(data).forEach(([dataset, items]) => {
items.forEach(item => {
datapoints.push({
dataset,
label: item.label,
value: item.value
});
});
});
return datapoints;
};
const getCustomColor = (dataset, label) => {
const colorEntry = customColors.find(c => c.dataset === dataset && c.label === label);
return colorEntry ? colorEntry.color : '#FF6384';
};
return (
<div className="lcp-color-controls">
<SelectControl
label={__('Color Source', 'lcp')}
value={colorSource}
options={[
{ label: __('Default Colors', 'lcp'), value: 'default' },
{ label: __('Single Color', 'lcp'), value: 'singleColor' },
{ label: __('Custom Colors', 'lcp'), value: 'customColors' }
]}
onChange={onColorSourceChange}
/>
{colorSource === 'singleColor' && (
<div className="components-base-control">
<label className="components-base-control__label">
{__('Bar Color', 'lcp')}
</label>
<ColorPicker
color={singleColor}
onChangeComplete={(color) => onSingleColorChange(color.hex)}
/>
</div>
)}
{colorSource === 'customColors' && (
<div className="custom-colors-grid" style={{ marginTop: '10px' }}>
{getAllDatapoints().map((datapoint, index) => (
<div
key={`${datapoint.dataset}-${datapoint.label}`}
style={{
display: 'flex',
alignItems: 'center',
marginBottom: '8px',
gap: '8px'
}}
>
<span style={{ flex: 1 }}>{datapoint.label}</span>
<Button
onClick={() => setActivePopover(index)}
style={{
backgroundColor: getCustomColor(datapoint.dataset, datapoint.label),
width: '24px',
height: '24px',
borderRadius: '50%',
padding: 0,
border: '1px solid #ddd'
}}
/>
{activePopover === index && (
<Popover
onClose={() => setActivePopover(null)}
>
<div style={{ padding: '12px' }}>
<ColorPicker
color={getCustomColor(datapoint.dataset, datapoint.label)}
onChangeComplete={(color) => {
onCustomColorChange(
datapoint.dataset,
datapoint.label,
color.hex
);
}}
/>
</div>
</Popover>
)}
</div>
))}
</div>
)}
</div>
);
};
export default ColorControls;

View File

@ -11,9 +11,11 @@ import {
ToggleControl, ToggleControl,
TabPanel, TabPanel,
TextControl, TextControl,
Button Button,
Popover
} from '@wordpress/components'; } from '@wordpress/components';
import {useState} from '@wordpress/element';
/** /**
* Internal dependencies * Internal dependencies
*/ */
@ -21,6 +23,7 @@ import './editor.scss';
import LCPDataSelector from '../../../components/LCPDataSelector'; import LCPDataSelector from '../../../components/LCPDataSelector';
import BarGraph from './components/BarGraph'; import BarGraph from './components/BarGraph';
import LCPDimensionControl from '../../../components/LCPDimensionControl'; import LCPDimensionControl from '../../../components/LCPDimensionControl';
import ColorControls from './components/ColorControls';
export default function Edit({ attributes, setAttributes }) { export default function Edit({ attributes, setAttributes }) {
const { const {
@ -43,8 +46,9 @@ export default function Edit({ attributes, setAttributes }) {
showGridX, showGridX,
gridWidth, gridWidth,
xAxisLabel, xAxisLabel,
yAxisLabel yAxisLabel,
} = attributes; chartColorSource,
chartCustomColors } = attributes;
const blockProps = useBlockProps(); const blockProps = useBlockProps();
@ -60,6 +64,21 @@ export default function Edit({ attributes, setAttributes }) {
}); });
}; };
const handleCustomColorChange = (dataset, label, color) => {
const existingColors = [...chartCustomColors];
const existingIndex = existingColors.findIndex(
c => c.dataset === dataset && c.label === label
);
if (existingIndex >= 0) {
existingColors[existingIndex].color = color;
} else {
existingColors.push({ dataset, label, color });
}
setAttributes({ chartCustomColors: existingColors });
};
const handleDownload = () => { const handleDownload = () => {
// Get the SVG element // Get the SVG element
const svg = document.querySelector('.bar-graph svg'); const svg = document.querySelector('.bar-graph svg');
@ -103,6 +122,33 @@ export default function Edit({ attributes, setAttributes }) {
img.src = url; img.src = url;
}; };
// Grid Color Popover
const [isGridColorPopoverOpen, setIsGridColorPopoverOpen] = useState(false);
// Toggle the popover visibility
const toggleGridColorPopover = () => {
setIsGridColorPopoverOpen(!isGridColorPopoverOpen);
};
// Handle color change
const onGridColorChange = (newColor) => {
setAttributes({ gridColor: newColor });
};
// Grid Color Popover
const [isBackgroundColorPopoverOpen, setIsBackgroundColorPopoverOpen] = useState(false);
// Toggle the background popover visibility
const toggleBackgroundColorPopover = () => {
setIsBackgroundColorPopoverOpen(!isBackgroundColorPopoverOpen);
};
// Handle color change
const onBackgroundColorChange = (newColor) => {
setAttributes({ backgroundColor: newColor });
};
// Debug log to check chartData // Debug log to check chartData
console.log('Chart data:', chartData); console.log('Chart data:', chartData);
@ -167,15 +213,65 @@ export default function Edit({ attributes, setAttributes }) {
/> />
</PanelBody> </PanelBody>
<PanelBody title="Appearance"> <PanelBody title="Appearance">
<div className="components-base-control"> <div className="grid-color-row" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '10px' }}>
<label className="components-base-control__label"> <span>Grid Color</span>
{__('Bar Color', 'lcp')} <Button
</label> className="grid-color-button"
onClick={toggleGridColorPopover}
style={{
backgroundColor: gridColor,
width: '26px',
height: '26px',
borderRadius: '50%',
border: 'none',
}}
></Button>
{isGridColorPopoverOpen && (
<Popover
position="bottom center"
onClose={toggleGridColorPopover} // Close the popover when clicked outside
>
<ColorPicker <ColorPicker
color={barColor} color={gridColor}
onChangeComplete={(color) => setAttributes({ barColor: color.hex })} onChangeComplete={(color) => onGridColorChange(color.hex)} // Update color
/> />
</Popover>
)}
</div> </div>
<div className="background-color-row" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '10px' }}>
<span>Background Color</span>
<Button
className="background-color-button"
onClick={toggleBackgroundColorPopover}
style={{
backgroundColor: backgroundColor,
width: '26px',
height: '26px',
borderRadius: '50%',
border: '1px solid #ccc',
}}
></Button>
{isBackgroundColorPopoverOpen && (
<Popover
position="bottom center"
onClose={toggleBackgroundColorPopover} // Close the popover when clicked outside
>
<ColorPicker
color={backgroundColor}
onChangeComplete={(color) => onBackgroundColorChange(color.hex)} // Update color
/>
</Popover>
)}
</div>
<ColorControls
data={chartData}
colorSource={chartColorSource}
singleColor={barColor}
customColors={chartCustomColors}
onColorSourceChange={(value) => setAttributes({ chartColorSource: value })}
onSingleColorChange={(color) => setAttributes({ barColor: color })}
onCustomColorChange={handleCustomColorChange}
/>
<RangeControl <RangeControl
label={__('Bar Opacity', 'lcp')} label={__('Bar Opacity', 'lcp')}
value={barOpacity} value={barOpacity}
@ -230,6 +326,12 @@ export default function Edit({ attributes, setAttributes }) {
color={gridColor} color={gridColor}
onChangeComplete={(color) => setAttributes({ gridColor: color.hex })} onChangeComplete={(color) => setAttributes({ gridColor: color.hex })}
/> />
</div> </div>
<RangeControl <RangeControl
label={__('Grid Width', 'lcp')} label={__('Grid Width', 'lcp')}
@ -317,6 +419,8 @@ export default function Edit({ attributes, setAttributes }) {
showBarValues={showBarValues} showBarValues={showBarValues}
xAxisLabel={xAxisLabel} xAxisLabel={xAxisLabel}
yAxisLabel={yAxisLabel} yAxisLabel={yAxisLabel}
colorSource={chartColorSource}
customColors={chartCustomColors}
/> />
) : ( ) : (
<div <div