@graphenedata/cli 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.ts +25 -55
- package/dist/cli/cli.js +1194 -435
- package/dist/docs/graphene.md +1074 -166
- package/dist/ui/component-utilities/echarts.js +3 -1
- package/dist/ui/component-utilities/inputUtils.ts +11 -0
- package/dist/ui/component-utilities/themeStores.ts +35 -7
- package/dist/ui/components/Area.svelte +6 -3
- package/dist/ui/components/AreaChart.svelte +3 -1
- package/dist/ui/components/Bar.svelte +14 -8
- package/dist/ui/components/BarChart.svelte +3 -1
- package/dist/ui/components/BigValue.svelte +1 -1
- package/dist/ui/components/Chart.svelte +57 -101
- package/dist/ui/components/Column.svelte +2 -0
- package/dist/ui/components/ECharts.svelte +2 -0
- package/dist/ui/components/Line.svelte +8 -5
- package/dist/ui/components/LineChart.svelte +3 -2
- package/dist/ui/components/PieChart.svelte +1 -1
- package/dist/ui/components/QueryLoad.svelte +5 -6
- package/dist/ui/components/TableRow.svelte +1 -1
- package/dist/ui/components/_Table.svelte +2 -0
- package/dist/ui/internal/queryEngine.ts +39 -15
- package/dist/ui/internal/telemetry.ts +5 -3
- package/dist/ui/web.js +28 -12
- package/package.json +3 -2
- package/dist/docs/data_apps/components/charts/annotations.md +0 -673
- package/dist/docs/data_apps/components/charts/area-chart.md +0 -202
- package/dist/docs/data_apps/components/charts/bar-chart.md +0 -317
- package/dist/docs/data_apps/components/charts/box-plot.md +0 -190
- package/dist/docs/data_apps/components/charts/bubble-chart.md +0 -151
- package/dist/docs/data_apps/components/charts/calendar-heatmap.md +0 -112
- package/dist/docs/data_apps/components/charts/custom-echarts.md +0 -308
- package/dist/docs/data_apps/components/charts/echarts-options.md +0 -217
- package/dist/docs/data_apps/components/charts/funnel-chart.md +0 -106
- package/dist/docs/data_apps/components/charts/heatmap.md +0 -180
- package/dist/docs/data_apps/components/charts/histogram.md +0 -107
- package/dist/docs/data_apps/components/charts/line-chart.md +0 -265
- package/dist/docs/data_apps/components/charts/mixed-type-charts.md +0 -240
- package/dist/docs/data_apps/components/charts/sankey-diagram.md +0 -301
- package/dist/docs/data_apps/components/charts/scatter-plot.md +0 -134
- package/dist/docs/data_apps/components/charts/sparkline.md +0 -68
- package/dist/docs/data_apps/components/data/big-value.md +0 -153
- package/dist/docs/data_apps/components/data/delta.md +0 -89
- package/dist/docs/data_apps/components/data/table.md +0 -470
- package/dist/docs/data_apps/components/data/value.md +0 -97
- package/dist/docs/data_apps/components/inputs/button-group.md +0 -154
- package/dist/docs/data_apps/components/inputs/checkbox.md +0 -52
- package/dist/docs/data_apps/components/inputs/date-input.md +0 -131
- package/dist/docs/data_apps/components/inputs/date-range.md +0 -124
- package/dist/docs/data_apps/components/inputs/dimension-grid.md +0 -67
- package/dist/docs/data_apps/components/inputs/dropdown.md +0 -199
- package/dist/docs/data_apps/components/inputs/index.md +0 -3
- package/dist/docs/data_apps/components/inputs/slider.md +0 -126
- package/dist/docs/data_apps/components/inputs/text-input.md +0 -86
- package/dist/docs/data_apps/components/maps/area-map.md +0 -397
- package/dist/docs/data_apps/components/maps/base-map.md +0 -269
- package/dist/docs/data_apps/components/maps/bubble-map.md +0 -361
- package/dist/docs/data_apps/components/maps/point-map.md +0 -326
- package/dist/docs/data_apps/components/maps/us-map.md +0 -167
- package/dist/docs/data_apps/components/ui/accordion.md +0 -116
- package/dist/docs/data_apps/components/ui/alert.md +0 -37
- package/dist/docs/data_apps/components/ui/big-link.md +0 -19
- package/dist/docs/data_apps/components/ui/details.md +0 -58
- package/dist/docs/data_apps/components/ui/download-data.md +0 -41
- package/dist/docs/data_apps/components/ui/embed.md +0 -47
- package/dist/docs/data_apps/components/ui/grid.md +0 -45
- package/dist/docs/data_apps/components/ui/image.md +0 -61
- package/dist/docs/data_apps/components/ui/info.md +0 -47
- package/dist/docs/data_apps/components/ui/last-refreshed.md +0 -28
- package/dist/docs/data_apps/components/ui/link-button.md +0 -20
- package/dist/docs/data_apps/components/ui/link.md +0 -40
- package/dist/docs/data_apps/components/ui/modal.md +0 -57
- package/dist/docs/data_apps/components/ui/note.md +0 -32
- package/dist/docs/data_apps/components/ui/print-format-components.md +0 -85
- package/dist/docs/data_apps/components/ui/tabs.md +0 -122
|
@@ -12,7 +12,7 @@ import * as chartWindowDebug from './chartWindowDebug'
|
|
|
12
12
|
* } EChartsActionOptions
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
const ANIMATION_DURATION =
|
|
15
|
+
const ANIMATION_DURATION = 0
|
|
16
16
|
const pendingChartsKey = Symbol.for('graphene.pendingCharts')
|
|
17
17
|
|
|
18
18
|
/** @returns {Set<number> | null} */
|
|
@@ -139,6 +139,7 @@ const echartsAction = (node, options) => {
|
|
|
139
139
|
// Initial options set:
|
|
140
140
|
chart.setOption({
|
|
141
141
|
...options.config,
|
|
142
|
+
animation: false,
|
|
142
143
|
animationDuration: ANIMATION_DURATION,
|
|
143
144
|
animationDurationUpdate: ANIMATION_DURATION,
|
|
144
145
|
})
|
|
@@ -219,6 +220,7 @@ const echartsAction = (node, options) => {
|
|
|
219
220
|
chart.setOption(
|
|
220
221
|
{
|
|
221
222
|
...options.config,
|
|
223
|
+
animation: false,
|
|
222
224
|
animationDuration: ANIMATION_DURATION,
|
|
223
225
|
animationDurationUpdate: ANIMATION_DURATION,
|
|
224
226
|
},
|
|
@@ -23,3 +23,14 @@ export function serializeValue (value: unknown): string {
|
|
|
23
23
|
let str = String(value)
|
|
24
24
|
return `'${str.replace(/'/g, "''")}'`
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
// Parse a comma-separated list into an array of trimmed strings.
|
|
28
|
+
// - Strings are split on commas; whitespace trimmed; empty entries removed.
|
|
29
|
+
// - Arrays are normalized by trimming string items and String()-ing non-strings.
|
|
30
|
+
// - null/undefined -> []
|
|
31
|
+
export function parseCommaList (value: unknown): string[] {
|
|
32
|
+
if (value === undefined || value === null) return []
|
|
33
|
+
if (Array.isArray(value)) return value.map(v => typeof v === 'string' ? v.trim() : String(v)).filter(v => v.length > 0)
|
|
34
|
+
if (typeof value === 'string') return value.split(',').map(v => v.trim()).filter(v => v.length > 0)
|
|
35
|
+
return [String(value).trim()].filter(v => v.length > 0)
|
|
36
|
+
}
|
|
@@ -12,7 +12,7 @@ type ThemeStores = {
|
|
|
12
12
|
activeAppearance: Readable<Appearance>
|
|
13
13
|
theme: Readable<Theme>
|
|
14
14
|
resolveColor: <T>(input: T) => Readable<T | string | undefined>
|
|
15
|
-
resolveColorsObject: (input: Record<string, unknown> | undefined) => Readable<Record<string, string | undefined> | undefined>
|
|
15
|
+
resolveColorsObject: (input: Record<string, unknown> | string | undefined) => Readable<Record<string, string | undefined> | undefined>
|
|
16
16
|
resolveColorPalette: (input: unknown) => Readable<string[] | undefined>
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -55,6 +55,14 @@ const isReadable = <T>(value: unknown): value is Readable<T> => {
|
|
|
55
55
|
return Boolean(value && typeof value === 'object' && 'subscribe' in (value as any))
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
const parseJson = (value: string): unknown => {
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(value)
|
|
61
|
+
} catch {
|
|
62
|
+
return undefined
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
58
66
|
const normalizeColor = (value: unknown): string | undefined => {
|
|
59
67
|
if (value == null) return undefined
|
|
60
68
|
if (Array.isArray(value)) return normalizeColor(value[0])
|
|
@@ -64,25 +72,38 @@ const normalizeColor = (value: unknown): string | undefined => {
|
|
|
64
72
|
if (!trimmed) return undefined
|
|
65
73
|
if (trimmed === 'true' || trimmed === 'false') return trimmed
|
|
66
74
|
if (DEFAULT_THEME.colors[trimmed]) return DEFAULT_THEME.colors[trimmed]
|
|
67
|
-
|
|
68
|
-
if (
|
|
75
|
+
let lower = trimmed.toLowerCase()
|
|
76
|
+
if (trimmed.startsWith('#')) return trimmed
|
|
77
|
+
if (lower.startsWith('rgb') || lower.startsWith('hsl')) return trimmed
|
|
69
78
|
return trimmed
|
|
70
79
|
}
|
|
71
80
|
|
|
72
|
-
const resolveColor = <T>(input: T): Readable<T | string | undefined> => {
|
|
81
|
+
export const resolveColor = <T>(input: T): Readable<T | string | undefined> => {
|
|
73
82
|
if (isReadable<T | string | undefined>(input)) return input
|
|
74
83
|
return readable(normalizeColor(input) as T | string | undefined)
|
|
75
84
|
}
|
|
76
85
|
|
|
77
|
-
const resolveColorsObject = (input: Record<string, unknown> | undefined): Readable<Record<string, string | undefined> | undefined> => {
|
|
86
|
+
export const resolveColorsObject = (input: Record<string, unknown> | string | undefined): Readable<Record<string, string | undefined> | undefined> => {
|
|
78
87
|
if (isReadable<Record<string, string | undefined> | undefined>(input)) return input
|
|
79
88
|
if (!input) return readable(undefined)
|
|
80
89
|
|
|
81
|
-
let
|
|
90
|
+
let record: Record<string, unknown> | undefined = input as Record<string, unknown>
|
|
91
|
+
if (typeof input === 'string') {
|
|
92
|
+
let parsed = parseJson(input.trim())
|
|
93
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
94
|
+
record = parsed as Record<string, unknown>
|
|
95
|
+
} else {
|
|
96
|
+
return readable(undefined)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!record) return readable(undefined)
|
|
101
|
+
|
|
102
|
+
let entries = Object.entries(record).map(([key, value]) => [key, normalizeColor(value)] as const)
|
|
82
103
|
return readable(Object.fromEntries(entries))
|
|
83
104
|
}
|
|
84
105
|
|
|
85
|
-
const resolveColorPalette = (input: unknown): Readable<string[] | undefined> => {
|
|
106
|
+
export const resolveColorPalette = (input: unknown): Readable<string[] | undefined> => {
|
|
86
107
|
if (isReadable<string[] | undefined>(input)) return input
|
|
87
108
|
if (input == null) return readable(DEFAULT_PALETTE)
|
|
88
109
|
|
|
@@ -90,6 +111,13 @@ const resolveColorPalette = (input: unknown): Readable<string[] | undefined> =>
|
|
|
90
111
|
let key = input.trim()
|
|
91
112
|
if (!key || key === 'default') return readable(DEFAULT_PALETTE)
|
|
92
113
|
if (DEFAULT_THEME.colorPalettes[key]) return readable(DEFAULT_THEME.colorPalettes[key])
|
|
114
|
+
if (key.startsWith('[')) {
|
|
115
|
+
let parsed = parseJson(key)
|
|
116
|
+
if (Array.isArray(parsed)) {
|
|
117
|
+
let values = parsed.map(item => normalizeColor(item)).filter(Boolean) as string[]
|
|
118
|
+
return readable(values.length ? values : DEFAULT_PALETTE)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
93
121
|
if (key.includes(',')) {
|
|
94
122
|
let values = key.split(',').map(part => normalizeColor(part)).filter(Boolean) as string[]
|
|
95
123
|
return readable(values.length ? values : DEFAULT_PALETTE)
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
getFormatObjectFromString,
|
|
11
11
|
} from '../component-utilities/formatting.js'
|
|
12
12
|
import {getThemeStores} from '../component-utilities/themeStores'
|
|
13
|
+
import {parseCommaList} from '../component-utilities/inputUtils.ts'
|
|
13
14
|
|
|
14
15
|
const {resolveColor} = getThemeStores()
|
|
15
16
|
const props = getContext(propKey)
|
|
@@ -72,12 +73,14 @@
|
|
|
72
73
|
$: xMismatch = $props.xMismatch
|
|
73
74
|
$: columnSummary = $props.columnSummary
|
|
74
75
|
$: series = seriesSet ? series : $props.series
|
|
75
|
-
$: resolvedY = ySet ? y : $props.y
|
|
76
|
+
$: resolvedY = ySet ? parseCommaList(y) : $props.y
|
|
77
|
+
$: seriesOrder = parseCommaList(seriesOrder)
|
|
76
78
|
|
|
77
79
|
$: {
|
|
78
|
-
if (!series &&
|
|
80
|
+
if (!series && (!Array.isArray(resolvedY) || resolvedY.length === 1)) {
|
|
79
81
|
stackName = undefined
|
|
80
|
-
|
|
82
|
+
let col = Array.isArray(resolvedY) ? resolvedY[0] : resolvedY
|
|
83
|
+
if (columnSummary?.[col]) name = name ?? formatTitle(col, columnSummary[col].title)
|
|
81
84
|
} else {
|
|
82
85
|
stackName = 'area'
|
|
83
86
|
data = getCompletedData(data, x, resolvedY, series, false, xType === 'value')
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import Area from './Area.svelte'
|
|
4
4
|
import QueryLoad from './QueryLoad.svelte'
|
|
5
5
|
import {getThemeStores} from '../component-utilities/themeStores'
|
|
6
|
+
import {parseCommaList} from '../component-utilities/inputUtils.ts'
|
|
6
7
|
|
|
7
8
|
const {resolveColor, resolveColorsObject, resolveColorPalette} = getThemeStores()
|
|
8
9
|
|
|
@@ -83,9 +84,10 @@
|
|
|
83
84
|
export let xLabelWrap = undefined
|
|
84
85
|
</script>
|
|
85
86
|
|
|
86
|
-
<QueryLoad data={data} fields={
|
|
87
|
+
<QueryLoad data={data} fields={{x, y: parseCommaList(y), series}} let:loaded>
|
|
87
88
|
<Chart
|
|
88
89
|
data={loaded}
|
|
90
|
+
chartContext={{data, x, y, series}}
|
|
89
91
|
{x}
|
|
90
92
|
{y}
|
|
91
93
|
{xFmt}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
getFormatObjectFromString,
|
|
17
17
|
} from '../component-utilities/formatting.js'
|
|
18
18
|
import {getThemeStores} from '../component-utilities/themeStores'
|
|
19
|
+
import {parseCommaList} from '../component-utilities/inputUtils.ts'
|
|
19
20
|
|
|
20
21
|
const {resolveColor} = getThemeStores()
|
|
21
22
|
|
|
@@ -77,8 +78,8 @@
|
|
|
77
78
|
// Prop check. If local props supplied, use those. Otherwise fall back to global props.
|
|
78
79
|
$: data = $props.data
|
|
79
80
|
$: x = $props.x
|
|
80
|
-
$: y = ySet ? y : $props.y
|
|
81
|
-
$: y2 = y2Set ? y2 : $props.y2
|
|
81
|
+
$: y = ySet ? parseCommaList(y) : $props.y
|
|
82
|
+
$: y2 = y2Set ? parseCommaList(y2) : $props.y2
|
|
82
83
|
$: yFormat = $props.yFormat
|
|
83
84
|
$: y2Format = $props.y2Format
|
|
84
85
|
$: yCount = $props.yCount
|
|
@@ -89,14 +90,18 @@
|
|
|
89
90
|
$: columnSummary = $props.columnSummary
|
|
90
91
|
$: sort = $props.sort
|
|
91
92
|
$: series = seriesSet ? series : $props.series
|
|
93
|
+
$: seriesOrder = parseCommaList(seriesOrder)
|
|
92
94
|
|
|
93
95
|
let stackedData
|
|
94
96
|
let sortOrder
|
|
95
97
|
let defaultLabelPosition
|
|
96
98
|
|
|
97
|
-
$: if (!series &&
|
|
99
|
+
$: if (!series && (!Array.isArray(y) || y.length === 1)) {
|
|
98
100
|
// Single Series
|
|
99
|
-
|
|
101
|
+
{
|
|
102
|
+
let col = Array.isArray(y) ? y[0] : y
|
|
103
|
+
name = name ?? formatTitle(col, columnSummary[col].title)
|
|
104
|
+
}
|
|
100
105
|
|
|
101
106
|
if (swapXY && xType !== 'category') {
|
|
102
107
|
data = getCompletedData(data, x, y, series, true, xType !== 'time')
|
|
@@ -112,10 +117,11 @@
|
|
|
112
117
|
if (sort === true && xType === 'category') {
|
|
113
118
|
stackedData = getStackedData(data, x, y)
|
|
114
119
|
|
|
115
|
-
if (
|
|
120
|
+
if (Array.isArray(y) && y.length > 1) {
|
|
116
121
|
stackedData = getSortedData(stackedData, 'stackTotal', false)
|
|
117
122
|
} else {
|
|
118
|
-
|
|
123
|
+
let col = Array.isArray(y) ? y[0] : y
|
|
124
|
+
stackedData = getSortedData(stackedData, col, false)
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
sortOrder = stackedData.map((d) => d[x])
|
|
@@ -233,7 +239,7 @@
|
|
|
233
239
|
if (
|
|
234
240
|
labels === true &&
|
|
235
241
|
type === 'stacked' &&
|
|
236
|
-
(
|
|
242
|
+
((Array.isArray(y) && y.length > 1) || (series !== undefined)) &&
|
|
237
243
|
stackTotalLabel === true &&
|
|
238
244
|
series !== x
|
|
239
245
|
) {
|
|
@@ -309,7 +315,7 @@
|
|
|
309
315
|
} else {
|
|
310
316
|
d.yAxis[0] = {...d.yAxis[0], ...chartOverrides.yAxis}
|
|
311
317
|
d.xAxis = {...d.xAxis, ...chartOverrides.xAxis}
|
|
312
|
-
if (
|
|
318
|
+
if (y2Count > 0) {
|
|
313
319
|
d.yAxis[1] = {...d.yAxis[1], show: true}
|
|
314
320
|
if (['line', 'bar', 'scatter'].includes(y2SeriesType)) {
|
|
315
321
|
for (let i = 0; i < y2Count; i++) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import Bar from './Bar.svelte'
|
|
4
4
|
import QueryLoad from './QueryLoad.svelte'
|
|
5
5
|
import {getThemeStores} from '../component-utilities/themeStores'
|
|
6
|
+
import {parseCommaList} from '../component-utilities/inputUtils.ts'
|
|
6
7
|
|
|
7
8
|
const {resolveColor, resolveColorsObject, resolveColorPalette} = getThemeStores()
|
|
8
9
|
|
|
@@ -114,9 +115,10 @@
|
|
|
114
115
|
export let xLabelWrap = undefined
|
|
115
116
|
</script>
|
|
116
117
|
|
|
117
|
-
<QueryLoad data={data} fields={
|
|
118
|
+
<QueryLoad data={data} fields={{x, y: parseCommaList(y), y2: parseCommaList(y2), series}} let:loaded>
|
|
118
119
|
<Chart
|
|
119
120
|
data={loaded}
|
|
121
|
+
chartContext={{data, x, y, series}}
|
|
120
122
|
{x}
|
|
121
123
|
{y}
|
|
122
124
|
{y2}
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
}
|
|
35
35
|
</script>
|
|
36
36
|
|
|
37
|
-
<QueryLoad {data} fields={
|
|
37
|
+
<QueryLoad {data} fields={{value}} let:loaded>
|
|
38
38
|
<div class="big-value">
|
|
39
39
|
{#if title}<div class="big-value__title">{title}</div>{/if}
|
|
40
40
|
{#if subtitle}<div class="big-value__subtitle">{subtitle}</div>{/if}
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
import checkInputs from '../component-utilities/checkInputs.js'
|
|
24
24
|
import {getThemeStores} from '../component-utilities/themeStores'
|
|
25
25
|
import {toBoolean} from '../component-utilities/convert'
|
|
26
|
+
import {parseCommaList} from '../component-utilities/inputUtils.ts'
|
|
27
|
+
import {logError} from '../internal/telemetry.ts'
|
|
26
28
|
|
|
27
29
|
const {theme, resolveColor, resolveColorsObject, resolveColorPalette} = getThemeStores()
|
|
28
30
|
|
|
@@ -31,6 +33,7 @@
|
|
|
31
33
|
// ---------------------------------------------------------------------------------------
|
|
32
34
|
// Data and columns:
|
|
33
35
|
export let data = undefined
|
|
36
|
+
export let chartContext = undefined
|
|
34
37
|
export let queryID = undefined
|
|
35
38
|
export let x = undefined
|
|
36
39
|
export let y = undefined
|
|
@@ -248,7 +251,10 @@
|
|
|
248
251
|
inputCols = []
|
|
249
252
|
optCols = []
|
|
250
253
|
uColName = []
|
|
251
|
-
|
|
254
|
+
// Normalize list-like inputs first
|
|
255
|
+
y = parseCommaList(y)
|
|
256
|
+
y2 = parseCommaList(y2)
|
|
257
|
+
ySet = y.length > 0
|
|
252
258
|
xSet = x ? true : false
|
|
253
259
|
|
|
254
260
|
checkInputs(data) // check that dataset exists
|
|
@@ -284,7 +290,7 @@
|
|
|
284
290
|
}
|
|
285
291
|
}
|
|
286
292
|
|
|
287
|
-
y = unusedColumns
|
|
293
|
+
y = unusedColumns // always array; empty handled by required prop checks
|
|
288
294
|
}
|
|
289
295
|
// Establish required columns based on chart type:
|
|
290
296
|
if (bubble) {
|
|
@@ -320,40 +326,17 @@
|
|
|
320
326
|
}
|
|
321
327
|
|
|
322
328
|
// Fix for stacked100 overwriting y variable. Bandaid fix - not a long-term solution:
|
|
323
|
-
if (stacked100 === true && y.includes('_pct') && originalRun === false) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
y[i] = y[i].replace('_pct', '')
|
|
327
|
-
}
|
|
328
|
-
originalRun = false
|
|
329
|
-
} else {
|
|
330
|
-
y = y.replace('_pct', '')
|
|
331
|
-
originalRun = false
|
|
332
|
-
}
|
|
329
|
+
if (stacked100 === true && Array.isArray(y) && y.some(col => col.includes('_pct')) && originalRun === false) {
|
|
330
|
+
for (let i = 0; i < y.length; i++) y[i] = y[i].replace('_pct', '')
|
|
331
|
+
originalRun = false
|
|
333
332
|
}
|
|
334
333
|
|
|
335
334
|
// Check the inputs supplied to the chart:
|
|
336
335
|
if (x) {
|
|
337
336
|
inputCols.push(x)
|
|
338
337
|
}
|
|
339
|
-
if (y)
|
|
340
|
-
|
|
341
|
-
for (i = 0; i < y.length; i++) {
|
|
342
|
-
inputCols.push(y[i])
|
|
343
|
-
}
|
|
344
|
-
} else {
|
|
345
|
-
inputCols.push(y)
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
if (y2) {
|
|
349
|
-
if (typeof y2 === 'object') {
|
|
350
|
-
for (i = 0; i < y2.length; i++) {
|
|
351
|
-
inputCols.push(y2[i])
|
|
352
|
-
}
|
|
353
|
-
} else {
|
|
354
|
-
inputCols.push(y2)
|
|
355
|
-
}
|
|
356
|
-
}
|
|
338
|
+
if (Array.isArray(y)) for (i = 0; i < y.length; i++) inputCols.push(y[i])
|
|
339
|
+
if (Array.isArray(y2)) for (i = 0; i < y2.length; i++) inputCols.push(y2[i])
|
|
357
340
|
if (size) {
|
|
358
341
|
inputCols.push(size)
|
|
359
342
|
}
|
|
@@ -372,18 +355,8 @@
|
|
|
372
355
|
|
|
373
356
|
if (stacked100 === true) {
|
|
374
357
|
data = getStackPercentages(data, x, y)
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
for (let i = 0; i < y.length; i++) {
|
|
378
|
-
y[i] = y[i] + '_pct'
|
|
379
|
-
}
|
|
380
|
-
originalRun = false
|
|
381
|
-
} else {
|
|
382
|
-
y = y + '_pct'
|
|
383
|
-
originalRun = false
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Re-run column summary for new columns (not ideal):
|
|
358
|
+
for (let i = 0; i < y.length; i++) y[i] = y[i] + '_pct'
|
|
359
|
+
originalRun = false
|
|
387
360
|
columnSummary = getColumnSummary(data)
|
|
388
361
|
}
|
|
389
362
|
|
|
@@ -426,7 +399,7 @@
|
|
|
426
399
|
}
|
|
427
400
|
|
|
428
401
|
// Throw error if attempting to plot secondary y-axis on horizontal chart:
|
|
429
|
-
if (swapXY && y2) {
|
|
402
|
+
if (swapXY && y2.length) {
|
|
430
403
|
throw Error(
|
|
431
404
|
'Horizontal charts do not support a secondary y-axis. You can either set swapXY=false or remove the y2 prop from your chart.',
|
|
432
405
|
)
|
|
@@ -444,7 +417,10 @@
|
|
|
444
417
|
// Sort data based on xType
|
|
445
418
|
// ---------------------------------------------------------------------------------------
|
|
446
419
|
if (sort) {
|
|
447
|
-
let sortColumn =
|
|
420
|
+
let sortColumn = x
|
|
421
|
+
if (xDataType === 'category') {
|
|
422
|
+
sortColumn = Array.isArray(y) ? (y[0] ?? x) : x
|
|
423
|
+
}
|
|
448
424
|
let sortAscending = xDataType !== 'category'
|
|
449
425
|
data = getSortedData(data, sortColumn, sortAscending)
|
|
450
426
|
}
|
|
@@ -477,38 +453,16 @@
|
|
|
477
453
|
xFormat = columnSummary[x].format
|
|
478
454
|
}
|
|
479
455
|
|
|
480
|
-
if (
|
|
456
|
+
if (y.length === 0) {
|
|
481
457
|
yFormat = 'str'
|
|
482
458
|
} else {
|
|
483
|
-
if (yFmt)
|
|
484
|
-
|
|
485
|
-
yFormat = getFormatObjectFromString(yFmt, columnSummary[y[0]].format?.valueType)
|
|
486
|
-
} else {
|
|
487
|
-
yFormat = getFormatObjectFromString(yFmt, columnSummary[y].format?.valueType)
|
|
488
|
-
}
|
|
489
|
-
} else {
|
|
490
|
-
if (typeof y === 'object') {
|
|
491
|
-
yFormat = columnSummary[y[0]].format
|
|
492
|
-
} else {
|
|
493
|
-
yFormat = columnSummary[y].format
|
|
494
|
-
}
|
|
495
|
-
}
|
|
459
|
+
if (yFmt) yFormat = getFormatObjectFromString(yFmt, columnSummary[y[0]].format?.valueType)
|
|
460
|
+
else yFormat = columnSummary[y[0]].format
|
|
496
461
|
}
|
|
497
462
|
|
|
498
|
-
if (y2) {
|
|
499
|
-
if (y2Fmt)
|
|
500
|
-
|
|
501
|
-
y2Format = getFormatObjectFromString(y2Fmt, columnSummary[y2[0]].format?.valueType)
|
|
502
|
-
} else {
|
|
503
|
-
y2Format = getFormatObjectFromString(y2Fmt, columnSummary[y2].format?.valueType)
|
|
504
|
-
}
|
|
505
|
-
} else {
|
|
506
|
-
if (typeof y2 === 'object') {
|
|
507
|
-
y2Format = columnSummary[y2[0]].format
|
|
508
|
-
} else {
|
|
509
|
-
y2Format = columnSummary[y2].format
|
|
510
|
-
}
|
|
511
|
-
}
|
|
463
|
+
if (y2.length) {
|
|
464
|
+
if (y2Fmt) y2Format = getFormatObjectFromString(y2Fmt, columnSummary[y2[0]].format?.valueType)
|
|
465
|
+
else y2Format = columnSummary[y2[0]].format
|
|
512
466
|
}
|
|
513
467
|
|
|
514
468
|
if (size) {
|
|
@@ -521,21 +475,9 @@
|
|
|
521
475
|
|
|
522
476
|
xUnitSummary = columnSummary[x].columnUnitSummary
|
|
523
477
|
|
|
524
|
-
if (y)
|
|
525
|
-
if (typeof y === 'object') {
|
|
526
|
-
yUnitSummary = columnSummary[y[0]].columnUnitSummary
|
|
527
|
-
} else {
|
|
528
|
-
yUnitSummary = columnSummary[y].columnUnitSummary
|
|
529
|
-
}
|
|
530
|
-
}
|
|
478
|
+
if (y.length) yUnitSummary = columnSummary[y[0]].columnUnitSummary
|
|
531
479
|
|
|
532
|
-
if (y2)
|
|
533
|
-
if (typeof y2 === 'object') {
|
|
534
|
-
y2UnitSummary = columnSummary[y2[0]].columnUnitSummary
|
|
535
|
-
} else {
|
|
536
|
-
y2UnitSummary = columnSummary[y2].columnUnitSummary
|
|
537
|
-
}
|
|
538
|
-
}
|
|
480
|
+
if (y2.length) y2UnitSummary = columnSummary[y2[0]].columnUnitSummary
|
|
539
481
|
|
|
540
482
|
if (xAxisTitle === 'true') {
|
|
541
483
|
xAxisTitle = formatTitle(x, xFormat)
|
|
@@ -544,13 +486,21 @@
|
|
|
544
486
|
}
|
|
545
487
|
|
|
546
488
|
if (yAxisTitle === 'true') {
|
|
547
|
-
|
|
489
|
+
if (y.length === 1) {
|
|
490
|
+
yAxisTitle = formatTitle(y[0], yFormat)
|
|
491
|
+
} else {
|
|
492
|
+
yAxisTitle = ''
|
|
493
|
+
}
|
|
548
494
|
} else if (yAxisTitle === 'false') {
|
|
549
495
|
yAxisTitle = ''
|
|
550
496
|
}
|
|
551
497
|
|
|
552
498
|
if (y2AxisTitle === 'true') {
|
|
553
|
-
|
|
499
|
+
if (y2.length === 1) {
|
|
500
|
+
y2AxisTitle = formatTitle(y2[0], y2Format)
|
|
501
|
+
} else {
|
|
502
|
+
y2AxisTitle = ''
|
|
503
|
+
}
|
|
554
504
|
} else if (y2AxisTitle === 'false') {
|
|
555
505
|
y2AxisTitle = ''
|
|
556
506
|
}
|
|
@@ -558,18 +508,13 @@
|
|
|
558
508
|
// ---------------------------------------------------------------------------------------
|
|
559
509
|
// Get total series count
|
|
560
510
|
// ---------------------------------------------------------------------------------------
|
|
561
|
-
let yCount =
|
|
511
|
+
let yCount = y.length
|
|
562
512
|
let seriesCount = series ? getDistinctCount(data, series) : 1
|
|
563
513
|
let ySeriesCount = yCount * seriesCount
|
|
564
514
|
|
|
565
515
|
// y2Count may need to be adjusted to also factor in the series column. For now, we really
|
|
566
516
|
// only need to know that it's multi-series, so > 1 is sufficient
|
|
567
|
-
let y2Count =
|
|
568
|
-
if (typeof y2 === 'object') {
|
|
569
|
-
y2Count = y2.length
|
|
570
|
-
} else if (y2) {
|
|
571
|
-
y2Count = 1
|
|
572
|
-
}
|
|
517
|
+
let y2Count = y2.length
|
|
573
518
|
let totalSeriesCount = ySeriesCount + y2Count
|
|
574
519
|
|
|
575
520
|
// ---------------------------------------------------------------------------------------
|
|
@@ -592,15 +537,13 @@
|
|
|
592
537
|
}
|
|
593
538
|
|
|
594
539
|
let minYValue
|
|
595
|
-
if (
|
|
540
|
+
if (y.length) {
|
|
596
541
|
minYValue = columnSummary[y[0]].columnUnitSummary.min
|
|
597
542
|
for (let i = 0; i < y.length; i++) {
|
|
598
543
|
if (columnSummary[y[i]].columnUnitSummary.min < minYValue) {
|
|
599
544
|
minYValue = columnSummary[y[i]].columnUnitSummary.min
|
|
600
545
|
}
|
|
601
546
|
}
|
|
602
|
-
} else if (y) {
|
|
603
|
-
minYValue = columnSummary[y].columnUnitSummary.min
|
|
604
547
|
}
|
|
605
548
|
|
|
606
549
|
if (yLog === true && minYValue <= 0 && minYValue !== null) {
|
|
@@ -745,7 +688,7 @@
|
|
|
745
688
|
}
|
|
746
689
|
} else {
|
|
747
690
|
let primaryAxisColor = (() => {
|
|
748
|
-
if (!y2) return undefined
|
|
691
|
+
if (!(Array.isArray(y2) && y2.length)) return undefined
|
|
749
692
|
if ($yAxisColorStore === 'true') return $colorPaletteStore[0]
|
|
750
693
|
if ($yAxisColorStore === 'false') return undefined
|
|
751
694
|
return $yAxisColorStore
|
|
@@ -855,7 +798,7 @@
|
|
|
855
798
|
|
|
856
799
|
hasTitle = title ? true : false
|
|
857
800
|
hasSubtitle = subtitle ? true : false
|
|
858
|
-
hasLegend = legend * (series !== null || (
|
|
801
|
+
hasLegend = legend * (series !== null || (y.length > 1))
|
|
859
802
|
hasTopAxisTitle = yAxisTitle !== '' && swapXY
|
|
860
803
|
hasBottomAxisTitle = xAxisTitle !== '' && !swapXY
|
|
861
804
|
|
|
@@ -1044,6 +987,18 @@
|
|
|
1044
987
|
error = e.message
|
|
1045
988
|
let setTextRed = '\x1b[31m%s\x1b[0m'
|
|
1046
989
|
console.error(setTextRed, `Error in ${chartType}: ${e.message}`)
|
|
990
|
+
|
|
991
|
+
// Make an "id" for the chart so its clear to users/agents exactly which caused an error.
|
|
992
|
+
let fieldStr = Object.entries(chartContext || {})
|
|
993
|
+
.filter(([_, val]) => {
|
|
994
|
+
if (Array.isArray(val)) return val.length > 0
|
|
995
|
+
if (typeof val === 'string') return val.trim().length > 0
|
|
996
|
+
return Boolean(val)
|
|
997
|
+
})
|
|
998
|
+
.map(([name, val]) => `${name}="${Array.isArray(val) ? val.join(', ') : val}"`)
|
|
999
|
+
let id = `${title || chartType} (${fieldStr.join(' ')})`
|
|
1000
|
+
logError(e, {id})
|
|
1001
|
+
|
|
1047
1002
|
props.update((d) => {
|
|
1048
1003
|
return {...d, error}
|
|
1049
1004
|
})
|
|
@@ -1059,11 +1014,12 @@
|
|
|
1059
1014
|
{width}
|
|
1060
1015
|
{data}
|
|
1061
1016
|
{queryID}
|
|
1017
|
+
chartTitle={title}
|
|
1062
1018
|
{echartsOptions}
|
|
1063
1019
|
{seriesOptions}
|
|
1064
1020
|
{connectGroup}
|
|
1065
1021
|
{xAxisLabelOverflow}
|
|
1066
|
-
seriesColors={seriesColorsStore}
|
|
1022
|
+
seriesColors={$seriesColorsStore}
|
|
1067
1023
|
/>
|
|
1068
1024
|
{:else}
|
|
1069
1025
|
<ErrorChart {error} title={chartType} />
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import {propKey, strictBuild} from '../component-utilities/chartContext.js'
|
|
8
8
|
import {getThemeStores} from '../component-utilities/themeStores'
|
|
9
9
|
import {toBoolean} from '../component-utilities/convert'
|
|
10
|
+
import {parseCommaList} from '../component-utilities/inputUtils.ts'
|
|
10
11
|
|
|
11
12
|
export let id: string
|
|
12
13
|
export let description: string | undefined = undefined
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
$: colorScaleStore = resolveColorPalette(colorScale)
|
|
65
66
|
|
|
66
67
|
const props = getContext(propKey)
|
|
68
|
+
$: colorBreakpoints = parseCommaList(colorBreakpoints)
|
|
67
69
|
|
|
68
70
|
const identifier = Symbol('GrapheneColumn')
|
|
69
71
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
export let seriesColors: any = undefined
|
|
17
17
|
export let connectGroup: string | undefined = undefined
|
|
18
18
|
export let xAxisLabelOverflow: 'truncate' | 'break' | undefined = undefined
|
|
19
|
+
export let chartTitle: string | undefined = undefined
|
|
19
20
|
|
|
20
21
|
const dispatch = createEventDispatcher()
|
|
21
22
|
const isBrowser = typeof window !== 'undefined'
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
{:else}
|
|
34
35
|
<div
|
|
35
36
|
class="echarts-chart"
|
|
37
|
+
data-chart-title={chartTitle ?? undefined}
|
|
36
38
|
data-query-id={queryID}
|
|
37
39
|
style={`height:${toDimension(height, '240px')};width:${toDimension(width, '100%')}`}
|
|
38
40
|
use:echarts={{
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import {formatValue, getFormatObjectFromString} from '../component-utilities/formatting.js'
|
|
9
9
|
import {getThemeStores} from '../component-utilities/themeStores'
|
|
10
10
|
import {toBoolean} from '../component-utilities/convert'
|
|
11
|
+
import {parseCommaList} from '../component-utilities/inputUtils.ts'
|
|
11
12
|
|
|
12
13
|
const {resolveColor} = getThemeStores()
|
|
13
14
|
const props = getContext(propKey)
|
|
@@ -90,12 +91,14 @@
|
|
|
90
91
|
$: xMismatch = $props.xMismatch
|
|
91
92
|
$: columnSummary = $props.columnSummary
|
|
92
93
|
$: series = seriesSet ? series : $props.series
|
|
93
|
-
$: resolvedY = ySet ? y : $props.y
|
|
94
|
-
$: resolvedY2 = y2Set ? y2 : $props.y2
|
|
94
|
+
$: resolvedY = ySet ? parseCommaList(y) : $props.y
|
|
95
|
+
$: resolvedY2 = y2Set ? parseCommaList(y2) : $props.y2
|
|
96
|
+
$: seriesOrder = parseCommaList(seriesOrder)
|
|
95
97
|
|
|
96
98
|
$: {
|
|
97
|
-
if (!series &&
|
|
98
|
-
|
|
99
|
+
if (!series && (!Array.isArray(resolvedY) || resolvedY.length === 1)) {
|
|
100
|
+
let col = Array.isArray(resolvedY) ? resolvedY[0] : resolvedY
|
|
101
|
+
if (columnSummary?.[col]) name = name ?? formatTitle(col, columnSummary[col].title)
|
|
99
102
|
} else {
|
|
100
103
|
try {
|
|
101
104
|
data = getCompletedData(data, x, resolvedY, series)
|
|
@@ -194,7 +197,7 @@
|
|
|
194
197
|
} else {
|
|
195
198
|
value.yAxis[0] = {...value.yAxis[0], ...chartOverrides.yAxis}
|
|
196
199
|
value.xAxis = {...value.xAxis, ...chartOverrides.xAxis}
|
|
197
|
-
if (
|
|
200
|
+
if (y2Count > 0) {
|
|
198
201
|
value.yAxis[1] = {...value.yAxis[1], show: true}
|
|
199
202
|
if (['line', 'bar', 'scatter'].includes(y2SeriesType)) {
|
|
200
203
|
for (let index = 0; index < y2Count; index++) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import Line from './Line.svelte'
|
|
4
4
|
import QueryLoad from './QueryLoad.svelte'
|
|
5
5
|
import {getThemeStores} from '../component-utilities/themeStores'
|
|
6
|
+
import {parseCommaList} from '../component-utilities/inputUtils.ts'
|
|
6
7
|
|
|
7
8
|
const {resolveColor, resolveColorsObject, resolveColorPalette} = getThemeStores()
|
|
8
9
|
|
|
@@ -98,10 +99,10 @@
|
|
|
98
99
|
export let xLabelWrap = undefined
|
|
99
100
|
</script>
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
<QueryLoad data={data} fields={[x, y, y2, series]} let:loaded>
|
|
102
|
+
<QueryLoad data={data} fields={{x, y: parseCommaList(y), y2: parseCommaList(y2), series}} let:loaded>
|
|
103
103
|
<Chart
|
|
104
104
|
data={loaded}
|
|
105
|
+
chartContext={{data, x, y, series}}
|
|
105
106
|
{x}
|
|
106
107
|
{y}
|
|
107
108
|
{y2}
|