mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-22 21:36:00 +00:00
chart initial import
This commit is contained in:
@@ -8,11 +8,13 @@
|
|||||||
"validate": "svelte-check"
|
"validate": "svelte-check"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@ant-design/colors": "^5.0.0",
|
||||||
"@rollup/plugin-commonjs": "^17.0.0",
|
"@rollup/plugin-commonjs": "^17.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||||
"@rollup/plugin-typescript": "^6.0.0",
|
"@rollup/plugin-typescript": "^6.0.0",
|
||||||
"@tsconfig/svelte": "^1.0.0",
|
"@tsconfig/svelte": "^1.0.0",
|
||||||
"ace-builds": "^1.4.8",
|
"ace-builds": "^1.4.8",
|
||||||
|
"chart.js": "^2.9.4",
|
||||||
"dbgate-datalib": "^3.9.5",
|
"dbgate-datalib": "^3.9.5",
|
||||||
"dbgate-sqltree": "^3.9.5",
|
"dbgate-sqltree": "^3.9.5",
|
||||||
"dbgate-tools": "^3.9.5",
|
"dbgate-tools": "^3.9.5",
|
||||||
@@ -20,6 +22,7 @@
|
|||||||
"json-stable-stringify": "^1.0.1",
|
"json-stable-stringify": "^1.0.1",
|
||||||
"localforage": "^1.9.0",
|
"localforage": "^1.9.0",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
|
"randomcolor": "^0.6.2",
|
||||||
"rollup": "^2.3.4",
|
"rollup": "^2.3.4",
|
||||||
"rollup-plugin-copy": "^3.3.0",
|
"rollup-plugin-copy": "^3.3.0",
|
||||||
"rollup-plugin-css-only": "^3.1.0",
|
"rollup-plugin-css-only": "^3.1.0",
|
||||||
|
|||||||
34
packages/web/src/charts/ChartCore.svelte
Normal file
34
packages/web/src/charts/ChartCore.svelte
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount, afterUpdate, onDestroy } from 'svelte';
|
||||||
|
import Chart from 'chart.js';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
export let type = 'line';
|
||||||
|
export let options = {};
|
||||||
|
export let plugins = {};
|
||||||
|
|
||||||
|
let chart = null;
|
||||||
|
let domChart;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
chart = new Chart(domChart, {
|
||||||
|
type,
|
||||||
|
data,
|
||||||
|
options,
|
||||||
|
plugins,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
afterUpdate(() => {
|
||||||
|
if (!chart) return;
|
||||||
|
chart.data = data;
|
||||||
|
chart.type = type;
|
||||||
|
chart.options = options;
|
||||||
|
chart.plugins = plugins;
|
||||||
|
chart.update();
|
||||||
|
});
|
||||||
|
onDestroy(() => {
|
||||||
|
chart = null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<canvas bind:this={domChart} {...$$restProps} />
|
||||||
169
packages/web/src/charts/ChartEditor.svelte
Normal file
169
packages/web/src/charts/ChartEditor.svelte
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import AboutModal from '../modals/AboutModal.svelte';
|
||||||
|
import { presetPrimaryColors } from '@ant-design/colors';
|
||||||
|
import { startCase } from 'lodash';
|
||||||
|
import FormProviderCore from '../forms/FormProviderCore.svelte';
|
||||||
|
import HorizontalSplitter from '../elements/HorizontalSplitter.svelte';
|
||||||
|
import WidgetColumnBar from '../widgets/WidgetColumnBar.svelte';
|
||||||
|
import WidgetColumnBarItem from '../widgets/WidgetColumnBarItem.svelte';
|
||||||
|
import ManagerInnerContainer from '../elements/ManagerInnerContainer.svelte';
|
||||||
|
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||||
|
import FormTextField from '../forms/FormTextField.svelte';
|
||||||
|
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import FormFieldTemplateTiny from '../forms/FormFieldTemplateTiny.svelte';
|
||||||
|
import createRef from '../utility/createRef';
|
||||||
|
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||||
|
import { findEngineDriver } from 'dbgate-tools';
|
||||||
|
import { extensions } from '../stores';
|
||||||
|
import { loadChartData, loadChartStructure } from './chartDataLoader';
|
||||||
|
import DataChart from './DataChart.svelte';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
export let config;
|
||||||
|
export let setConfig;
|
||||||
|
export let conid;
|
||||||
|
export let database;
|
||||||
|
export let sql;
|
||||||
|
|
||||||
|
let availableColumnNames = [];
|
||||||
|
let error = null;
|
||||||
|
let loadedData = null;
|
||||||
|
|
||||||
|
const getDriver = async () => {
|
||||||
|
const conn = await getConnectionInfo({ conid });
|
||||||
|
if (!conn) return;
|
||||||
|
const driver = findEngineDriver(conn, $extensions);
|
||||||
|
return driver;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLoadColumns = async () => {
|
||||||
|
const driver = await getDriver();
|
||||||
|
if (!driver) return;
|
||||||
|
try {
|
||||||
|
const columns = await loadChartStructure(driver, conid, database, sql);
|
||||||
|
availableColumnNames = columns;
|
||||||
|
} catch (err) {
|
||||||
|
error = err.message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLoadData = async () => {
|
||||||
|
const driver = await getDriver();
|
||||||
|
if (!driver) return;
|
||||||
|
const loaded = await loadChartData(driver, conid, database, sql, config);
|
||||||
|
if (!loaded) return;
|
||||||
|
const { columns, rows } = loaded;
|
||||||
|
loadedData = {
|
||||||
|
structure: columns,
|
||||||
|
rows,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const configStore = writable(config);
|
||||||
|
const changingRef = createRef(false);
|
||||||
|
$: {
|
||||||
|
if (!changingRef.get()) {
|
||||||
|
$configStore = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// $: {
|
||||||
|
// changingRef.set(true);
|
||||||
|
// setConfig($configStore);
|
||||||
|
// changingRef.set(false);
|
||||||
|
// }
|
||||||
|
$: {
|
||||||
|
$extensions;
|
||||||
|
if (sql && conid && database) {
|
||||||
|
handleLoadColumns();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$: {
|
||||||
|
if (data) {
|
||||||
|
availableColumnNames = data.structure.columns.map(x => x.columnName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$: {
|
||||||
|
if (config.labelColumn && sql && conid && database) {
|
||||||
|
handleLoadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let managerSize;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormProviderCore values={configStore} template={FormFieldTemplateTiny}>
|
||||||
|
<HorizontalSplitter initialValue="300px" bind:size={managerSize}>
|
||||||
|
<div class="left" slot="1">
|
||||||
|
<WidgetColumnBar>
|
||||||
|
<WidgetColumnBarItem title="Style" name="style" height="40%">
|
||||||
|
<ManagerInnerContainer width={managerSize}>
|
||||||
|
<FormSelectField
|
||||||
|
label="Chart type"
|
||||||
|
name="chartType"
|
||||||
|
isNative
|
||||||
|
options={[
|
||||||
|
{ value: 'bar', label: 'Bar' },
|
||||||
|
{ value: 'line', label: 'Line' },
|
||||||
|
{ value: 'pie', label: 'Pie' },
|
||||||
|
{ value: 'polarArea', label: 'Polar area' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<FormTextField label="Color set" name="colorSeed" />
|
||||||
|
<FormSelectField
|
||||||
|
label="Truncate from"
|
||||||
|
name="truncateFrom"
|
||||||
|
isNative
|
||||||
|
options={[
|
||||||
|
{ value: 'begin', label: 'Begin' },
|
||||||
|
{ value: 'end', label: 'End (most recent data for datetime)' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<FormTextField label="Truncate limit" name="truncateLimit" />
|
||||||
|
<FormCheckboxField label="Show relative values" name="showRelativeValues" />
|
||||||
|
</ManagerInnerContainer>
|
||||||
|
</WidgetColumnBarItem>
|
||||||
|
<WidgetColumnBarItem title="Data" name="data">
|
||||||
|
<ManagerInnerContainer width={managerSize}>
|
||||||
|
{#if availableColumnNames.length > 0}
|
||||||
|
<FormSelectField
|
||||||
|
label="Label column"
|
||||||
|
name="labelColumn"
|
||||||
|
isNative
|
||||||
|
options={[{ value: '' }, ...availableColumnNames.map(col => ({ value: col, label: col }))]}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#each availableColumnNames as col (col)}
|
||||||
|
<FormCheckboxField label={col} name={`dataColumn_${col}`} />
|
||||||
|
{#if config[`dataColumn_${col}`]}
|
||||||
|
<FormSelectField
|
||||||
|
label="Color"
|
||||||
|
name={`dataColumnColor_${col}`}
|
||||||
|
isNative
|
||||||
|
options={[
|
||||||
|
{ value: '', label: 'Random' },
|
||||||
|
..._.keys(presetPrimaryColors).map(color => ({ value: color, label: _.startCase(color) })),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</ManagerInnerContainer>
|
||||||
|
</WidgetColumnBarItem>
|
||||||
|
</WidgetColumnBar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<svelte:fragment slot="2">
|
||||||
|
<DataChart data={data || loadedData} />
|
||||||
|
</svelte:fragment>
|
||||||
|
</HorizontalSplitter>
|
||||||
|
</FormProviderCore>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.left {
|
||||||
|
background-color: var(--theme-bg-0);
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
137
packages/web/src/charts/DataChart.svelte
Normal file
137
packages/web/src/charts/DataChart.svelte
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
<script lang="ts" context="module">
|
||||||
|
function getTimeAxis(labels) {
|
||||||
|
const res = [];
|
||||||
|
for (const label of labels) {
|
||||||
|
const parsed = moment(label);
|
||||||
|
if (!parsed.isValid()) return null;
|
||||||
|
const iso = parsed.toISOString();
|
||||||
|
if (iso < '1850-01-01T00:00:00' || iso > '2150-01-01T00:00:00') return null;
|
||||||
|
res.push(parsed);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLabels(labelValues, timeAxis, chartType) {
|
||||||
|
if (!timeAxis) return labelValues;
|
||||||
|
if (chartType === 'line') return timeAxis.map(x => x.toDate());
|
||||||
|
return timeAxis.map(x => x.format('D. M. YYYY'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOptions(timeAxis, chartType) {
|
||||||
|
if (timeAxis && chartType === 'line') {
|
||||||
|
return {
|
||||||
|
scales: {
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
type: 'time',
|
||||||
|
distribution: 'linear',
|
||||||
|
|
||||||
|
time: {
|
||||||
|
tooltipFormat: 'D. M. YYYY HH:mm',
|
||||||
|
displayFormats: {
|
||||||
|
millisecond: 'HH:mm:ss.SSS',
|
||||||
|
second: 'HH:mm:ss',
|
||||||
|
minute: 'HH:mm',
|
||||||
|
hour: 'D.M hA',
|
||||||
|
day: 'D. M.',
|
||||||
|
week: 'D. M. YYYY',
|
||||||
|
month: 'MM-YYYY',
|
||||||
|
quarter: '[Q]Q - YYYY',
|
||||||
|
year: 'YYYY',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createChartData(freeData, labelColumn, dataColumns, colorSeed, chartType, dataColumnColors) {
|
||||||
|
if (!freeData || !labelColumn || !dataColumns || !freeData.rows || dataColumns.length == 0) return [{}, {}];
|
||||||
|
const colors = randomcolor({
|
||||||
|
count: _.max([freeData.rows.length, dataColumns.length, 1]),
|
||||||
|
seed: colorSeed,
|
||||||
|
});
|
||||||
|
let backgroundColor = null;
|
||||||
|
let borderColor = null;
|
||||||
|
const labelValues = freeData.rows.map(x => x[labelColumn]);
|
||||||
|
const timeAxis = getTimeAxis(labelValues);
|
||||||
|
const labels = getLabels(labelValues, timeAxis, chartType);
|
||||||
|
const res = {
|
||||||
|
labels,
|
||||||
|
datasets: dataColumns.map((dataColumn, columnIndex) => {
|
||||||
|
if (chartType == 'line' || chartType == 'bar') {
|
||||||
|
const color = dataColumnColors[dataColumn];
|
||||||
|
if (color) {
|
||||||
|
backgroundColor = presetPalettes[color][4] + '80';
|
||||||
|
borderColor = presetPalettes[color][7];
|
||||||
|
} else {
|
||||||
|
backgroundColor = colors[columnIndex] + '80';
|
||||||
|
borderColor = colors[columnIndex];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
backgroundColor = colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: dataColumn,
|
||||||
|
data: freeData.rows.map(row => row[dataColumn]),
|
||||||
|
backgroundColor,
|
||||||
|
borderColor,
|
||||||
|
borderWidth: 1,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = getOptions(timeAxis, chartType);
|
||||||
|
return [res, options];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import _ from 'lodash';
|
||||||
|
import randomcolor from 'randomcolor';
|
||||||
|
import moment from 'moment';
|
||||||
|
import ChartCore from './ChartCore.svelte';
|
||||||
|
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
||||||
|
import { generate, presetPalettes, presetDarkPalettes, presetPrimaryColors } from '@ant-design/colors';
|
||||||
|
import { extractDataColumnColors, extractDataColumns } from './chartDataLoader';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
|
||||||
|
const { values } = getFormContext();
|
||||||
|
|
||||||
|
let clientWidth;
|
||||||
|
let clientHeight;
|
||||||
|
|
||||||
|
$: dataColumns = extractDataColumns(values);
|
||||||
|
$: dataColumnColors = extractDataColumnColors(values, dataColumns);
|
||||||
|
|
||||||
|
$: chartData = createChartData(
|
||||||
|
data,
|
||||||
|
$values.labelColumn,
|
||||||
|
dataColumns,
|
||||||
|
$values.colorSeed || '5',
|
||||||
|
$values.chartType,
|
||||||
|
dataColumnColors
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="wrapper" bind:clientWidth bind:clientHeight>
|
||||||
|
<ChartCore
|
||||||
|
width={clientWidth}
|
||||||
|
height={clientHeight}
|
||||||
|
data={chartData[0]}
|
||||||
|
type={$values.chartType}
|
||||||
|
options={chartData[1]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
122
packages/web/src/charts/chartDataLoader.ts
Normal file
122
packages/web/src/charts/chartDataLoader.ts
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import { dumpSqlSelect, Select } from 'dbgate-sqltree';
|
||||||
|
import { EngineDriver } from 'dbgate-types';
|
||||||
|
import axiosInstance from '../utility/axiosInstance';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export async function loadChartStructure(driver: EngineDriver, conid, database, sql) {
|
||||||
|
const select: Select = {
|
||||||
|
commandType: 'select',
|
||||||
|
selectAll: true,
|
||||||
|
topRecords: 1,
|
||||||
|
from: {
|
||||||
|
subQueryString: sql,
|
||||||
|
alias: 'subq',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const dmp = driver.createDumper();
|
||||||
|
dumpSqlSelect(dmp, select);
|
||||||
|
const resp = await axiosInstance.post('database-connections/query-data', { conid, database, sql: dmp.s });
|
||||||
|
if (resp.data.errorMessage) throw new Error(resp.data.errorMessage);
|
||||||
|
return resp.data.columns.map(x => x.columnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadChartData(driver: EngineDriver, conid, database, sql, config) {
|
||||||
|
const dataColumns = extractDataColumns(config);
|
||||||
|
const { labelColumn, truncateFrom, truncateLimit, showRelativeValues } = config;
|
||||||
|
if (!labelColumn || !dataColumns || dataColumns.length == 0) return null;
|
||||||
|
|
||||||
|
const select: Select = {
|
||||||
|
commandType: 'select',
|
||||||
|
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
exprType: 'column',
|
||||||
|
source: { alias: 'subq' },
|
||||||
|
columnName: labelColumn,
|
||||||
|
alias: labelColumn,
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
...dataColumns.map(columnName => ({
|
||||||
|
exprType: 'call',
|
||||||
|
func: 'SUM',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
exprType: 'column',
|
||||||
|
columnName,
|
||||||
|
source: { alias: 'subq' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
alias: columnName,
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
topRecords: truncateLimit || 100,
|
||||||
|
from: {
|
||||||
|
subQueryString: sql,
|
||||||
|
alias: 'subq',
|
||||||
|
},
|
||||||
|
groupBy: [
|
||||||
|
{
|
||||||
|
exprType: 'column',
|
||||||
|
source: { alias: 'subq' },
|
||||||
|
columnName: labelColumn,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
orderBy: [
|
||||||
|
{
|
||||||
|
exprType: 'column',
|
||||||
|
source: { alias: 'subq' },
|
||||||
|
columnName: labelColumn,
|
||||||
|
direction: truncateFrom == 'end' ? 'DESC' : 'ASC',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const dmp = driver.createDumper();
|
||||||
|
dumpSqlSelect(dmp, select);
|
||||||
|
const resp = await axiosInstance.post('database-connections/query-data', { conid, database, sql: dmp.s });
|
||||||
|
let { rows, columns } = resp.data;
|
||||||
|
if (truncateFrom == 'end' && rows) {
|
||||||
|
rows = _.reverse([...rows]);
|
||||||
|
}
|
||||||
|
if (showRelativeValues) {
|
||||||
|
const maxValues = dataColumns.map(col => _.max(rows.map(row => row[col])));
|
||||||
|
for (const [col, max] of _.zip(dataColumns, maxValues)) {
|
||||||
|
if (!max) continue;
|
||||||
|
if (!_.isNumber(max)) continue;
|
||||||
|
if (!(max > 0)) continue;
|
||||||
|
rows = rows.map(row => ({
|
||||||
|
...row,
|
||||||
|
[col]: (row[col] / max) * 100,
|
||||||
|
}));
|
||||||
|
// columns = columns.map((x) => {
|
||||||
|
// if (x.columnName == col) {
|
||||||
|
// return { columnName: `${col} %` };
|
||||||
|
// }
|
||||||
|
// return x;
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
columns,
|
||||||
|
rows,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractDataColumns(values) {
|
||||||
|
const dataColumns = [];
|
||||||
|
for (const key in values) {
|
||||||
|
if (key.startsWith('dataColumn_') && values[key]) {
|
||||||
|
dataColumns.push(key.substring('dataColumn_'.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dataColumns;
|
||||||
|
}
|
||||||
|
export function extractDataColumnColors(values, dataColumns) {
|
||||||
|
const res = {};
|
||||||
|
for (const column of dataColumns) {
|
||||||
|
const color = values[`dataColumnColor_${column}`];
|
||||||
|
if (color) res[column] = color;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
@@ -12,11 +12,13 @@
|
|||||||
import DesignerReference from './DesignerReference.svelte';
|
import DesignerReference from './DesignerReference.svelte';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { tick } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
|
import contextMenu from '../utility/contextMenu';
|
||||||
|
|
||||||
export let value;
|
export let value;
|
||||||
export let onChange;
|
export let onChange;
|
||||||
export let conid;
|
export let conid;
|
||||||
export let database;
|
export let database;
|
||||||
|
// export let menu;
|
||||||
|
|
||||||
let domCanvas;
|
let domCanvas;
|
||||||
|
|
||||||
|
|||||||
40
packages/web/src/forms/FormFieldTemplateTiny.svelte
Normal file
40
packages/web/src/forms/FormFieldTemplateTiny.svelte
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let type;
|
||||||
|
export let label;
|
||||||
|
export let noMargin;
|
||||||
|
export let disabled = false;
|
||||||
|
export let labelProps: any = {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{#if type == 'checkbox'}
|
||||||
|
<slot />
|
||||||
|
<span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span>
|
||||||
|
{:else}
|
||||||
|
<div class="label" {...labelProps} on:click={labelProps.onClick}>
|
||||||
|
<div class="label">
|
||||||
|
<span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.row {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
color: var(--theme-font-3);
|
||||||
|
}
|
||||||
|
.disabled {
|
||||||
|
color: var(--theme-font-3);
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import FormFieldTemplateLarge from '../modals/FormFieldTemplateLarge.svelte';
|
import FormFieldTemplateLarge from './FormFieldTemplateLarge.svelte';
|
||||||
import FormProviderCore from './FormProviderCore.svelte';
|
import FormProviderCore from './FormProviderCore.svelte';
|
||||||
|
|
||||||
export let initialValues = {};
|
export let initialValues = {};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import FormFieldTemplateLarge from '../modals/FormFieldTemplateLarge.svelte';
|
import FormFieldTemplateLarge from './FormFieldTemplateLarge.svelte';
|
||||||
import createRef from '../utility/createRef';
|
import createRef from '../utility/createRef';
|
||||||
|
|
||||||
import keycodes from '../utility/keycodes';
|
import keycodes from '../utility/keycodes';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import FormArgumentList from '../forms/FormArgumentList.svelte';
|
import FormArgumentList from '../forms/FormArgumentList.svelte';
|
||||||
import FormProviderCore from '../forms/FormProviderCore.svelte';
|
import FormProviderCore from '../forms/FormProviderCore.svelte';
|
||||||
import FormFieldTemplateRow from '../modals/FormFieldTemplateRow.svelte';
|
import FormFieldTemplateRow from '../forms/FormFieldTemplateRow.svelte';
|
||||||
|
|
||||||
export let args = [];
|
export let args = [];
|
||||||
const macroValues = getContext('macroValues');
|
const macroValues = getContext('macroValues');
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import ConnectionModalDriverFields from './ConnectionModalDriverFields.svelte';
|
import ConnectionModalDriverFields from './ConnectionModalDriverFields.svelte';
|
||||||
import ConnectionModalSshTunnelFields from './ConnectionModalSshTunnelFields.svelte';
|
import ConnectionModalSshTunnelFields from './ConnectionModalSshTunnelFields.svelte';
|
||||||
import ConnectionModalSslFields from './ConnectionModalSslFields.svelte';
|
import ConnectionModalSslFields from './ConnectionModalSslFields.svelte';
|
||||||
import FormFieldTemplateLarge from './FormFieldTemplateLarge.svelte';
|
import FormFieldTemplateLarge from '../forms/FormFieldTemplateLarge.svelte';
|
||||||
|
|
||||||
import ModalBase from './ModalBase.svelte';
|
import ModalBase from './ModalBase.svelte';
|
||||||
import { closeCurrentModal, closeModal } from './modalTools';
|
import { closeCurrentModal, closeModal } from './modalTools';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import FormProvider from '../forms/FormProvider.svelte';
|
import FormProvider from '../forms/FormProvider.svelte';
|
||||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||||
import FormTextFieldRaw from '../forms/FormTextFieldRaw.svelte';
|
import FormTextFieldRaw from '../forms/FormTextFieldRaw.svelte';
|
||||||
import FormFieldTemplateLarge from './FormFieldTemplateLarge.svelte';
|
import FormFieldTemplateLarge from '../forms/FormFieldTemplateLarge.svelte';
|
||||||
import SetFilterModal_Select from './SetFilterModal_Select.svelte';
|
import SetFilterModal_Select from './SetFilterModal_Select.svelte';
|
||||||
import ModalBase from './ModalBase.svelte';
|
import ModalBase from './ModalBase.svelte';
|
||||||
import { closeCurrentModal } from './modalTools';
|
import { closeCurrentModal } from './modalTools';
|
||||||
|
|||||||
56
packages/web/src/tabs/ChartTab.svelte
Normal file
56
packages/web/src/tabs/ChartTab.svelte
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import _ from 'lodash';
|
||||||
|
import ChartEditor from '../charts/ChartEditor.svelte';
|
||||||
|
|
||||||
|
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
||||||
|
|
||||||
|
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||||
|
|
||||||
|
import useEditorData from '../query/useEditorData';
|
||||||
|
import createUndoReducer from '../utility/createUndoReducer';
|
||||||
|
|
||||||
|
export let tabid;
|
||||||
|
export let conid;
|
||||||
|
export let database;
|
||||||
|
|
||||||
|
export function getData() {
|
||||||
|
return $editorState.value || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const { editorState, editorValue, setEditorData } = useEditorData({
|
||||||
|
tabid,
|
||||||
|
onInitialData: value => {
|
||||||
|
dispatchModel({ type: 'reset', value });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [modelState, dispatchModel] = createUndoReducer({
|
||||||
|
tables: [],
|
||||||
|
references: [],
|
||||||
|
columns: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
$: setEditorData($modelState.value);
|
||||||
|
|
||||||
|
const setConfig = config =>
|
||||||
|
// @ts-ignore
|
||||||
|
dispatchModel({
|
||||||
|
type: 'compute',
|
||||||
|
compute: v => ({ ...v, config: _.isFunction(config) ? config(v.config) : config }),
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $editorState.isLoading}
|
||||||
|
<LoadingInfo wrapper message="Loading data" />
|
||||||
|
{:else if $editorState.errorMessage}
|
||||||
|
<ErrorInfo message={$editorState.errorMessage} />
|
||||||
|
{:else}
|
||||||
|
<ChartEditor
|
||||||
|
data={$modelState.value && $modelState.value.data}
|
||||||
|
config={$modelState.value ? $modelState.value.config || {} : {}}
|
||||||
|
{setConfig}
|
||||||
|
sql={$modelState.value && $modelState.value.sql}
|
||||||
|
{conid}
|
||||||
|
{database}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
@@ -7,7 +7,7 @@ import * as ShellTab from './ShellTab.svelte';
|
|||||||
import * as ArchiveFileTab from './ArchiveFileTab.svelte';
|
import * as ArchiveFileTab from './ArchiveFileTab.svelte';
|
||||||
import * as FreeTableTab from './FreeTableTab.svelte';
|
import * as FreeTableTab from './FreeTableTab.svelte';
|
||||||
// import PluginTab from './PluginTab';
|
// import PluginTab from './PluginTab';
|
||||||
// import ChartTab from './ChartTab';
|
import * as ChartTab from './ChartTab.svelte';
|
||||||
import * as MarkdownEditorTab from './MarkdownEditorTab.svelte';
|
import * as MarkdownEditorTab from './MarkdownEditorTab.svelte';
|
||||||
// import MarkdownViewTab from './MarkdownViewTab';
|
// import MarkdownViewTab from './MarkdownViewTab';
|
||||||
// import MarkdownPreviewTab from './MarkdownPreviewTab';
|
// import MarkdownPreviewTab from './MarkdownPreviewTab';
|
||||||
@@ -24,7 +24,7 @@ export default {
|
|||||||
ArchiveFileTab,
|
ArchiveFileTab,
|
||||||
FreeTableTab,
|
FreeTableTab,
|
||||||
// PluginTab,
|
// PluginTab,
|
||||||
// ChartTab,
|
ChartTab,
|
||||||
MarkdownEditorTab,
|
MarkdownEditorTab,
|
||||||
// MarkdownViewTab,
|
// MarkdownViewTab,
|
||||||
// MarkdownPreviewTab,
|
// MarkdownPreviewTab,
|
||||||
|
|||||||
49
yarn.lock
49
yarn.lock
@@ -2,6 +2,13 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@ant-design/colors@^5.0.0":
|
||||||
|
version "5.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-5.1.1.tgz#800b2186b1e27e66432e67d03ed96af3e21d8940"
|
||||||
|
integrity sha512-Txy4KpHrp3q4XZdfgOBqLl+lkQIc3tEvHXOimRN1giX1AEC7mGtyrO9p8iRGJ3FLuVMGa2gNEzQyghVymLttKQ==
|
||||||
|
dependencies:
|
||||||
|
"@ctrl/tinycolor" "^3.3.1"
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
|
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
|
||||||
version "7.8.3"
|
version "7.8.3"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
|
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
|
||||||
@@ -160,6 +167,11 @@
|
|||||||
exec-sh "^0.3.2"
|
exec-sh "^0.3.2"
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
|
|
||||||
|
"@ctrl/tinycolor@^3.3.1":
|
||||||
|
version "3.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f"
|
||||||
|
integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ==
|
||||||
|
|
||||||
"@jest/console@^24.7.1", "@jest/console@^24.9.0":
|
"@jest/console@^24.7.1", "@jest/console@^24.9.0":
|
||||||
version "24.9.0"
|
version "24.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0"
|
resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0"
|
||||||
@@ -1523,6 +1535,29 @@ chardet@^0.7.0:
|
|||||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
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.0.2:
|
||||||
version "2.1.8"
|
version "2.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
|
||||||
@@ -1661,7 +1696,7 @@ collection-visit@^1.0.0:
|
|||||||
map-visit "^1.0.0"
|
map-visit "^1.0.0"
|
||||||
object-visit "^1.0.0"
|
object-visit "^1.0.0"
|
||||||
|
|
||||||
color-convert@^1.9.0:
|
color-convert@^1.9.0, color-convert@^1.9.3:
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||||
@@ -1680,7 +1715,7 @@ color-name@1.1.3:
|
|||||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||||
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||||
|
|
||||||
color-name@~1.1.4:
|
color-name@^1.0.0, color-name@~1.1.4:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
@@ -4881,6 +4916,11 @@ mkdirp@^1.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
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:
|
moment@^2.24.0:
|
||||||
version "2.24.0"
|
version "2.24.0"
|
||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||||
@@ -5781,6 +5821,11 @@ randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "^5.1.0"
|
safe-buffer "^5.1.0"
|
||||||
|
|
||||||
|
randomcolor@^0.6.2:
|
||||||
|
version "0.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/randomcolor/-/randomcolor-0.6.2.tgz#7a57362ae1a1278439aeed2c15e5deb8ea33f56d"
|
||||||
|
integrity sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A==
|
||||||
|
|
||||||
randomfill@^1.0.3:
|
randomfill@^1.0.3:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
|
resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
|
||||||
|
|||||||
Reference in New Issue
Block a user