diff --git a/packages/web/package.json b/packages/web/package.json
index 32b835c7d..9d4a2d801 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -8,11 +8,13 @@
"validate": "svelte-check"
},
"devDependencies": {
+ "@ant-design/colors": "^5.0.0",
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"@rollup/plugin-typescript": "^6.0.0",
"@tsconfig/svelte": "^1.0.0",
"ace-builds": "^1.4.8",
+ "chart.js": "^2.9.4",
"dbgate-datalib": "^3.9.5",
"dbgate-sqltree": "^3.9.5",
"dbgate-tools": "^3.9.5",
@@ -20,6 +22,7 @@
"json-stable-stringify": "^1.0.1",
"localforage": "^1.9.0",
"lodash": "^4.17.15",
+ "randomcolor": "^0.6.2",
"rollup": "^2.3.4",
"rollup-plugin-copy": "^3.3.0",
"rollup-plugin-css-only": "^3.1.0",
diff --git a/packages/web/src/charts/ChartCore.svelte b/packages/web/src/charts/ChartCore.svelte
new file mode 100644
index 000000000..49247f7dd
--- /dev/null
+++ b/packages/web/src/charts/ChartCore.svelte
@@ -0,0 +1,34 @@
+
+
+
diff --git a/packages/web/src/charts/ChartEditor.svelte b/packages/web/src/charts/ChartEditor.svelte
new file mode 100644
index 000000000..77b53f191
--- /dev/null
+++ b/packages/web/src/charts/ChartEditor.svelte
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if availableColumnNames.length > 0}
+ ({ value: col, label: col }))]}
+ />
+ {/if}
+
+ {#each availableColumnNames as col (col)}
+
+ {#if config[`dataColumn_${col}`]}
+ ({ value: color, label: _.startCase(color) })),
+ ]}
+ />
+ {/if}
+ {/each}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/web/src/charts/DataChart.svelte b/packages/web/src/charts/DataChart.svelte
new file mode 100644
index 000000000..17c695b10
--- /dev/null
+++ b/packages/web/src/charts/DataChart.svelte
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
diff --git a/packages/web/src/charts/chartDataLoader.ts b/packages/web/src/charts/chartDataLoader.ts
new file mode 100644
index 000000000..7f7bcbc54
--- /dev/null
+++ b/packages/web/src/charts/chartDataLoader.ts
@@ -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;
+}
diff --git a/packages/web/src/designer/Designer.svelte b/packages/web/src/designer/Designer.svelte
index 23975368c..243ca5d78 100644
--- a/packages/web/src/designer/Designer.svelte
+++ b/packages/web/src/designer/Designer.svelte
@@ -12,11 +12,13 @@
import DesignerReference from './DesignerReference.svelte';
import { writable } from 'svelte/store';
import { tick } from 'svelte';
+ import contextMenu from '../utility/contextMenu';
export let value;
export let onChange;
export let conid;
export let database;
+ // export let menu;
let domCanvas;
diff --git a/packages/web/src/modals/FormFieldTemplateLarge.svelte b/packages/web/src/forms/FormFieldTemplateLarge.svelte
similarity index 100%
rename from packages/web/src/modals/FormFieldTemplateLarge.svelte
rename to packages/web/src/forms/FormFieldTemplateLarge.svelte
diff --git a/packages/web/src/modals/FormFieldTemplateRow.svelte b/packages/web/src/forms/FormFieldTemplateRow.svelte
similarity index 100%
rename from packages/web/src/modals/FormFieldTemplateRow.svelte
rename to packages/web/src/forms/FormFieldTemplateRow.svelte
diff --git a/packages/web/src/forms/FormFieldTemplateTiny.svelte b/packages/web/src/forms/FormFieldTemplateTiny.svelte
new file mode 100644
index 000000000..dff6b6486
--- /dev/null
+++ b/packages/web/src/forms/FormFieldTemplateTiny.svelte
@@ -0,0 +1,40 @@
+
+
+
+ {#if type == 'checkbox'}
+
+
{label}
+ {:else}
+
+
+
+
+ {/if}
+
+
+
diff --git a/packages/web/src/forms/FormProvider.svelte b/packages/web/src/forms/FormProvider.svelte
index 11387d99b..073f26da7 100644
--- a/packages/web/src/forms/FormProvider.svelte
+++ b/packages/web/src/forms/FormProvider.svelte
@@ -1,6 +1,6 @@
+
+{#if $editorState.isLoading}
+
+{:else if $editorState.errorMessage}
+
+{:else}
+
+{/if}
diff --git a/packages/web/src/tabs/index.js b/packages/web/src/tabs/index.js
index d272c5a62..7f2973d77 100644
--- a/packages/web/src/tabs/index.js
+++ b/packages/web/src/tabs/index.js
@@ -7,7 +7,7 @@ import * as ShellTab from './ShellTab.svelte';
import * as ArchiveFileTab from './ArchiveFileTab.svelte';
import * as FreeTableTab from './FreeTableTab.svelte';
// import PluginTab from './PluginTab';
-// import ChartTab from './ChartTab';
+import * as ChartTab from './ChartTab.svelte';
import * as MarkdownEditorTab from './MarkdownEditorTab.svelte';
// import MarkdownViewTab from './MarkdownViewTab';
// import MarkdownPreviewTab from './MarkdownPreviewTab';
@@ -24,7 +24,7 @@ export default {
ArchiveFileTab,
FreeTableTab,
// PluginTab,
- // ChartTab,
+ ChartTab,
MarkdownEditorTab,
// MarkdownViewTab,
// MarkdownPreviewTab,
diff --git a/yarn.lock b/yarn.lock
index 6cd3e984f..3454249ab 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,13 @@
# 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":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@@ -160,6 +167,11 @@
exec-sh "^0.3.2"
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":
version "24.9.0"
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"
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:
version "2.1.8"
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"
object-visit "^1.0.0"
-color-convert@^1.9.0:
+color-convert@^1.9.0, 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==
@@ -1680,7 +1715,7 @@ color-name@1.1.3:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
-color-name@~1.1.4:
+color-name@^1.0.0, color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
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"
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"
@@ -5781,6 +5821,11 @@ randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
dependencies:
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:
version "1.0.4"
resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"