@graphenedata/cli 0.0.5 → 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.
@@ -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,10 +115,10 @@
114
115
  export let xLabelWrap = undefined
115
116
  </script>
116
117
 
117
- <QueryLoad data={data} fields={{x, y, y2, series}} let:loaded>
118
+ <QueryLoad data={data} fields={{x, y: parseCommaList(y), y2: parseCommaList(y2), series}} let:loaded>
118
119
  <Chart
119
120
  data={loaded}
120
- chartContext={{data, x, y, y2, series}}
121
+ chartContext={{data, x, y, series}}
121
122
  {x}
122
123
  {y}
123
124
  {y2}
@@ -23,6 +23,7 @@
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'
26
27
  import {logError} from '../internal/telemetry.ts'
27
28
 
28
29
  const {theme, resolveColor, resolveColorsObject, resolveColorPalette} = getThemeStores()
@@ -250,7 +251,10 @@
250
251
  inputCols = []
251
252
  optCols = []
252
253
  uColName = []
253
- ySet = y ? true : false
254
+ // Normalize list-like inputs first
255
+ y = parseCommaList(y)
256
+ y2 = parseCommaList(y2)
257
+ ySet = y.length > 0
254
258
  xSet = x ? true : false
255
259
 
256
260
  checkInputs(data) // check that dataset exists
@@ -286,7 +290,7 @@
286
290
  }
287
291
  }
288
292
 
289
- y = unusedColumns.length > 1 ? unusedColumns : unusedColumns[0]
293
+ y = unusedColumns // always array; empty handled by required prop checks
290
294
  }
291
295
  // Establish required columns based on chart type:
292
296
  if (bubble) {
@@ -322,40 +326,17 @@
322
326
  }
323
327
 
324
328
  // Fix for stacked100 overwriting y variable. Bandaid fix - not a long-term solution:
325
- if (stacked100 === true && y.includes('_pct') && originalRun === false) {
326
- if (typeof y === 'object') {
327
- for (let i = 0; i < y.length; i++) {
328
- y[i] = y[i].replace('_pct', '')
329
- }
330
- originalRun = false
331
- } else {
332
- y = y.replace('_pct', '')
333
- originalRun = false
334
- }
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
335
332
  }
336
333
 
337
334
  // Check the inputs supplied to the chart:
338
335
  if (x) {
339
336
  inputCols.push(x)
340
337
  }
341
- if (y) {
342
- if (typeof y === 'object') {
343
- for (i = 0; i < y.length; i++) {
344
- inputCols.push(y[i])
345
- }
346
- } else {
347
- inputCols.push(y)
348
- }
349
- }
350
- if (y2) {
351
- if (typeof y2 === 'object') {
352
- for (i = 0; i < y2.length; i++) {
353
- inputCols.push(y2[i])
354
- }
355
- } else {
356
- inputCols.push(y2)
357
- }
358
- }
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])
359
340
  if (size) {
360
341
  inputCols.push(size)
361
342
  }
@@ -374,18 +355,8 @@
374
355
 
375
356
  if (stacked100 === true) {
376
357
  data = getStackPercentages(data, x, y)
377
-
378
- if (typeof y === 'object') {
379
- for (let i = 0; i < y.length; i++) {
380
- y[i] = y[i] + '_pct'
381
- }
382
- originalRun = false
383
- } else {
384
- y = y + '_pct'
385
- originalRun = false
386
- }
387
-
388
- // 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
389
360
  columnSummary = getColumnSummary(data)
390
361
  }
391
362
 
@@ -428,7 +399,7 @@
428
399
  }
429
400
 
430
401
  // Throw error if attempting to plot secondary y-axis on horizontal chart:
431
- if (swapXY && y2) {
402
+ if (swapXY && y2.length) {
432
403
  throw Error(
433
404
  'Horizontal charts do not support a secondary y-axis. You can either set swapXY=false or remove the y2 prop from your chart.',
434
405
  )
@@ -446,7 +417,10 @@
446
417
  // Sort data based on xType
447
418
  // ---------------------------------------------------------------------------------------
448
419
  if (sort) {
449
- let sortColumn = xDataType === 'category' ? y : x
420
+ let sortColumn = x
421
+ if (xDataType === 'category') {
422
+ sortColumn = Array.isArray(y) ? (y[0] ?? x) : x
423
+ }
450
424
  let sortAscending = xDataType !== 'category'
451
425
  data = getSortedData(data, sortColumn, sortAscending)
452
426
  }
@@ -479,38 +453,16 @@
479
453
  xFormat = columnSummary[x].format
480
454
  }
481
455
 
482
- if (!y) {
456
+ if (y.length === 0) {
483
457
  yFormat = 'str'
484
458
  } else {
485
- if (yFmt) {
486
- if (typeof y === 'object') {
487
- yFormat = getFormatObjectFromString(yFmt, columnSummary[y[0]].format?.valueType)
488
- } else {
489
- yFormat = getFormatObjectFromString(yFmt, columnSummary[y].format?.valueType)
490
- }
491
- } else {
492
- if (typeof y === 'object') {
493
- yFormat = columnSummary[y[0]].format
494
- } else {
495
- yFormat = columnSummary[y].format
496
- }
497
- }
459
+ if (yFmt) yFormat = getFormatObjectFromString(yFmt, columnSummary[y[0]].format?.valueType)
460
+ else yFormat = columnSummary[y[0]].format
498
461
  }
499
462
 
500
- if (y2) {
501
- if (y2Fmt) {
502
- if (typeof y2 === 'object') {
503
- y2Format = getFormatObjectFromString(y2Fmt, columnSummary[y2[0]].format?.valueType)
504
- } else {
505
- y2Format = getFormatObjectFromString(y2Fmt, columnSummary[y2].format?.valueType)
506
- }
507
- } else {
508
- if (typeof y2 === 'object') {
509
- y2Format = columnSummary[y2[0]].format
510
- } else {
511
- y2Format = columnSummary[y2].format
512
- }
513
- }
463
+ if (y2.length) {
464
+ if (y2Fmt) y2Format = getFormatObjectFromString(y2Fmt, columnSummary[y2[0]].format?.valueType)
465
+ else y2Format = columnSummary[y2[0]].format
514
466
  }
515
467
 
516
468
  if (size) {
@@ -523,21 +475,9 @@
523
475
 
524
476
  xUnitSummary = columnSummary[x].columnUnitSummary
525
477
 
526
- if (y) {
527
- if (typeof y === 'object') {
528
- yUnitSummary = columnSummary[y[0]].columnUnitSummary
529
- } else {
530
- yUnitSummary = columnSummary[y].columnUnitSummary
531
- }
532
- }
478
+ if (y.length) yUnitSummary = columnSummary[y[0]].columnUnitSummary
533
479
 
534
- if (y2) {
535
- if (typeof y2 === 'object') {
536
- y2UnitSummary = columnSummary[y2[0]].columnUnitSummary
537
- } else {
538
- y2UnitSummary = columnSummary[y2].columnUnitSummary
539
- }
540
- }
480
+ if (y2.length) y2UnitSummary = columnSummary[y2[0]].columnUnitSummary
541
481
 
542
482
  if (xAxisTitle === 'true') {
543
483
  xAxisTitle = formatTitle(x, xFormat)
@@ -546,13 +486,21 @@
546
486
  }
547
487
 
548
488
  if (yAxisTitle === 'true') {
549
- yAxisTitle = typeof y === 'object' ? '' : formatTitle(y, yFormat)
489
+ if (y.length === 1) {
490
+ yAxisTitle = formatTitle(y[0], yFormat)
491
+ } else {
492
+ yAxisTitle = ''
493
+ }
550
494
  } else if (yAxisTitle === 'false') {
551
495
  yAxisTitle = ''
552
496
  }
553
497
 
554
498
  if (y2AxisTitle === 'true') {
555
- y2AxisTitle = typeof y2 === 'object' ? '' : formatTitle(y2, y2Format)
499
+ if (y2.length === 1) {
500
+ y2AxisTitle = formatTitle(y2[0], y2Format)
501
+ } else {
502
+ y2AxisTitle = ''
503
+ }
556
504
  } else if (y2AxisTitle === 'false') {
557
505
  y2AxisTitle = ''
558
506
  }
@@ -560,18 +508,13 @@
560
508
  // ---------------------------------------------------------------------------------------
561
509
  // Get total series count
562
510
  // ---------------------------------------------------------------------------------------
563
- let yCount = typeof y === 'object' ? y.length : 1
511
+ let yCount = y.length
564
512
  let seriesCount = series ? getDistinctCount(data, series) : 1
565
513
  let ySeriesCount = yCount * seriesCount
566
514
 
567
515
  // y2Count may need to be adjusted to also factor in the series column. For now, we really
568
516
  // only need to know that it's multi-series, so > 1 is sufficient
569
- let y2Count = 0
570
- if (typeof y2 === 'object') {
571
- y2Count = y2.length
572
- } else if (y2) {
573
- y2Count = 1
574
- }
517
+ let y2Count = y2.length
575
518
  let totalSeriesCount = ySeriesCount + y2Count
576
519
 
577
520
  // ---------------------------------------------------------------------------------------
@@ -594,15 +537,13 @@
594
537
  }
595
538
 
596
539
  let minYValue
597
- if (typeof y === 'object') {
540
+ if (y.length) {
598
541
  minYValue = columnSummary[y[0]].columnUnitSummary.min
599
542
  for (let i = 0; i < y.length; i++) {
600
543
  if (columnSummary[y[i]].columnUnitSummary.min < minYValue) {
601
544
  minYValue = columnSummary[y[i]].columnUnitSummary.min
602
545
  }
603
546
  }
604
- } else if (y) {
605
- minYValue = columnSummary[y].columnUnitSummary.min
606
547
  }
607
548
 
608
549
  if (yLog === true && minYValue <= 0 && minYValue !== null) {
@@ -747,7 +688,7 @@
747
688
  }
748
689
  } else {
749
690
  let primaryAxisColor = (() => {
750
- if (!y2) return undefined
691
+ if (!(Array.isArray(y2) && y2.length)) return undefined
751
692
  if ($yAxisColorStore === 'true') return $colorPaletteStore[0]
752
693
  if ($yAxisColorStore === 'false') return undefined
753
694
  return $yAxisColorStore
@@ -857,7 +798,7 @@
857
798
 
858
799
  hasTitle = title ? true : false
859
800
  hasSubtitle = subtitle ? true : false
860
- hasLegend = legend * (series !== null || (typeof y === 'object' && y.length > 1))
801
+ hasLegend = legend * (series !== null || (y.length > 1))
861
802
  hasTopAxisTitle = yAxisTitle !== '' && swapXY
862
803
  hasBottomAxisTitle = xAxisTitle !== '' && !swapXY
863
804
 
@@ -1048,7 +989,13 @@
1048
989
  console.error(setTextRed, `Error in ${chartType}: ${e.message}`)
1049
990
 
1050
991
  // Make an "id" for the chart so its clear to users/agents exactly which caused an error.
1051
- let fieldStr = Object.entries(chartContext || {}).filter(([_, val]) => !!val).map(([name, val]) => `${name}="${val}"`)
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}"`)
1052
999
  let id = `${title || chartType} (${fieldStr.join(' ')})`
1053
1000
  logError(e, {id})
1054
1001
 
@@ -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
 
@@ -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 && typeof resolvedY !== 'object') {
98
- if (columnSummary?.[resolvedY]) name = name ?? formatTitle(resolvedY, columnSummary[resolvedY].title)
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 (resolvedY2) {
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,11 +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, y2, series}}
105
+ chartContext={{data, x, y, series}}
106
106
  {x}
107
107
  {y}
108
108
  {y2}
@@ -4,7 +4,7 @@
4
4
 
5
5
  export let data: string | {rows?: any[]}
6
6
  export let height = 200
7
- export let fields: Record<string, string> = {}
7
+ export let fields: Record<string, string | string[]> = {}
8
8
 
9
9
  let errors: Error[] | null = null
10
10
  let loaded: any[] | null = null
@@ -23,7 +23,7 @@ interface QueryNode {
23
23
  contents: string
24
24
  callback?: ResultHandler
25
25
  loading: boolean
26
- fields: Map<string, string>
26
+ fields: Map<string, string | string[]>
27
27
  errors: Error[]
28
28
  }
29
29
 
@@ -41,10 +41,18 @@ function updateParam (name: string, value: any) {
41
41
  runAll() // for now, do the easy thing and reload it all
42
42
  }
43
43
 
44
- function query (source: string, fields: Record<string, string>, callback: ResultHandler) {
44
+ function query (source: string, fields: Record<string, string | string[]>, callback: ResultHandler) {
45
45
  // using Map here because it preserves the order in which we add fields to the select, which we use when we get the result.
46
46
  let map = new Map(Object.entries(fields))
47
- let exprs = map.size > 0 ? Array.from(map.values()) : ['*']
47
+ let exprs: string[] = []
48
+ if (map.size > 0) {
49
+ map.forEach((value) => {
50
+ if (Array.isArray(value)) exprs.push(...value)
51
+ else exprs.push(value)
52
+ })
53
+ } else {
54
+ exprs = ['*']
55
+ }
48
56
  let contents = `from ${source} select ${exprs.join(', ')}`
49
57
  queries.push({contents, callback, loading: false, fields: map, errors: [], source})
50
58
  runAll()
@@ -87,13 +95,22 @@ async function runNode (n: QueryNode) {
87
95
  let body = isJson ? await response.json() : await response.text()
88
96
  n.errors = Array.isArray(body) ? body : [{message: body}]
89
97
 
90
- let fieldIds = Array.from(n.fields.entries()).map(([name, val]) => `${name}="${val}"`)
98
+ let fieldIds = Array.from(n.fields.entries()).flatMap(([name, val]) => {
99
+ if (Array.isArray(val)) {
100
+ if (val.length === 0) return [] as string[]
101
+ if (val.length === 1) return [`${name}="${val[0]}"`]
102
+ return [`${name}="${val.join(', ')}"`]
103
+ }
104
+ if (typeof val === 'string' && val.trim().length === 0) return [] as string[]
105
+ if (val == null) return [] as string[]
106
+ return [`${name}="${val}"`]
107
+ })
91
108
  let idStr = `Query (data="${n.source}" ` + fieldIds.join(' ') + ')'
92
- n.errors.forEach(e => e.id = idStr)
109
+ n.errors.forEach(e => (e as any).id = idStr)
93
110
  n.callback({errors: n.errors})
94
111
  }
95
112
  } catch (e) {
96
- n.errors = [e]
113
+ n.errors = [e as Error]
97
114
  } finally {
98
115
  n.loading = false
99
116
  }
@@ -115,7 +132,11 @@ function translateData (data: any, node: QueryNode) {
115
132
  let rows = data.rows || []
116
133
  rows.dataLoaded = true // evidence components need this to be set
117
134
  rows._evidenceColumnTypes = []
118
- let requestFields = Array.from(node.fields.values())
135
+ let requestFields: string[] = []
136
+ node.fields.forEach((value) => {
137
+ if (Array.isArray(value)) requestFields.push(...value)
138
+ else requestFields.push(value)
139
+ })
119
140
 
120
141
  data.fields.forEach((field, index) => {
121
142
  let name = field.name
@@ -143,7 +164,7 @@ errorProvider('queryEngine', () => {
143
164
  queries.flatMap(q => q.errors).filter(q => !!q).forEach(e => {
144
165
  unique[e.message + String((e as any).from?.lineText)] = e
145
166
  })
146
- return Object.values(unique)
167
+ return Object.values(unique) as Error[]
147
168
  })
148
169
 
149
170
  async function waitForQueries (timeout = 20_000) {
package/dist/ui/web.js CHANGED
@@ -86,10 +86,11 @@ async function captureChart (chartTitle) {
86
86
  async function takeScreenshot () {
87
87
  await waitForQueriesToFinish()
88
88
  if (!window.html2canvas) {
89
- let html2canvas = await import('html2canvas')
89
+ let html2canvas = await import('@graphenedata/html2canvas')
90
90
  window.html2canvas = html2canvas.default
91
91
  }
92
- let canvas = await window.html2canvas(document.body, {useCORS: true, allowTaint: true, scale: 1})
92
+
93
+ let canvas = await window.html2canvas(document.body, {useCORS: true, allowTaint: true, scale: 1, liveDOM: true})
93
94
  let errors = getErrors().map(e => ({message: e.message, id: e.id}))
94
95
  return {stillLoading: isLoading(), screenshot: canvas?.toDataURL('image/png'), errors}
95
96
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "main": "cli.ts",
4
4
  "type": "module",
5
5
  "author": "Graphene Systems Inc",
6
- "version": "0.0.5",
6
+ "version": "0.0.6",
7
7
  "license": "Elastic-2.0",
8
8
  "engines": {
9
9
  "node": ">=16"
@@ -39,7 +39,7 @@
39
39
  "echarts": "^5.5.0",
40
40
  "fs-extra": "11.2.0",
41
41
  "glob": "^11.0.3",
42
- "html2canvas": "^1.4.1",
42
+ "@graphenedata/html2canvas": "^1.4.1",
43
43
  "marked": "^16.3.0",
44
44
  "mdsvex": "^0.12.6",
45
45
  "nanoid": "3.3.8",