@graphenedata/cli 0.0.1

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.
Files changed (123) hide show
  1. package/LICENSE.md +100 -0
  2. package/THIRD_PARTY_NOTICES.md +12 -0
  3. package/cli.ts +157 -0
  4. package/dist/cli/cli.js +43 -0
  5. package/dist/docs/data_apps/components/charts/annotations.md +673 -0
  6. package/dist/docs/data_apps/components/charts/area-chart.md +202 -0
  7. package/dist/docs/data_apps/components/charts/bar-chart.md +317 -0
  8. package/dist/docs/data_apps/components/charts/box-plot.md +190 -0
  9. package/dist/docs/data_apps/components/charts/bubble-chart.md +151 -0
  10. package/dist/docs/data_apps/components/charts/calendar-heatmap.md +112 -0
  11. package/dist/docs/data_apps/components/charts/custom-echarts.md +308 -0
  12. package/dist/docs/data_apps/components/charts/echarts-options.md +217 -0
  13. package/dist/docs/data_apps/components/charts/funnel-chart.md +106 -0
  14. package/dist/docs/data_apps/components/charts/heatmap.md +180 -0
  15. package/dist/docs/data_apps/components/charts/histogram.md +107 -0
  16. package/dist/docs/data_apps/components/charts/line-chart.md +265 -0
  17. package/dist/docs/data_apps/components/charts/mixed-type-charts.md +240 -0
  18. package/dist/docs/data_apps/components/charts/sankey-diagram.md +301 -0
  19. package/dist/docs/data_apps/components/charts/scatter-plot.md +134 -0
  20. package/dist/docs/data_apps/components/charts/sparkline.md +68 -0
  21. package/dist/docs/data_apps/components/data/big-value.md +153 -0
  22. package/dist/docs/data_apps/components/data/delta.md +89 -0
  23. package/dist/docs/data_apps/components/data/table.md +470 -0
  24. package/dist/docs/data_apps/components/data/value.md +97 -0
  25. package/dist/docs/data_apps/components/inputs/button-group.md +154 -0
  26. package/dist/docs/data_apps/components/inputs/checkbox.md +52 -0
  27. package/dist/docs/data_apps/components/inputs/date-input.md +131 -0
  28. package/dist/docs/data_apps/components/inputs/date-range.md +124 -0
  29. package/dist/docs/data_apps/components/inputs/dimension-grid.md +67 -0
  30. package/dist/docs/data_apps/components/inputs/dropdown.md +199 -0
  31. package/dist/docs/data_apps/components/inputs/index.md +3 -0
  32. package/dist/docs/data_apps/components/inputs/slider.md +126 -0
  33. package/dist/docs/data_apps/components/inputs/text-input.md +86 -0
  34. package/dist/docs/data_apps/components/maps/area-map.md +397 -0
  35. package/dist/docs/data_apps/components/maps/base-map.md +269 -0
  36. package/dist/docs/data_apps/components/maps/bubble-map.md +361 -0
  37. package/dist/docs/data_apps/components/maps/point-map.md +326 -0
  38. package/dist/docs/data_apps/components/maps/us-map.md +167 -0
  39. package/dist/docs/data_apps/components/ui/accordion.md +116 -0
  40. package/dist/docs/data_apps/components/ui/alert.md +37 -0
  41. package/dist/docs/data_apps/components/ui/big-link.md +19 -0
  42. package/dist/docs/data_apps/components/ui/details.md +58 -0
  43. package/dist/docs/data_apps/components/ui/download-data.md +41 -0
  44. package/dist/docs/data_apps/components/ui/embed.md +47 -0
  45. package/dist/docs/data_apps/components/ui/grid.md +45 -0
  46. package/dist/docs/data_apps/components/ui/image.md +61 -0
  47. package/dist/docs/data_apps/components/ui/info.md +47 -0
  48. package/dist/docs/data_apps/components/ui/last-refreshed.md +28 -0
  49. package/dist/docs/data_apps/components/ui/link-button.md +20 -0
  50. package/dist/docs/data_apps/components/ui/link.md +40 -0
  51. package/dist/docs/data_apps/components/ui/modal.md +57 -0
  52. package/dist/docs/data_apps/components/ui/note.md +32 -0
  53. package/dist/docs/data_apps/components/ui/print-format-components.md +85 -0
  54. package/dist/docs/data_apps/components/ui/tabs.md +122 -0
  55. package/dist/docs/graphene.md +129 -0
  56. package/dist/ui/app.css +332 -0
  57. package/dist/ui/assets/favicon.ico +0 -0
  58. package/dist/ui/component-utilities/autoFormatting.js +301 -0
  59. package/dist/ui/component-utilities/builtInFormats.js +482 -0
  60. package/dist/ui/component-utilities/chartContext.js +12 -0
  61. package/dist/ui/component-utilities/chartWindowDebug.js +21 -0
  62. package/dist/ui/component-utilities/checkInputs.js +95 -0
  63. package/dist/ui/component-utilities/convert.js +15 -0
  64. package/dist/ui/component-utilities/dateParsing.js +57 -0
  65. package/dist/ui/component-utilities/dropdownContext.ts +1 -0
  66. package/dist/ui/component-utilities/echarts.js +262 -0
  67. package/dist/ui/component-utilities/echartsThemes.js +453 -0
  68. package/dist/ui/component-utilities/formatTitle.js +24 -0
  69. package/dist/ui/component-utilities/formatting.js +258 -0
  70. package/dist/ui/component-utilities/getColumnExtents.js +79 -0
  71. package/dist/ui/component-utilities/getColumnSummary.js +67 -0
  72. package/dist/ui/component-utilities/getCompletedData.js +114 -0
  73. package/dist/ui/component-utilities/getDistinctCount.js +7 -0
  74. package/dist/ui/component-utilities/getDistinctValues.js +15 -0
  75. package/dist/ui/component-utilities/getSeriesConfig.js +236 -0
  76. package/dist/ui/component-utilities/getSortedData.js +7 -0
  77. package/dist/ui/component-utilities/getStackPercentages.js +43 -0
  78. package/dist/ui/component-utilities/getStackedData.js +17 -0
  79. package/dist/ui/component-utilities/getYAxisIndex.js +15 -0
  80. package/dist/ui/component-utilities/globalContexts.js +1 -0
  81. package/dist/ui/component-utilities/helpers/getCompletedData.helpers.js +119 -0
  82. package/dist/ui/component-utilities/inputUtils.ts +25 -0
  83. package/dist/ui/component-utilities/replaceNulls.js +14 -0
  84. package/dist/ui/component-utilities/tableUtils.ts +120 -0
  85. package/dist/ui/component-utilities/themeStores.ts +116 -0
  86. package/dist/ui/components/Area.svelte +174 -0
  87. package/dist/ui/components/AreaChart.svelte +150 -0
  88. package/dist/ui/components/Bar.svelte +326 -0
  89. package/dist/ui/components/BarChart.svelte +194 -0
  90. package/dist/ui/components/BigValue.svelte +69 -0
  91. package/dist/ui/components/Chart.svelte +1070 -0
  92. package/dist/ui/components/Column.svelte +172 -0
  93. package/dist/ui/components/DateRange.svelte +324 -0
  94. package/dist/ui/components/Dropdown.svelte +738 -0
  95. package/dist/ui/components/DropdownOption.svelte +21 -0
  96. package/dist/ui/components/ECharts.svelte +77 -0
  97. package/dist/ui/components/ErrorChart.svelte +54 -0
  98. package/dist/ui/components/GrapheneQuery.svelte +12 -0
  99. package/dist/ui/components/InlineDelta.svelte +150 -0
  100. package/dist/ui/components/Line.svelte +210 -0
  101. package/dist/ui/components/LineChart.svelte +178 -0
  102. package/dist/ui/components/PieChart.svelte +36 -0
  103. package/dist/ui/components/QueryLoad.svelte +82 -0
  104. package/dist/ui/components/Row.svelte +14 -0
  105. package/dist/ui/components/SortIcon.svelte +32 -0
  106. package/dist/ui/components/Table.svelte +19 -0
  107. package/dist/ui/components/TableCell.svelte +75 -0
  108. package/dist/ui/components/TableGroupRow.svelte +136 -0
  109. package/dist/ui/components/TableGroupToggle.svelte +42 -0
  110. package/dist/ui/components/TableHeader.svelte +242 -0
  111. package/dist/ui/components/TableRow.svelte +283 -0
  112. package/dist/ui/components/TableSubtotalRow.svelte +62 -0
  113. package/dist/ui/components/TableTotalRow.svelte +88 -0
  114. package/dist/ui/components/TextInput.svelte +92 -0
  115. package/dist/ui/components/_Table.svelte +516 -0
  116. package/dist/ui/internal/clientCache.ts +43 -0
  117. package/dist/ui/internal/queryEngine.ts +169 -0
  118. package/dist/ui/internal/telemetry.ts +28 -0
  119. package/dist/ui/internal/theme.ts +88 -0
  120. package/dist/ui/layout.svelte +3 -0
  121. package/dist/ui/playwright.config.ts +30 -0
  122. package/dist/ui/web.js +106 -0
  123. package/package.json +71 -0
@@ -0,0 +1,1070 @@
1
+ <script>
2
+ import {writable} from 'svelte/store'
3
+ import {setContext} from 'svelte'
4
+ import {propKey, configKey} from '../component-utilities/chartContext.js'
5
+ let props = writable({})
6
+ /** @type {import("svelte/store").Writable<import("echarts").Options>} */
7
+ let config = writable({})
8
+
9
+ setContext(propKey, props)
10
+ setContext(configKey, config)
11
+
12
+ import ECharts from './ECharts.svelte'
13
+ import getColumnSummary from '../component-utilities/getColumnSummary.js'
14
+ import getDistinctValues from '../component-utilities/getDistinctValues.js'
15
+ import getDistinctCount from '../component-utilities/getDistinctCount.js'
16
+ import getStackPercentages from '../component-utilities/getStackPercentages.js'
17
+ import getSortedData from '../component-utilities/getSortedData.js'
18
+ import getYAxisIndex from '../component-utilities/getYAxisIndex.js'
19
+ import {standardizeDateColumn} from '../component-utilities/dateParsing.js'
20
+ import {formatAxisValue, formatValue, getFormatObjectFromString} from '../component-utilities/formatting.js'
21
+ import formatTitle from '../component-utilities/formatTitle.js'
22
+ import ErrorChart from './ErrorChart.svelte'
23
+ import checkInputs from '../component-utilities/checkInputs.js'
24
+ import {getThemeStores} from '../component-utilities/themeStores'
25
+ import {toBoolean} from '../component-utilities/convert'
26
+
27
+ const {theme, resolveColor, resolveColorsObject, resolveColorPalette} = getThemeStores()
28
+
29
+ // ---------------------------------------------------------------------------------------
30
+ // Input Props
31
+ // ---------------------------------------------------------------------------------------
32
+ // Data and columns:
33
+ export let data = undefined
34
+ export let queryID = undefined
35
+ export let x = undefined
36
+ export let y = undefined
37
+ export let y2 = undefined
38
+ export let series = undefined
39
+ export let size = undefined
40
+ export let tooltipTitle = undefined
41
+
42
+ export let showAllXAxisLabels = undefined
43
+
44
+ // This should be reworked to fit better with svelte's reactivity.
45
+
46
+ // We rewrite the x and y values with fallbacks if they aren't present
47
+ // the fallback logic *depends* on the values of x and y
48
+ // when x and y are replaced by the fallbacks, the fallback logic doesn't reset.
49
+ // if the y value isn't set, var y gets populated with a fall back from the data.
50
+ // if the data changes, we are now acting as if the fallback from above was entered by the user, and
51
+ // then we throw if the fallback column is now missing.
52
+
53
+ // This is a hack to get around the above
54
+ // Reactively updated below to prevent circular reactivity
55
+ let ySet = y ? true : false
56
+ // const y2Set = y2 ? true : false;
57
+ let xSet = x ? true : false
58
+
59
+ export let swapXY = false // Flipped axis chart
60
+ $: swapXY = toBoolean(swapXY)
61
+
62
+ // Chart titles:
63
+ export let title = undefined
64
+ export let subtitle = undefined
65
+
66
+ // Chart type:
67
+ export let chartType = 'Chart' // Used to label chart error messages
68
+ export let bubble = false
69
+ export let hist = false
70
+ export let boxplot = false
71
+ let reqCols
72
+
73
+ // X axis:
74
+ export let xType = undefined // category or value
75
+ export let xAxisTitle = 'false' // Default false. If true, use formatTitle(x). Or you can supply a custom string
76
+ export let xBaseline = true
77
+ $: xBaseline = toBoolean(xBaseline)
78
+ export let xTickMarks = false
79
+ $: xTickMarks = toBoolean(xTickMarks)
80
+ export let xGridlines = false
81
+ $: xGridlines = toBoolean(xGridlines)
82
+ export let xAxisLabels = true
83
+ $: xAxisLabels = toBoolean(xAxisLabels)
84
+ export let sort = true // sorts x values in case x is out of order in dataset (e.g., would create line chart that is out of order)
85
+ $: sort = toBoolean(sort)
86
+ export let xFmt = undefined
87
+ export let xMin = undefined
88
+ export let xMax = undefined
89
+
90
+ // Y axis:
91
+ export let yLog = false
92
+ $: yLog = toBoolean(yLog)
93
+ export let yType = yLog === true ? 'log' : 'value' // value or log
94
+ export let yLogBase = 10
95
+ export let yAxisTitle = 'false' // Default false. If true, use formatTitle(x). Or you can supply a custom string
96
+ export let yBaseline = false
97
+ $: yBaseline = toBoolean(yBaseline)
98
+
99
+ export let yTickMarks = false
100
+ $: yTickMarks = toBoolean(yTickMarks)
101
+ export let yGridlines = true
102
+ $: yGridlines = toBoolean(yGridlines)
103
+ export let yAxisLabels = true
104
+ $: yAxisLabels = toBoolean(yAxisLabels)
105
+
106
+ export let yMin = undefined
107
+ export let yMax = undefined
108
+ export let yScale = false
109
+ $: yScale = toBoolean(yScale)
110
+ export let yFmt = undefined
111
+
112
+ export let yAxisColor = 'true'
113
+ $: yAxisColorStore = resolveColor(yAxisColor)
114
+
115
+ // Y2 axis:
116
+ export let y2AxisTitle = 'false' // Default false. If true, use formatTitle(x). Or you can supply a custom string
117
+ export let y2Baseline = false
118
+ $: y2Baseline = toBoolean(y2Baseline)
119
+ export let y2TickMarks = false
120
+ $: y2TickMarks = toBoolean(y2TickMarks)
121
+ export let y2Gridlines = true
122
+ $: y2Gridlines = toBoolean(y2Gridlines)
123
+ export let y2AxisLabels = true
124
+ $: y2AxisLabels = toBoolean(y2AxisLabels)
125
+ export let y2Min = undefined
126
+ export let y2Max = undefined
127
+ export let y2Scale = false
128
+ $: y2Scale = toBoolean(y2Scale)
129
+ export let y2Fmt = undefined
130
+
131
+ export let y2AxisColor = 'true'
132
+ $: y2AxisColorStore = resolveColor(y2AxisColor)
133
+
134
+ // Other column formats:
135
+ export let sizeFmt = undefined
136
+
137
+ // Color palette:
138
+ export let colorPalette = 'default'
139
+ $: colorPaletteStore = resolveColorPalette(colorPalette)
140
+
141
+ // Legend:
142
+ export let legend = undefined
143
+
144
+ // Additional Config Options:
145
+ export let echartsOptions = undefined // additional ECharts config object that will append to the config generated by our API
146
+ export let seriesOptions = undefined // additional ECharts config object that will be applied to every series in the config
147
+
148
+ export let seriesColors = undefined
149
+ $: seriesColorsStore = resolveColorsObject(seriesColors)
150
+
151
+ export let stackType = undefined // used in BarChart (stacked, grouped) and AreaChart (stacked)
152
+ export let stacked100 = false
153
+
154
+ export let chartAreaHeight = undefined
155
+
156
+ export let connectGroup = undefined // string represent name of group for connected charts. Charts with same connectGroup will have connected interactions
157
+
158
+ export let leftPadding = undefined
159
+ export let rightPadding = undefined
160
+
161
+ export let xLabelWrap = false // false = truncate, true = wrap
162
+ $: xLabelWrap = toBoolean(xLabelWrap)
163
+
164
+ const xAxisLabelOverflow = xLabelWrap ? 'break' : 'truncate'
165
+
166
+ // ---------------------------------------------------------------------------------------
167
+ // Variable Declaration
168
+ // ---------------------------------------------------------------------------------------
169
+ // Column Summary:
170
+ let columnSummary
171
+ let columnNames
172
+ let uColNames = []
173
+ let unusedColumns = []
174
+ let uColType
175
+ let uColName
176
+ let xDataType
177
+ let xMismatch
178
+ let xFormat
179
+ let yFormat
180
+ let y2Format
181
+ let sizeFormat
182
+ let xUnitSummary
183
+ let yUnitSummary
184
+ let y2UnitSummary
185
+ let xDistinct
186
+
187
+ // Individual Config Sections:
188
+ let horizAxisConfig
189
+ let verticalAxisConfig
190
+ let horizAxisTitleConfig
191
+ let chartConfig
192
+
193
+ // Chart area sizing:
194
+ let hasTitle
195
+ let hasSubtitle
196
+ let hasLegend
197
+ let hasTopAxisTitle
198
+ let hasBottomAxisTitle
199
+ let titleFontSize
200
+ let subtitleFontSize
201
+ let titleBoxPadding
202
+ let titleBoxHeight
203
+ let chartAreaPaddingTop
204
+ let chartAreaPaddingBottom
205
+ let bottomAxisTitleSize
206
+ let topAxisTitleSize
207
+ let legendHeight
208
+ let legendPaddingTop
209
+ let legendTop
210
+ let chartTop
211
+ let chartBottom
212
+ let chartContainerHeight
213
+ let topAxisTitleTop
214
+
215
+ let horizAxisTitle
216
+
217
+ // Adjustment to avoid small bars on horizontal bar chart (extend chart height to accomodate):
218
+ let maxBars
219
+ let barCount
220
+ let heightMultiplier
221
+
222
+ // Set final chart height:
223
+ let height
224
+ let width
225
+
226
+ let missingCols = []
227
+
228
+ let originalRun = true
229
+
230
+ // Error Handling:
231
+
232
+ let inputCols = []
233
+ let optCols = []
234
+ let i
235
+
236
+ let error
237
+
238
+ // Date String Handling:
239
+ let columnSummaryArray
240
+ let dateCols
241
+
242
+ $: {
243
+ try {
244
+ error = undefined
245
+ missingCols = []
246
+ unusedColumns = []
247
+ // Error Handling:
248
+ inputCols = []
249
+ optCols = []
250
+ uColName = []
251
+ ySet = y ? true : false
252
+ xSet = x ? true : false
253
+
254
+ checkInputs(data) // check that dataset exists
255
+
256
+ // ---------------------------------------------------------------------------------------
257
+ // Get column information
258
+ // ---------------------------------------------------------------------------------------
259
+ // Get column summary:
260
+ columnSummary = getColumnSummary(data)
261
+
262
+ // Get column names:
263
+ columnNames = Object.keys(columnSummary)
264
+
265
+ // ---------------------------------------------------------------------------------------
266
+ // Make assumptions to complete required props
267
+ // ---------------------------------------------------------------------------------------
268
+ // If no x column was supplied, assume first column in dataset is x
269
+ if (!xSet) {
270
+ x = columnNames[0]
271
+ }
272
+
273
+ // If no y column(s) supplied, assume all number columns other than x are the y columns:
274
+ if (!ySet) {
275
+ uColNames = columnNames.filter(function (col) {
276
+ return ![x, series, size].includes(col)
277
+ })
278
+
279
+ for (let i = 0; i < uColNames.length; i++) {
280
+ uColName = uColNames[i]
281
+ uColType = columnSummary[uColName].type
282
+ if (uColType === 'number') {
283
+ unusedColumns.push(uColName)
284
+ }
285
+ }
286
+
287
+ y = unusedColumns.length > 1 ? unusedColumns : unusedColumns[0]
288
+ }
289
+ // Establish required columns based on chart type:
290
+ if (bubble) {
291
+ reqCols = {
292
+ x: x,
293
+ y: y,
294
+ size: size,
295
+ }
296
+ } else if (hist) {
297
+ reqCols = {
298
+ x: x,
299
+ }
300
+ } else if (boxplot) {
301
+ reqCols = {}
302
+ } else {
303
+ reqCols = {
304
+ x: x,
305
+ y: y,
306
+ }
307
+ }
308
+
309
+ // Check which columns were not supplied to the chart:
310
+ for (let property in reqCols) {
311
+ if (reqCols[property] == null) {
312
+ missingCols.push(property)
313
+ }
314
+ }
315
+
316
+ if (missingCols.length === 1) {
317
+ throw Error(new Intl.ListFormat().format(missingCols) + ' is required')
318
+ } else if (missingCols.length > 1) {
319
+ throw Error(new Intl.ListFormat().format(missingCols) + ' are required')
320
+ }
321
+
322
+ // Fix for stacked100 overwriting y variable. Bandaid fix - not a long-term solution:
323
+ if (stacked100 === true && y.includes('_pct') && originalRun === false) {
324
+ if (typeof y === 'object') {
325
+ for (let i = 0; i < y.length; i++) {
326
+ y[i] = y[i].replace('_pct', '')
327
+ }
328
+ originalRun = false
329
+ } else {
330
+ y = y.replace('_pct', '')
331
+ originalRun = false
332
+ }
333
+ }
334
+
335
+ // Check the inputs supplied to the chart:
336
+ if (x) {
337
+ inputCols.push(x)
338
+ }
339
+ if (y) {
340
+ if (typeof y === 'object') {
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
+ }
357
+ if (size) {
358
+ inputCols.push(size)
359
+ }
360
+ if (series) {
361
+ optCols.push(series)
362
+ }
363
+ if (tooltipTitle) {
364
+ optCols.push(tooltipTitle)
365
+ }
366
+
367
+ checkInputs(data, inputCols, optCols)
368
+
369
+ // ---------------------------------------------------------------------------------------
370
+ // Aggregate Data if Required
371
+ // ---------------------------------------------------------------------------------------
372
+
373
+ if (stacked100 === true) {
374
+ data = getStackPercentages(data, x, y)
375
+
376
+ if (typeof y === 'object') {
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):
387
+ columnSummary = getColumnSummary(data)
388
+ }
389
+
390
+ // ---------------------------------------------------------------------------------------
391
+ // Define x axis type
392
+ // ---------------------------------------------------------------------------------------
393
+ xDataType = columnSummary[x].type
394
+
395
+ // Get xDataType into ECharts default types:
396
+ switch (xDataType) {
397
+ case 'number':
398
+ xDataType = 'value'
399
+ break
400
+ case 'string':
401
+ xDataType = 'category'
402
+ break
403
+ case 'date':
404
+ xDataType = 'time'
405
+ break
406
+ default:
407
+ break
408
+ }
409
+
410
+ xType = xType === 'category' ? 'category' : xDataType
411
+
412
+ // Set xAxisLabel overflow behaviour based on column type
413
+ if (!showAllXAxisLabels) {
414
+ // if user has not set showXAxisLabels
415
+ showAllXAxisLabels = xType === 'category'
416
+ } else {
417
+ // if user has set showXAxisLabels, convert to boolean
418
+ showAllXAxisLabels = showAllXAxisLabels === 'true' || showAllXAxisLabels === true
419
+ }
420
+
421
+ // Throw error if attempting to plot value or time on horizontal x-axis:
422
+ if (swapXY && xType !== 'category') {
423
+ throw Error(
424
+ 'Horizontal charts do not support a value or time-based x-axis. You can either change your SQL query to output string values or set swapXY=false.',
425
+ )
426
+ }
427
+
428
+ // Throw error if attempting to plot secondary y-axis on horizontal chart:
429
+ if (swapXY && y2) {
430
+ throw Error(
431
+ 'Horizontal charts do not support a secondary y-axis. You can either set swapXY=false or remove the y2 prop from your chart.',
432
+ )
433
+ }
434
+
435
+ // Override xType if axes are swapped - only category enabled on horizontal axis
436
+ if (swapXY) {
437
+ xType = 'category'
438
+ }
439
+
440
+ // Check for x mismatch:
441
+ xMismatch = xDataType === 'value' && xType === 'category'
442
+
443
+ // ---------------------------------------------------------------------------------------
444
+ // Sort data based on xType
445
+ // ---------------------------------------------------------------------------------------
446
+ if (sort) {
447
+ let sortColumn = xDataType === 'category' ? y : x
448
+ let sortAscending = xDataType !== 'category'
449
+ data = getSortedData(data, sortColumn, sortAscending)
450
+ }
451
+
452
+ // Always sort time axes by x - this prevents the lines from being drawn out of order
453
+ if (xDataType === 'time') {
454
+ data = getSortedData(data, x, true)
455
+ }
456
+
457
+ // ---------------------------------------------------------------------------------------
458
+ // Standardize date columns
459
+ // ---------------------------------------------------------------------------------------
460
+
461
+ columnSummaryArray = getColumnSummary(data, 'array')
462
+ dateCols = columnSummaryArray.filter((d) => d.type === 'date')
463
+ dateCols = dateCols.map((d) => d.id)
464
+
465
+ if (dateCols.length > 0) {
466
+ for (let i = 0; i < dateCols.length; i++) {
467
+ data = standardizeDateColumn(data, dateCols[i])
468
+ }
469
+ }
470
+
471
+ // ---------------------------------------------------------------------------------------
472
+ // Get format codes for axes
473
+ // ---------------------------------------------------------------------------------------
474
+ if (xFmt) {
475
+ xFormat = getFormatObjectFromString(xFmt, columnSummary[x].format?.valueType)
476
+ } else {
477
+ xFormat = columnSummary[x].format
478
+ }
479
+
480
+ if (!y) {
481
+ yFormat = 'str'
482
+ } else {
483
+ if (yFmt) {
484
+ if (typeof y === 'object') {
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
+ }
496
+ }
497
+
498
+ if (y2) {
499
+ if (y2Fmt) {
500
+ if (typeof y2 === 'object') {
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
+ }
512
+ }
513
+
514
+ if (size) {
515
+ if (sizeFmt) {
516
+ sizeFormat = getFormatObjectFromString(sizeFmt, columnSummary[size].format?.valueType)
517
+ } else {
518
+ sizeFormat = columnSummary[size].format
519
+ }
520
+ }
521
+
522
+ xUnitSummary = columnSummary[x].columnUnitSummary
523
+
524
+ if (y) {
525
+ if (typeof y === 'object') {
526
+ yUnitSummary = columnSummary[y[0]].columnUnitSummary
527
+ } else {
528
+ yUnitSummary = columnSummary[y].columnUnitSummary
529
+ }
530
+ }
531
+
532
+ if (y2) {
533
+ if (typeof y2 === 'object') {
534
+ y2UnitSummary = columnSummary[y2[0]].columnUnitSummary
535
+ } else {
536
+ y2UnitSummary = columnSummary[y2].columnUnitSummary
537
+ }
538
+ }
539
+
540
+ if (xAxisTitle === 'true') {
541
+ xAxisTitle = formatTitle(x, xFormat)
542
+ } else if (xAxisTitle === 'false') {
543
+ xAxisTitle = ''
544
+ }
545
+
546
+ if (yAxisTitle === 'true') {
547
+ yAxisTitle = typeof y === 'object' ? '' : formatTitle(y, yFormat)
548
+ } else if (yAxisTitle === 'false') {
549
+ yAxisTitle = ''
550
+ }
551
+
552
+ if (y2AxisTitle === 'true') {
553
+ y2AxisTitle = typeof y2 === 'object' ? '' : formatTitle(y2, y2Format)
554
+ } else if (y2AxisTitle === 'false') {
555
+ y2AxisTitle = ''
556
+ }
557
+
558
+ // ---------------------------------------------------------------------------------------
559
+ // Get total series count
560
+ // ---------------------------------------------------------------------------------------
561
+ let yCount = typeof y === 'object' ? y.length : 1
562
+ let seriesCount = series ? getDistinctCount(data, series) : 1
563
+ let ySeriesCount = yCount * seriesCount
564
+
565
+ // y2Count may need to be adjusted to also factor in the series column. For now, we really
566
+ // only need to know that it's multi-series, so > 1 is sufficient
567
+ let y2Count = 0
568
+ if (typeof y2 === 'object') {
569
+ y2Count = y2.length
570
+ } else if (y2) {
571
+ y2Count = 1
572
+ }
573
+ let totalSeriesCount = ySeriesCount + y2Count
574
+
575
+ // ---------------------------------------------------------------------------------------
576
+ // Set legend flag
577
+ // ---------------------------------------------------------------------------------------
578
+ if (legend !== undefined) {
579
+ legend = legend === 'true' || legend === true
580
+ }
581
+
582
+ legend = legend ?? totalSeriesCount > 1
583
+
584
+ // ---------------------------------------------------------------------------------------
585
+ // Handle errors for log axes (cannot be used with stacked charts)
586
+ // ---------------------------------------------------------------------------------------
587
+
588
+ if (stacked100 === true && yLog === true) {
589
+ throw Error('Log axis cannot be used in a 100% stacked chart')
590
+ } else if (stackType === 'stacked' && totalSeriesCount > 1 && yLog === true) {
591
+ throw Error('Log axis cannot be used in a stacked chart')
592
+ }
593
+
594
+ let minYValue
595
+ if (typeof y === 'object') {
596
+ minYValue = columnSummary[y[0]].columnUnitSummary.min
597
+ for (let i = 0; i < y.length; i++) {
598
+ if (columnSummary[y[i]].columnUnitSummary.min < minYValue) {
599
+ minYValue = columnSummary[y[i]].columnUnitSummary.min
600
+ }
601
+ }
602
+ } else if (y) {
603
+ minYValue = columnSummary[y].columnUnitSummary.min
604
+ }
605
+
606
+ if (yLog === true && minYValue <= 0 && minYValue !== null) {
607
+ throw Error('Log axis cannot display values less than or equal to zero')
608
+ }
609
+
610
+ // ---------------------------------------------------------------------------------------
611
+ // Add props to store to let child components access them
612
+ // ---------------------------------------------------------------------------------------
613
+ props.update((d) => {
614
+ return {
615
+ ...d,
616
+ data,
617
+ x,
618
+ y,
619
+ y2,
620
+ series,
621
+ swapXY,
622
+ sort,
623
+ xType,
624
+ xFormat,
625
+ yFormat,
626
+ y2Format,
627
+ sizeFormat,
628
+ xMismatch,
629
+ size,
630
+ yMin,
631
+ y2Min,
632
+ columnSummary,
633
+ xAxisTitle,
634
+ yAxisTitle,
635
+ y2AxisTitle,
636
+ tooltipTitle,
637
+ chartAreaHeight,
638
+ chartType,
639
+ yCount,
640
+ y2Count,
641
+ }
642
+ })
643
+
644
+ // ---------------------------------------------------------------------------------------
645
+ // Axis Configuration
646
+ // ---------------------------------------------------------------------------------------
647
+ xDistinct = getDistinctValues(data, x)
648
+ let secondaryAxis
649
+
650
+ if (swapXY) {
651
+ horizAxisConfig = {
652
+ type: yType,
653
+ logBase: yLogBase,
654
+ position: 'top',
655
+ axisLabel: {
656
+ show: yAxisLabels,
657
+ hideOverlap: true,
658
+ showMaxLabel: true,
659
+ formatter: function (value) {
660
+ return formatAxisValue(value, yFormat, yUnitSummary)
661
+ },
662
+ margin: 4,
663
+ },
664
+ min: yMin,
665
+ max: yMax,
666
+ scale: yScale,
667
+ splitLine: {
668
+ show: yGridlines,
669
+ },
670
+ axisLine: {
671
+ show: yBaseline,
672
+ onZero: false,
673
+ },
674
+ axisTick: {
675
+ show: yTickMarks,
676
+ },
677
+ boundaryGap: false,
678
+ z: 2,
679
+ }
680
+ } else {
681
+ horizAxisConfig = {
682
+ type: xType,
683
+ min: xMin,
684
+ max: xMax,
685
+ tooltip: {
686
+ show: true,
687
+ position: 'inside',
688
+ formatter (p) {
689
+ if (p.isTruncated()) {
690
+ return p.name
691
+ }
692
+ },
693
+ },
694
+ splitLine: {
695
+ show: xGridlines,
696
+ },
697
+ axisLine: {
698
+ show: xBaseline,
699
+ },
700
+ axisTick: {
701
+ show: xTickMarks,
702
+ },
703
+ axisLabel: {
704
+ show: xAxisLabels,
705
+ hideOverlap: true,
706
+ showMaxLabel: xType === 'category' || xType === 'value', // max label for ECharts' time axis is a stub - default for that is false
707
+ formatter:
708
+ xType === 'time' || xType === 'category'
709
+ ? false
710
+ : function (value) {
711
+ return formatAxisValue(value, xFormat, xUnitSummary)
712
+ },
713
+ margin: 6,
714
+ },
715
+ scale: true,
716
+ z: 2,
717
+ }
718
+ }
719
+
720
+ if (swapXY) {
721
+ verticalAxisConfig = {
722
+ type: xType,
723
+ inverse: 'true',
724
+ splitLine: {
725
+ show: xGridlines,
726
+ },
727
+ axisLine: {
728
+ show: xBaseline,
729
+ },
730
+ axisTick: {
731
+ show: xTickMarks,
732
+ },
733
+ axisLabel: {
734
+ show: xAxisLabels,
735
+ hideOverlap: true,
736
+ // formatter:
737
+ // function(value){
738
+ // return formatAxisValue(value, xFormat, xUnitSummary)
739
+ // },
740
+ },
741
+ scale: true,
742
+ min: xMin,
743
+ max: xMax,
744
+ z: 2,
745
+ }
746
+ } else {
747
+ let primaryAxisColor = (() => {
748
+ if (!y2) return undefined
749
+ if ($yAxisColorStore === 'true') return $colorPaletteStore[0]
750
+ if ($yAxisColorStore === 'false') return undefined
751
+ return $yAxisColorStore
752
+ })()
753
+ let secondaryAxisColor = (() => {
754
+ if ($y2AxisColorStore === 'true') return $colorPaletteStore[ySeriesCount]
755
+ if ($y2AxisColorStore === 'false') return undefined
756
+ return $y2AxisColorStore
757
+ })()
758
+
759
+ verticalAxisConfig = {
760
+ type: yType,
761
+ logBase: yLogBase,
762
+ splitLine: {
763
+ show: yGridlines,
764
+ },
765
+ axisLine: {
766
+ show: yBaseline,
767
+ onZero: false,
768
+ },
769
+ axisTick: {
770
+ show: yTickMarks,
771
+ },
772
+ axisLabel: {
773
+ show: yAxisLabels,
774
+ hideOverlap: true,
775
+ margin: 4,
776
+ formatter: function (value) {
777
+ return formatAxisValue(value, yFormat, yUnitSummary)
778
+ },
779
+ color: primaryAxisColor,
780
+ },
781
+ name: yAxisTitle,
782
+ nameLocation: 'end',
783
+ nameTextStyle: {
784
+ align: 'left',
785
+ verticalAlign: 'top',
786
+ padding: [0, 5, 0, 0],
787
+ color: primaryAxisColor,
788
+ },
789
+ nameGap: 6,
790
+ min: yMin,
791
+ max: yMax,
792
+ scale: yScale,
793
+ boundaryGap: ['0%', '1%'],
794
+ z: 2,
795
+ }
796
+
797
+ secondaryAxis = {
798
+ type: 'value',
799
+ show: false,
800
+ alignTicks: true,
801
+ splitLine: {
802
+ show: y2Gridlines,
803
+ },
804
+ axisLine: {
805
+ show: y2Baseline,
806
+ onZero: false,
807
+ },
808
+ axisTick: {
809
+ show: y2TickMarks,
810
+ },
811
+ axisLabel: {
812
+ show: y2AxisLabels,
813
+ hideOverlap: true,
814
+ margin: 4,
815
+ formatter: function (value) {
816
+ return formatAxisValue(value, y2Format, y2UnitSummary)
817
+ },
818
+ color: secondaryAxisColor,
819
+ },
820
+ name: y2AxisTitle,
821
+ nameLocation: 'end',
822
+ nameTextStyle: {
823
+ align: 'right',
824
+ verticalAlign: 'top',
825
+ padding: [0, 0, 0, 5],
826
+ color: secondaryAxisColor,
827
+ },
828
+ nameGap: 6,
829
+ min: y2Min,
830
+ max: y2Max,
831
+ scale: y2Scale,
832
+ boundaryGap: ['0%', '1%'],
833
+ z: 2,
834
+ }
835
+
836
+ verticalAxisConfig = [verticalAxisConfig, secondaryAxis]
837
+ }
838
+
839
+ // ---------------------------------------------------------------------------------------
840
+ // Set up chart area
841
+ // ---------------------------------------------------------------------------------------
842
+
843
+ if (chartAreaHeight) {
844
+ // if chartAreaHeight was user-supplied
845
+ chartAreaHeight = Number(chartAreaHeight)
846
+ if (isNaN(chartAreaHeight)) {
847
+ // input must be a number
848
+ throw Error('chartAreaHeight must be a number')
849
+ } else if (chartAreaHeight <= 0) {
850
+ throw Error('chartAreaHeight must be a positive number')
851
+ }
852
+ } else {
853
+ chartAreaHeight = 220
854
+ }
855
+
856
+ hasTitle = title ? true : false
857
+ hasSubtitle = subtitle ? true : false
858
+ hasLegend = legend * (series !== null || (typeof y === 'object' && y.length > 1))
859
+ hasTopAxisTitle = yAxisTitle !== '' && swapXY
860
+ hasBottomAxisTitle = xAxisTitle !== '' && !swapXY
861
+
862
+ titleFontSize = 15
863
+ subtitleFontSize = 13
864
+ titleBoxPadding = 6 * hasSubtitle
865
+
866
+ titleBoxHeight =
867
+ hasTitle * titleFontSize +
868
+ hasSubtitle * subtitleFontSize +
869
+ titleBoxPadding * Math.max(hasTitle, hasSubtitle)
870
+
871
+ chartAreaPaddingTop = 10
872
+ chartAreaPaddingBottom = 10
873
+
874
+ bottomAxisTitleSize = 14
875
+ topAxisTitleSize = 14 + 0 // font size + padding top
876
+
877
+ legendHeight = 15
878
+ legendHeight = legendHeight * hasLegend
879
+
880
+ legendPaddingTop = 7
881
+ legendPaddingTop = legendPaddingTop * Math.max(hasTitle, hasSubtitle)
882
+
883
+ legendTop = titleBoxHeight + legendPaddingTop
884
+ chartTop =
885
+ legendTop + legendHeight + topAxisTitleSize * hasTopAxisTitle + chartAreaPaddingTop
886
+ chartBottom = hasBottomAxisTitle * bottomAxisTitleSize + chartAreaPaddingBottom
887
+
888
+ // Adjustment to avoid small bars on horizontal bar chart (extend chart height to accomodate)
889
+ // Small bars are allowed on normal bar chart (e.g., time series bar chart)
890
+ maxBars = 8
891
+ heightMultiplier = 1
892
+ if (swapXY) {
893
+ barCount = xDistinct.length
894
+ heightMultiplier = Math.max(1, barCount / maxBars)
895
+ }
896
+
897
+ chartContainerHeight = chartAreaHeight * heightMultiplier + chartTop + chartBottom
898
+
899
+ topAxisTitleTop = legendTop + legendHeight + 7
900
+
901
+ // Set final chart height:
902
+ height = chartContainerHeight + 'px'
903
+ width = '100%'
904
+
905
+ // ---------------------------------------------------------------------------------------
906
+ // Set up horizontal axis title (custom graphic)
907
+ // ---------------------------------------------------------------------------------------
908
+ horizAxisTitle = swapXY ? yAxisTitle : xAxisTitle
909
+ if (horizAxisTitle !== '') {
910
+ horizAxisTitle = horizAxisTitle + ' →' // u2192 is js escaped version of &rarr;
911
+ }
912
+
913
+ horizAxisTitleConfig = {
914
+ id: 'horiz-axis-title',
915
+ type: 'text',
916
+ style: {
917
+ text: horizAxisTitle,
918
+ textAlign: 'right',
919
+ fill: $theme.colors['base-content-muted'],
920
+ },
921
+ cursor: 'auto',
922
+ // Positioning (if swapXY, top right; otherwise bottom right)
923
+ right: swapXY ? '2%' : '3%',
924
+ top: swapXY ? topAxisTitleTop : null,
925
+ bottom: swapXY ? null : '2%',
926
+ }
927
+
928
+ // ---------------------------------------------------------------------------------------
929
+ // Build chart config and update config store so child components can access it
930
+ // ---------------------------------------------------------------------------------------
931
+
932
+ chartConfig = {
933
+ title: {
934
+ text: title,
935
+ subtext: subtitle,
936
+ subtextStyle: {
937
+ width: width,
938
+ },
939
+ },
940
+ tooltip: {
941
+ trigger: 'axis',
942
+ show: true,
943
+ // formatter function is overridden in ScatterPlot, BubbleChart, and Histogram
944
+ formatter: function (params) {
945
+ let output
946
+ let xVal
947
+ let yVal
948
+ let yCol
949
+ if (totalSeriesCount > 1) {
950
+ // If multi-series, add series name as title of tooltip
951
+ xVal = params[0].value[swapXY ? 1 : 0]
952
+ output = `<span id="tooltip" style='font-weight: 600;'>${formatValue(
953
+ xVal,
954
+ xFormat,
955
+ )}</span>`
956
+ for (let i = params.length - 1; i >= 0; i--) {
957
+ if (params[i].seriesName !== 'stackTotal') {
958
+ yVal = params[i].value[swapXY ? 0 : 1]
959
+ output =
960
+ output +
961
+ `<br> <span style='font-size: 11px;'>${params[i].marker} ${
962
+ params[i].seriesName
963
+ }<span/><span style='float:right; margin-left: 10px; font-size: 12px;'>${formatValue(
964
+ yVal,
965
+ // Not sure if this will work. Need to check with multi series on both axes
966
+ // Check if echarts does the order in the same way - y first, then y2
967
+ getYAxisIndex(params[i].componentIndex, yCount, y2Count) === 0
968
+ ? yFormat
969
+ : y2Format,
970
+ )}</span>`
971
+ }
972
+ }
973
+ } else if (xType === 'value') {
974
+ // If single-series and a numerical x-axis, include x column as a normal column rather than title (so as not to show a number as the title)
975
+ xVal = params[0].value[swapXY ? 1 : 0]
976
+ yVal = params[0].value[swapXY ? 0 : 1]
977
+ yCol = params[0].seriesName
978
+ output = `<span id="tooltip" style='font-weight: 600;'>${formatTitle(
979
+ x,
980
+ xFormat,
981
+ )}: </span><span style='float:right; margin-left: 10px;'>${formatValue(
982
+ xVal,
983
+ xFormat,
984
+ )}</span><br/><span style='font-weight: 600;'>${formatTitle(
985
+ yCol,
986
+ yFormat,
987
+ )}: </span><span style='float:right; margin-left: 10px;'>${formatValue(
988
+ yVal,
989
+ yFormat,
990
+ )}</span>`
991
+ } else {
992
+ // If single series and categorical or date x-axis, use x value as title of tooltip
993
+ xVal = params[0].value[swapXY ? 1 : 0]
994
+ yVal = params[0].value[swapXY ? 0 : 1]
995
+ yCol = params[0].seriesName
996
+ output = `<span id="tooltip" style='font-weight: 600;'>${formatValue(
997
+ xVal,
998
+ xFormat,
999
+ )}</span><br/><span>${formatTitle(
1000
+ yCol,
1001
+ yFormat,
1002
+ )}: </span><span style='float:right; margin-left: 10px;'>${formatValue(
1003
+ yVal,
1004
+ yFormat,
1005
+ )}</span>`
1006
+ }
1007
+ return output
1008
+ },
1009
+ confine: true,
1010
+ axisPointer: {
1011
+ // Use axis to trigger tooltip
1012
+ type: 'shadow', // 'shadow' as default; can also be 'line' or 'shadow'
1013
+ },
1014
+ extraCssText:
1015
+ 'box-shadow: 0 3px 6px rgba(0,0,0,.15); box-shadow: 0 2px 4px rgba(0,0,0,.12); z-index: 1; font-feature-settings: "cv02", "tnum";',
1016
+ order: 'valueDesc',
1017
+ },
1018
+ legend: {
1019
+ show: legend,
1020
+ type: 'scroll',
1021
+ top: legendTop,
1022
+ padding: [0, 0, 0, 0],
1023
+ data: [],
1024
+ },
1025
+ grid: {
1026
+ left: leftPadding ?? (swapXY ? '1%' : '0.8%'),
1027
+ right: rightPadding ?? (swapXY ? '4%' : '3%'),
1028
+ bottom: chartBottom,
1029
+ top: chartTop,
1030
+ containLabel: true,
1031
+ },
1032
+ xAxis: horizAxisConfig,
1033
+ yAxis: verticalAxisConfig,
1034
+ series: [],
1035
+ animation: true,
1036
+ graphic: horizAxisTitleConfig,
1037
+ color: $colorPaletteStore,
1038
+ }
1039
+
1040
+ config.update(() => {
1041
+ return chartConfig
1042
+ })
1043
+ } catch (e) {
1044
+ error = e.message
1045
+ let setTextRed = '\x1b[31m%s\x1b[0m'
1046
+ console.error(setTextRed, `Error in ${chartType}: ${e.message}`)
1047
+ props.update((d) => {
1048
+ return {...d, error}
1049
+ })
1050
+ }
1051
+ }
1052
+ </script>
1053
+
1054
+ {#if !error}
1055
+ <slot />
1056
+ <ECharts
1057
+ config={$config}
1058
+ {height}
1059
+ {width}
1060
+ {data}
1061
+ {queryID}
1062
+ {echartsOptions}
1063
+ {seriesOptions}
1064
+ {connectGroup}
1065
+ {xAxisLabelOverflow}
1066
+ seriesColors={seriesColorsStore}
1067
+ />
1068
+ {:else}
1069
+ <ErrorChart {error} title={chartType} />
1070
+ {/if}