Initial
This commit is contained in:
201
blocks/components/LCPDatasetBuilder.js
Normal file
201
blocks/components/LCPDatasetBuilder.js
Normal file
@ -0,0 +1,201 @@
|
||||
import { useState, useEffect, useMemo } from '@wordpress/element';
|
||||
import { Button, Modal, SelectControl, ToggleControl } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import LCPDataGrid from './LCPDataGrid';
|
||||
|
||||
const LCPDatasetBuilder = ({ chartData = [], onChange, attributes, setAttributes, chartType }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [options, setOptions] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (chartData && chartData.length > 0) {
|
||||
const columns = Object.keys(chartData[0]).filter(key => key !== 'lcpId');
|
||||
setOptions(columns.map(col => ({ label: col, value: col })));
|
||||
}
|
||||
}, [chartData]);
|
||||
|
||||
// Get available columns from chartData
|
||||
const getAvailableColumns = () => {
|
||||
if (!chartData.length) return [];
|
||||
|
||||
// Get all unique keys from the data
|
||||
const columns = Array.from(new Set(
|
||||
chartData.flatMap(item => Object.keys(item))
|
||||
)).filter(key => key !== 'lcpId'); // Exclude lcpId column
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
// Get column options based on type filter
|
||||
const getColumnOptions = (columns, typeFilter = null) => {
|
||||
return columns
|
||||
.filter(col => {
|
||||
if (!typeFilter) return true;
|
||||
const columnType = attributes.columnTypes?.[col];
|
||||
return columnType === typeFilter;
|
||||
})
|
||||
.map(col => ({ label: col, value: col }));
|
||||
};
|
||||
|
||||
const handleDataChange = (newData) => {
|
||||
console.log('DatasetBuilder updating chartData:', newData);
|
||||
onChange(newData);
|
||||
};
|
||||
|
||||
const downloadCSV = () => {
|
||||
// Get all columns except lcpId
|
||||
const columns = getAvailableColumns().map(col => col.value);
|
||||
|
||||
// Create CSV header
|
||||
const header = columns.join(',');
|
||||
|
||||
// Create CSV rows
|
||||
const rows = chartData.map(row =>
|
||||
columns.map(col => {
|
||||
let value = row[col] || '';
|
||||
if (typeof value === 'string' && (value.includes(',') || value.includes('"'))) {
|
||||
value = `"${value.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return value;
|
||||
}).join(',')
|
||||
).join('\n');
|
||||
|
||||
const csv = `${header}\n${rows}`;
|
||||
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', 'chart-data.csv');
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
// Get default value for valueColumn
|
||||
const defaultValueColumn = useMemo(() => {
|
||||
return attributes.valueColumn || (options[0]?.value || '');
|
||||
}, [attributes.valueColumn, options]);
|
||||
|
||||
// Get all available columns
|
||||
const availableColumns = getAvailableColumns();
|
||||
|
||||
// Get all column options
|
||||
const allOptions = getColumnOptions(availableColumns);
|
||||
|
||||
// Get numeric column options for bar chart value
|
||||
const numericOptions = getColumnOptions(availableColumns, 'lcpNumber');
|
||||
|
||||
// Determine which options to use for value column
|
||||
const valueColumnOptions = chartType === 'bar' ? numericOptions : allOptions;
|
||||
|
||||
// Add "None" option for optional fields
|
||||
const optionalOptions = [{ label: __('None', 'lcp-visualize'), value: '' }, ...allOptions];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => setIsOpen(true)}
|
||||
style={{ marginBottom: '10px', width: '100%' }}
|
||||
>
|
||||
{__('Edit Dataset', 'lcp-visualize')}
|
||||
</Button>
|
||||
{isOpen && (
|
||||
<Modal
|
||||
onRequestClose={() => setIsOpen(false)}
|
||||
title="Dataset Builder"
|
||||
style={{ width: '90vw', height: '90vh' }}
|
||||
>
|
||||
<div style={{ height: 'calc(90vh - 40px)', padding: '20px', display: 'flex', gap: '20px', marginBottom: '20px' }}>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<LCPDataGrid
|
||||
chartData={chartData}
|
||||
onDataChange={handleDataChange}
|
||||
attributes={attributes}
|
||||
setAttributes={setAttributes}
|
||||
columnTypes={attributes.columnTypes}
|
||||
onColumnTypeChange={(field, type) => {
|
||||
const newColumnTypes = {
|
||||
...attributes.columnTypes,
|
||||
[field]: type
|
||||
};
|
||||
|
||||
setAttributes({
|
||||
columnTypes: newColumnTypes,
|
||||
// If we're changing from a numeric type and this is the value column, reset it
|
||||
valueColumn: field === attributes.valueColumn &&
|
||||
type !== 'lcpNumber' &&
|
||||
chartType === 'bar' ? '' : attributes.valueColumn
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{
|
||||
width: '300px',
|
||||
padding: '16px',
|
||||
backgroundColor: '#f8f9fa',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '16px'
|
||||
}}>
|
||||
<SelectControl
|
||||
label={__('Value Column', 'lcp-visualize')}
|
||||
help={chartType === 'bar' ?
|
||||
__('Select a numeric column for bar heights', 'lcp-visualize') :
|
||||
__('Column to use for values', 'lcp-visualize')
|
||||
}
|
||||
value={attributes.valueColumn || ''}
|
||||
options={valueColumnOptions}
|
||||
onChange={(value) => setAttributes({ valueColumn: value })}
|
||||
/>
|
||||
{chartType === 'bar' && valueColumnOptions.length === 0 && (
|
||||
<div style={{ color: '#cc1818', marginTop: '4px' }}>
|
||||
{__('Please set at least one column type to Number to use as the value column', 'lcp-visualize')}
|
||||
</div>
|
||||
)}
|
||||
<SelectControl
|
||||
label={__('Labels Column', 'lcp-visualize')}
|
||||
help={__('Column to use for bar labels', 'lcp-visualize')}
|
||||
value={attributes.labelsColumn || (options[0]?.value || '')}
|
||||
options={options}
|
||||
onChange={(value) => setAttributes({ labelsColumn: value })}
|
||||
/>
|
||||
<SelectControl
|
||||
label={__('Color Column', 'lcp-visualize')}
|
||||
help={__('Column containing bar colors (optional)', 'lcp-visualize')}
|
||||
value={attributes.colorColumn || ''}
|
||||
options={optionalOptions}
|
||||
onChange={(value) => setAttributes({ colorColumn: value })}
|
||||
/>
|
||||
<SelectControl
|
||||
label={__('Popover Content Column', 'lcp-visualize')}
|
||||
help={__('Column containing HTML content for popovers (optional)', 'lcp-visualize')}
|
||||
value={attributes.popoverColumn || ''}
|
||||
options={optionalOptions}
|
||||
onChange={(value) => setAttributes({ popoverColumn: value })}
|
||||
/>
|
||||
<div style={{ marginBottom: '10px' }}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={downloadCSV}
|
||||
icon={
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" style={{ fill: 'currentColor' }}>
|
||||
<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
|
||||
</svg>
|
||||
}
|
||||
>
|
||||
{__('Download CSV', 'lcp-visualize')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LCPDatasetBuilder;
|
||||
Reference in New Issue
Block a user