charts version 0

This commit is contained in:
Jan Prochazka 2020-12-05 15:13:08 +01:00
parent 6471141926
commit 1644587072
10 changed files with 284 additions and 8 deletions

View File

@ -10,6 +10,7 @@
"@testing-library/user-event": "^7.1.2",
"ace-builds": "^1.4.8",
"axios": "^0.19.0",
"chart.js": "^2.9.4",
"cross-env": "^6.0.3",
"dbgate-datalib": "^1.0.0",
"dbgate-sqltree": "^1.0.0",
@ -21,6 +22,7 @@
"localforage": "^1.9.0",
"react": "^16.12.0",
"react-ace": "^8.0.0",
"react-chartjs-2": "^2.11.1",
"react-dom": "^16.12.0",
"react-dropzone": "^11.2.3",
"react-helmet": "^6.1.0",

View File

@ -0,0 +1,56 @@
import React from 'react';
import Chart from 'react-chartjs-2';
import styled from 'styled-components';
import useTheme from '../theme/useTheme';
import useDimensions from '../utility/useDimensions';
import { HorizontalSplitter } from '../widgets/Splitter';
import WidgetColumnBar, { WidgetColumnBarItem } from '../widgets/WidgetColumnBar';
import { FormCheckboxField, FormSelectField } from '../utility/forms';
import { Formik, Form } from 'formik';
import DataChart from './DataChart';
const LeftContainer = styled.div`
background-color: ${(props) => props.theme.manager_background};
display: flex;
flex: 1;
`;
export default function ChartEditor({ data }) {
const [managerSize, setManagerSize] = React.useState(0);
const theme = useTheme();
const availableColumnNames = data ? data.structure.columns.map((x) => x.columnName) : [];
return (
<Formik initialValues={{ chartType: 'bar' }} onSubmit={() => {}}>
<Form>
<HorizontalSplitter initialValue="300px" size={managerSize} setSize={setManagerSize}>
<LeftContainer theme={theme}>
<WidgetColumnBar>
<WidgetColumnBarItem title="Style" name="style" height="40%">
<FormSelectField label="Chart type" name="chartType">
<option value="bar">Bar</option>
<option value="line">Line</option>
</FormSelectField>
</WidgetColumnBarItem>
<WidgetColumnBarItem title="Data" name="data">
<FormSelectField label="Label column" name="labelColumn">
<option value=""></option>
{availableColumnNames.map((col) => (
<option value={col} key={col}>
{col}
</option>
))}
</FormSelectField>
{availableColumnNames.map((col) => (
<FormCheckboxField label={col} name={`dataColumn_${col}`} key={col} />
))}
</WidgetColumnBarItem>
</WidgetColumnBar>
</LeftContainer>
<DataChart data={data} />
</HorizontalSplitter>
</Form>
</Formik>
);
}

View File

@ -0,0 +1,99 @@
import React from 'react';
import _ from 'lodash'
import Chart from 'react-chartjs-2';
import styled from 'styled-components';
import useTheme from '../theme/useTheme';
import useDimensions from '../utility/useDimensions';
import { HorizontalSplitter } from '../widgets/Splitter';
import WidgetColumnBar, { WidgetColumnBarItem } from '../widgets/WidgetColumnBar';
import { FormSelectField } from '../utility/forms';
import { Formik, Form, useFormikContext } from 'formik';
const ChartWrapper = styled.div`
flex: 1;
overflow: hidden;
`;
const chartData = {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [
{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)',
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)',
],
borderWidth: 1,
},
],
};
function createChartData(freeData, labelColumn, dataColumns) {
if (!freeData || !labelColumn || !dataColumns || dataColumns.length == 0) return {};
const labels = _.uniq(freeData.rows.map((x) => x[labelColumn]));
const res = {
labels: freeData.rows.map((x) => x[labelColumn]),
datasets: dataColumns.map((dataColumn) => ({
label: dataColumn,
// data: [12, 19, 3, 5, 2, 3],
data: freeData.rows.map((x) => x[dataColumn]),
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)',
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)',
],
borderWidth: 1,
})),
};
return res;
}
export default function DataChart({ data }) {
const [containerRef, { height: containerHeight, width: containerWidth }] = useDimensions();
const { values } = useFormikContext();
const { labelColumn } = values;
const dataColumns = [];
for (const key in values) {
if (key.startsWith('dataColumn_') && values[key]) {
dataColumns.push(key.substring('dataColumn_'.length));
}
}
return (
<ChartWrapper ref={containerRef}>
<Chart
key={`${values.chartType}|${containerWidth}|${containerHeight}`}
width={containerWidth}
height={containerHeight}
data={createChartData(data, labelColumn, dataColumns)}
type={values.chartType}
/>
</ChartWrapper>
);
}

View File

@ -12,6 +12,7 @@ export default function DataGridContextMenu({
filterSelectedValue,
openQuery,
openFreeTable,
openChart,
}) {
return (
<>
@ -53,6 +54,7 @@ export default function DataGridContextMenu({
)}
{openQuery && <DropDownMenuItem onClick={openQuery}>Open query</DropDownMenuItem>}
<DropDownMenuItem onClick={openFreeTable}>Open selection in free table editor</DropDownMenuItem>
<DropDownMenuItem onClick={openChart}>Open chart from selection</DropDownMenuItem>
</>
);
}

View File

@ -321,9 +321,18 @@ export default function DataGridCore(props) {
setFirstVisibleColumnScrollIndex(value);
};
const handleOpenFreeTable = () => {
const getSelectedFreeData = () => {
const columns = getSelectedColumns();
const rows = getSelectedRowData().map((row) => _.pickBy(row, (v, col) => columns.find((x) => x.columnName == col)));
return {
structure: {
columns,
},
rows,
};
};
const handleOpenFreeTable = () => {
openNewTab(
setOpenedTabs,
{
@ -332,11 +341,21 @@ export default function DataGridCore(props) {
tabComponent: 'FreeTableTab',
props: {},
},
getSelectedFreeData()
);
};
const handleOpenChart = () => {
openNewTab(
setOpenedTabs,
{
structure: {
columns,
},
rows,
title: 'Chart',
icon: 'img chart',
tabComponent: 'ChartTab',
props: {},
},
{
data: getSelectedFreeData(),
}
);
};
@ -357,6 +376,7 @@ export default function DataGridCore(props) {
filterSelectedValue={display.filterable ? filterSelectedValue : null}
openQuery={openQuery}
openFreeTable={handleOpenFreeTable}
openChart={handleOpenChart}
/>
);
};

View File

@ -59,6 +59,7 @@ const iconNames = {
'img foreign-key': 'mdi mdi-key-link',
'img sql-file': 'mdi mdi-file',
'img shell': 'mdi mdi-flash color-blue-7',
'img chart': 'mdi mdi-chart-bar color-magenta-7',
'img free-table': 'mdi mdi-table color-green-7',
'img macro': 'mdi mdi-hammer-wrench',

View File

@ -0,0 +1,53 @@
import React from 'react';
import { createFreeTableModel } from 'dbgate-datalib';
import useUndoReducer from '../utility/useUndoReducer';
import { useSetOpenedTabs } from '../utility/globalState';
import useGridConfig from '../utility/useGridConfig';
import FreeTableGrid from '../freetable/FreeTableGrid';
import SaveArchiveModal from '../modals/SaveArchiveModal';
import useModalState from '../modals/useModalState';
import axios from '../utility/axios';
import LoadingInfo from '../widgets/LoadingInfo';
import { changeTab } from '../utility/common';
import ErrorInfo from '../widgets/ErrorInfo';
import useEditorData from '../utility/useEditorData';
import SaveTabModal from '../modals/SaveTabModal';
import ChartEditor from '../charts/ChartEditor';
export default function ChartTab({ tabVisible, toolbarPortalRef, tabid }) {
const [config, setConfig] = useGridConfig(tabid);
const [modelState, dispatchModel] = useUndoReducer(createFreeTableModel());
const saveFileModalState = useModalState();
const { initialData, setEditorData, errorMessage, isLoading } = useEditorData({
tabid,
});
React.useEffect(() => {
// @ts-ignore
if (initialData) dispatchModel({ type: 'reset', value: initialData });
}, [initialData]);
React.useEffect(() => {
setEditorData(modelState.value);
}, [modelState]);
if (isLoading) {
return <LoadingInfo wrapper message="Loading data" />;
}
if (errorMessage) {
return <ErrorInfo message={errorMessage} />;
}
return (
<>
<ChartEditor data={modelState.value && modelState.value.data} />
<SaveTabModal
modalState={saveFileModalState}
data={modelState.value}
format="json"
folder="charts"
tabid={tabid}
/>
</>
);
}

View File

@ -7,6 +7,7 @@ import InfoPageTab from './InfoPageTab';
import ArchiveFileTab from './ArchiveFileTab';
import FreeTableTab from './FreeTableTab';
import PluginTab from './PluginTab';
import ChartTab from './ChartTab';
export default {
TableDataTab,
@ -18,4 +19,5 @@ export default {
ArchiveFileTab,
FreeTableTab,
PluginTab,
ChartTab,
};

View File

@ -51,7 +51,7 @@ import ResizeObserver from 'resize-observer-polyfill';
// Export hook
export default function useDimensions(dependencies = []) {
const [node, setNode] = useState(null);
const ref = useCallback(newNode => {
const ref = useCallback((newNode) => {
setNode(newNode);
}, []);
@ -68,7 +68,7 @@ export default function useDimensions(dependencies = []) {
});
// Define measure function
const measure = useCallback(innerNode => {
const measure = useCallback((innerNode) => {
const rect = innerNode.getBoundingClientRect();
setDimensions({
x: rect.left,

View File

@ -2971,6 +2971,29 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
chart.js@^2.9.4:
version "2.9.4"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"
integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
dependencies:
chartjs-color "^2.1.0"
moment "^2.10.2"
chartjs-color-string@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"
integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
dependencies:
color-name "^1.0.0"
chartjs-color@^2.1.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0"
integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==
dependencies:
chartjs-color-string "^0.6.0"
color-convert "^1.9.3"
chokidar@^2.0.2, chokidar@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@ -3144,7 +3167,7 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"
color-convert@^1.9.0, color-convert@^1.9.1:
color-convert@^1.9.0, color-convert@^1.9.1, color-convert@^1.9.3:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
@ -7364,6 +7387,11 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
lodash@^4.17.19:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
loglevel@^1.6.4:
version "1.6.6"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312"
@ -7755,6 +7783,11 @@ mkdirp@^1.0.3:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
moment@^2.10.2:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
moment@^2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
@ -9675,6 +9708,14 @@ react-base16-styling@^0.6.0:
lodash.flow "^3.3.0"
pure-color "^1.2.0"
react-chartjs-2@^2.11.1:
version "2.11.1"
resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-2.11.1.tgz#a78d0df05fc8bc8ffcd4c4ab5b89a25dd2ca3278"
integrity sha512-G7cNq/n2Bkh/v4vcI+GKx7Q1xwZexKYhOSj2HmrFXlvNeaURWXun6KlOUpEQwi1cv9Tgs4H3kGywDWMrX2kxfA==
dependencies:
lodash "^4.17.19"
prop-types "^15.7.2"
react-dev-utils@^10.0.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.1.0.tgz#ccf82135f6dc2fc91969bc729ce57a69d8e86025"