@graphenedata/cli 0.0.15 → 0.0.16

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 (117) hide show
  1. package/README.md +138 -0
  2. package/dist/cli/bigQuery-I3F46SC6.js +75 -0
  3. package/dist/cli/bigQuery-I3F46SC6.js.map +7 -0
  4. package/dist/cli/chunk-OVWODUTJ.js +12849 -0
  5. package/dist/cli/chunk-OVWODUTJ.js.map +7 -0
  6. package/dist/cli/chunk-QAXEOZ43.js +53 -0
  7. package/dist/cli/chunk-QAXEOZ43.js.map +7 -0
  8. package/dist/cli/cli.js +234 -11197
  9. package/dist/cli/clickhouse-ZN5AN2UL.js +64 -0
  10. package/dist/cli/clickhouse-ZN5AN2UL.js.map +7 -0
  11. package/dist/cli/duckdb-IYBIO5KJ.js +87 -0
  12. package/dist/cli/duckdb-IYBIO5KJ.js.map +7 -0
  13. package/dist/cli/serve2-TNN5EROW.js +447 -0
  14. package/dist/cli/serve2-TNN5EROW.js.map +7 -0
  15. package/dist/cli/snowflake-MOQB5GA4.js +128 -0
  16. package/dist/cli/snowflake-MOQB5GA4.js.map +7 -0
  17. package/dist/index.d.ts +63 -0
  18. package/dist/lang/index.d.ts +63 -0
  19. package/dist/skills/graphene/SKILL.md +150 -96
  20. package/dist/skills/graphene/references/big-value.md +6 -41
  21. package/dist/skills/graphene/references/date-range.md +64 -0
  22. package/dist/skills/graphene/references/dropdown.md +3 -4
  23. package/dist/skills/graphene/references/echarts.md +162 -0
  24. package/dist/skills/graphene/references/gsql.md +55 -25
  25. package/dist/skills/graphene/references/model-gsql.md +72 -0
  26. package/dist/skills/graphene/references/table.md +13 -14
  27. package/dist/skills/graphene/references/text-input.md +2 -1
  28. package/dist/ui/app.css +239 -340
  29. package/dist/ui/component-utilities/dataShaping.ts +484 -0
  30. package/dist/ui/component-utilities/dataSummary.ts +57 -0
  31. package/dist/ui/component-utilities/enrich.ts +763 -0
  32. package/dist/ui/component-utilities/format.ts +177 -0
  33. package/dist/ui/component-utilities/inputUtils.ts +44 -8
  34. package/dist/ui/component-utilities/theme.ts +200 -0
  35. package/dist/ui/component-utilities/themeStores.ts +21 -8
  36. package/dist/ui/component-utilities/types.ts +70 -0
  37. package/dist/ui/components/AreaChart.svelte +57 -105
  38. package/dist/ui/components/BarChart.svelte +71 -129
  39. package/dist/ui/components/BigValue.svelte +24 -40
  40. package/dist/ui/components/Column.svelte +10 -18
  41. package/dist/ui/components/DateRange.svelte +54 -21
  42. package/dist/ui/components/Dropdown.svelte +47 -26
  43. package/dist/ui/components/DropdownOption.svelte +1 -2
  44. package/dist/ui/components/ECharts.svelte +181 -67
  45. package/dist/ui/components/InlineDelta.svelte +50 -31
  46. package/dist/ui/components/LineChart.svelte +54 -125
  47. package/dist/ui/components/PieChart.svelte +27 -37
  48. package/dist/ui/components/QueryLoad.svelte +77 -45
  49. package/dist/ui/components/Row.svelte +2 -1
  50. package/dist/ui/components/ScatterPlot.svelte +52 -0
  51. package/dist/ui/components/Skeleton.svelte +32 -0
  52. package/dist/ui/components/Table.svelte +3 -2
  53. package/dist/ui/components/TableGroupRow.svelte +28 -36
  54. package/dist/ui/components/TableHarness.svelte +32 -0
  55. package/dist/ui/components/TableHeader.svelte +34 -59
  56. package/dist/ui/components/TableRow.svelte +14 -38
  57. package/dist/ui/components/TableSubtotalRow.svelte +18 -21
  58. package/dist/ui/components/TableTotalRow.svelte +27 -37
  59. package/dist/ui/components/TextInput.svelte +13 -12
  60. package/dist/ui/components/Value.svelte +25 -0
  61. package/dist/ui/components/_Table.svelte +72 -70
  62. package/dist/ui/internal/ChartGallery.svelte +527 -0
  63. package/dist/ui/internal/ErrorDisplay.svelte +22 -97
  64. package/dist/ui/internal/LocalApp.svelte +80 -17
  65. package/dist/ui/internal/PageNavGroup.svelte +269 -0
  66. package/dist/ui/internal/Sidebar.svelte +178 -0
  67. package/dist/ui/internal/SidebarToggle.svelte +47 -0
  68. package/dist/ui/internal/StyleGallery.svelte +244 -0
  69. package/dist/ui/internal/clientCache.ts +2 -2
  70. package/dist/ui/internal/pageInputs.svelte.js +292 -0
  71. package/dist/ui/internal/queryEngine.ts +102 -117
  72. package/dist/ui/internal/runSocket.ts +32 -12
  73. package/dist/ui/internal/sidebar.svelte.js +18 -0
  74. package/dist/ui/internal/telemetry.ts +51 -16
  75. package/dist/ui/internal/types.d.ts +7 -0
  76. package/dist/ui/web.js +28 -11
  77. package/package.json +36 -38
  78. package/dist/skills/graphene/references/area-chart.md +0 -95
  79. package/dist/skills/graphene/references/bar-chart.md +0 -112
  80. package/dist/skills/graphene/references/line-chart.md +0 -108
  81. package/dist/skills/graphene/references/pie-chart.md +0 -29
  82. package/dist/skills/graphene/references/value-formats.md +0 -104
  83. package/dist/ui/component-utilities/autoFormatting.js +0 -280
  84. package/dist/ui/component-utilities/builtInFormats.js +0 -481
  85. package/dist/ui/component-utilities/chartContext.js +0 -12
  86. package/dist/ui/component-utilities/chartWindowDebug.js +0 -21
  87. package/dist/ui/component-utilities/checkInputs.js +0 -84
  88. package/dist/ui/component-utilities/convert.js +0 -15
  89. package/dist/ui/component-utilities/dateParsing.js +0 -56
  90. package/dist/ui/component-utilities/dropdownContext.ts +0 -1
  91. package/dist/ui/component-utilities/echarts.js +0 -252
  92. package/dist/ui/component-utilities/echartsThemes.js +0 -443
  93. package/dist/ui/component-utilities/formatTitle.js +0 -24
  94. package/dist/ui/component-utilities/formatting.js +0 -241
  95. package/dist/ui/component-utilities/getColumnExtents.js +0 -79
  96. package/dist/ui/component-utilities/getColumnSummary.js +0 -62
  97. package/dist/ui/component-utilities/getCompletedData.js +0 -122
  98. package/dist/ui/component-utilities/getDistinctCount.js +0 -7
  99. package/dist/ui/component-utilities/getDistinctValues.js +0 -15
  100. package/dist/ui/component-utilities/getSeriesConfig.js +0 -231
  101. package/dist/ui/component-utilities/getSortedData.js +0 -9
  102. package/dist/ui/component-utilities/getStackPercentages.js +0 -45
  103. package/dist/ui/component-utilities/getStackedData.js +0 -19
  104. package/dist/ui/component-utilities/getYAxisIndex.js +0 -15
  105. package/dist/ui/component-utilities/globalContexts.js +0 -1
  106. package/dist/ui/component-utilities/helpers/getCompletedData.helpers.js +0 -119
  107. package/dist/ui/component-utilities/replaceNulls.js +0 -16
  108. package/dist/ui/component-utilities/tableUtils.ts +0 -107
  109. package/dist/ui/component-utilities/tidyWithTypes.js +0 -9
  110. package/dist/ui/components/Area.svelte +0 -214
  111. package/dist/ui/components/Bar.svelte +0 -347
  112. package/dist/ui/components/Chart.svelte +0 -995
  113. package/dist/ui/components/Line.svelte +0 -227
  114. package/dist/ui/internal/NavSidebar.svelte +0 -396
  115. package/dist/ui/internal/theme.ts +0 -60
  116. package/dist/ui/public/inter-latin-ext.woff2 +0 -0
  117. package/dist/ui/public/inter-latin.woff2 +0 -0
@@ -1,10 +1,11 @@
1
1
  <script lang="ts">
2
2
  import type {Snippet} from 'svelte'
3
+ import type {QueryResult} from '../component-utilities/types.ts'
3
4
  import QueryLoad from './QueryLoad.svelte'
4
5
  import TableInner from './_Table.svelte'
5
6
 
6
7
  interface Props {
7
- data: string
8
+ data: string | QueryResult
8
9
  children?: Snippet
9
10
  [key: string]: unknown
10
11
  }
@@ -14,7 +15,7 @@
14
15
  let spreadProps = $derived(Object.fromEntries(Object.entries(restProps).filter(([, value]) => value !== undefined)))
15
16
  </script>
16
17
 
17
- {#snippet tableContent(loaded: any[])}
18
+ {#snippet tableContent(loaded: QueryResult)}
18
19
  {#if children}
19
20
  <TableInner {...spreadProps} data={loaded} {children} />
20
21
  {:else}
@@ -2,15 +2,14 @@
2
2
  import TableCell from './TableCell.svelte'
3
3
  import TableGroupToggle from './TableGroupToggle.svelte'
4
4
  import InlineDelta from './InlineDelta.svelte'
5
- import {aggregateColumn, safeExtractColumn} from '../component-utilities/tableUtils'
6
- import {formatValue, getFormatObjectFromString} from '../component-utilities/formatting.js'
7
- import {toBoolean} from '../component-utilities/convert'
5
+ import {summarizeColumn, type SummaryMetric} from '../component-utilities/dataSummary.ts'
6
+ import {formatFromField} from '../component-utilities/format.ts'
7
+ import {toBoolean} from '../component-utilities/inputUtils'
8
8
 
9
9
  interface Props {
10
10
  groupName: string
11
11
  currentGroupData?: any[]
12
12
  toggled?: boolean
13
- columnSummary?: any[]
14
13
  rowNumbers?: boolean | string
15
14
  rowColor?: string
16
15
  subtotals?: boolean | string
@@ -20,7 +19,7 @@
20
19
  }
21
20
 
22
21
  let {
23
- groupName, currentGroupData = [], toggled = true, columnSummary = [],
22
+ groupName, currentGroupData = [], toggled = true,
24
23
  rowNumbers: rowNumbersProp = undefined, rowColor = undefined, subtotals: subtotalsProp = true,
25
24
  orderedColumns = [], compact: compactProp = undefined, onToggle,
26
25
  }: Props = $props()
@@ -29,6 +28,16 @@
29
28
  let subtotals = $derived(toBoolean(subtotalsProp) ?? true)
30
29
  let compact = $derived(toBoolean(compactProp))
31
30
 
31
+ const SUPPORTED_METRICS: SummaryMetric[] = ['sum', 'mean', 'median', 'min', 'max', 'count', 'countDistinct']
32
+
33
+ const getAggregateValue = (rows: Record<string, unknown>[], column: any) => {
34
+ let metric = column?.totalAgg as SummaryMetric | undefined
35
+ if (!metric && String(column?.type || '').toLowerCase() === 'number') metric = 'sum'
36
+ if (!metric || !SUPPORTED_METRICS.includes(metric)) return '-'
37
+ let summary = summarizeColumn(rows, column.field ?? {name: column.id, type: column.type}, [metric])
38
+ return summary[metric] ?? null
39
+ }
40
+
32
41
  const toggleGroup = () => onToggle?.({groupName})
33
42
 
34
43
  const handleKeydown = (event: KeyboardEvent) => {
@@ -38,12 +47,6 @@
38
47
  }
39
48
  }
40
49
 
41
- const resolveFormat = (column: any, summary: any) => {
42
- if (column.subtotalFmt) return getFormatObjectFromString(column.subtotalFmt)
43
- if (column.totalFmt) return getFormatObjectFromString(column.totalFmt)
44
- if (column.fmt) return getFormatObjectFromString(column.fmt, summary.format?.valueType)
45
- return summary.format
46
- }
47
50
  </script>
48
51
 
49
52
  <tr
@@ -63,8 +66,6 @@
63
66
  {/if}
64
67
 
65
68
  {#each orderedColumns as column, index (index)}
66
- {@const summary = safeExtractColumn(column, columnSummary)}
67
- {@const format = resolveFormat(column, summary)}
68
69
  {#if index === 0}
69
70
  {#if rowNumbers}
70
71
  <!-- Covered by the row-number label cell -->
@@ -77,30 +78,21 @@
77
78
  </TableCell>
78
79
  {/if}
79
80
  {:else if subtotals}
80
- <TableCell class={summary.type} {compact} align={column.align}>
81
- {#if ['sum', 'mean', 'median', 'min', 'max', 'weightedMean', 'count', 'countDistinct', undefined].includes(column.totalAgg) || column.subtotalFmt}
82
- {#if column.contentType === 'delta'}
83
- <InlineDelta
84
- value={aggregateColumn(currentGroupData, column.id, column.totalAgg, summary.type, column.weightCol)}
85
- downIsGood={column.downIsGood}
86
- formatObject={format}
87
- columnUnitSummary={summary.columnUnitSummary}
88
- showValue={column.showValue}
89
- showSymbol={column.deltaSymbol}
90
- align={column.align}
91
- neutralMin={column.neutralMin ?? 0}
92
- neutralMax={column.neutralMax ?? 0}
93
- chip={column.chip}
94
- />
95
- {:else}
96
- {formatValue(
97
- aggregateColumn(currentGroupData, column.id, column.totalAgg, summary.type, column.weightCol),
98
- format,
99
- summary.columnUnitSummary,
100
- )}
101
- {/if}
81
+ <TableCell class={column.type} {compact} align={column.align}>
82
+ {#if column.contentType === 'delta'}
83
+ <InlineDelta
84
+ value={getAggregateValue(currentGroupData, column)}
85
+ downIsGood={column.downIsGood}
86
+ field={column.field}
87
+ showValue={column.showValue}
88
+ showSymbol={column.deltaSymbol}
89
+ align={column.align}
90
+ neutralMin={column.neutralMin ?? 0}
91
+ neutralMax={column.neutralMax ?? 0}
92
+ chip={column.chip}
93
+ />
102
94
  {:else}
103
- {column.totalAgg}
95
+ {formatFromField(column.field, getAggregateValue(currentGroupData, column))}
104
96
  {/if}
105
97
  </TableCell>
106
98
  {:else}
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import Column from './Column.svelte'
3
+ import Table from './Table.svelte'
4
+
5
+ interface Props {
6
+ data: any
7
+ tableProps?: Record<string, unknown>
8
+ columns?: Record<string, unknown>[]
9
+ width?: number | string
10
+ }
11
+
12
+ let {data, tableProps = {}, columns = [], width = 880}: Props = $props()
13
+ let wrapperWidth = $derived(typeof width === 'number' ? `${width}px` : width)
14
+ </script>
15
+
16
+ <div class="table-harness" style:width={wrapperWidth}>
17
+ {#if columns.length > 0}
18
+ <Table data={data} {...tableProps}>
19
+ {#each columns as column, index (index)}
20
+ <Column {...column} />
21
+ {/each}
22
+ </Table>
23
+ {:else}
24
+ <Table data={data} {...tableProps} />
25
+ {/if}
26
+ </div>
27
+
28
+ <style>
29
+ .table-harness {
30
+ max-width: 100%;
31
+ }
32
+ </style>
@@ -1,14 +1,12 @@
1
1
  <script lang="ts">
2
2
  import SortIcon from './SortIcon.svelte'
3
- import {safeExtractColumn} from '../component-utilities/tableUtils'
4
- import {toBoolean} from '../component-utilities/convert'
3
+ import {toBoolean} from '../component-utilities/inputUtils'
5
4
 
6
5
  interface Props {
7
6
  rowNumbers?: boolean | string
8
7
  headerColor?: string
9
8
  headerFontColor?: string
10
9
  orderedColumns?: any[]
11
- columnSummary?: any[]
12
10
  sortable?: boolean | string
13
11
  sortClick?: (columnId: string) => () => void
14
12
  formatColumnTitles?: boolean | string
@@ -20,7 +18,7 @@
20
18
 
21
19
  let {
22
20
  rowNumbers: rowNumbersProp = false, headerColor = undefined, headerFontColor = undefined,
23
- orderedColumns = [], columnSummary = [], sortable: sortableProp = true, sortClick = () => () => {},
21
+ orderedColumns = [], sortable: sortableProp = true, sortClick = () => () => {},
24
22
  formatColumnTitles: formatColumnTitlesProp = true, sortObj = {col: null, ascending: null},
25
23
  wrapTitles: wrapTitlesProp = false, compact: compactProp = false, link = undefined,
26
24
  }: Props = $props()
@@ -31,12 +29,11 @@
31
29
  let wrapTitles = $derived(toBoolean(wrapTitlesProp) ?? false)
32
30
  let compact = $derived(toBoolean(compactProp) ?? false)
33
31
 
34
- const getWrapTitleAlignment = (column: any) => {
35
- if (column.align === 'right') return 'header-title--align-end'
36
- if (column.align === 'center') return 'header-title--align-center'
37
- let extracted = safeExtractColumn(column, columnSummary)
38
- if (extracted.type === 'number') return 'header-title--align-end'
39
- return 'header-title--align-start'
32
+ const getHeaderAlignment = (column: any) => {
33
+ if (column.align) return column.align
34
+ if (['sparkline', 'sparkbar', 'sparkarea', 'bar'].includes(column.contentType)) return 'center'
35
+ if (column.type === 'number') return 'right'
36
+ return 'left'
40
37
  }
41
38
 
42
39
  const computeGroupSpans = (columns: any[]) => {
@@ -55,10 +52,10 @@
55
52
  return sortObj.ascending ? 'ascending' : 'descending'
56
53
  }
57
54
 
58
- const resolveHeaderTitle = (column: any, summary: any) => {
55
+ const resolveHeaderTitle = (column: any) => {
59
56
  if (column.title) return column.title
60
- if (formatColumnTitles) return summary.title
61
- return summary.id
57
+ if (formatColumnTitles) return column.defaultTitle ?? column.id
58
+ return column.id
62
59
  }
63
60
 
64
61
  let columnsWithGroupSpan = $derived(computeGroupSpans(orderedColumns))
@@ -71,7 +68,7 @@
71
68
  {#if rowNumbers}
72
69
  <th class={`header-index ${compact ? 'header-index--compact' : ''}`} style:background-color={headerColor}></th>
73
70
  {/if}
74
- {#each columnsWithGroupSpan as column (column.id)}
71
+ {#each columnsWithGroupSpan as column (column.identifier ?? column.id)}
75
72
  {#if column.colGroup && column.isNewGroup}
76
73
  <th class="header-group" colspan={column.span}>
77
74
  <div class="header-group__label">{column.colGroup}</div>
@@ -95,33 +92,28 @@
95
92
  style:color={headerFontColor}
96
93
  ></th>
97
94
  {/if}
98
- {#each orderedColumns as column (column.id)}
99
- {@const summary = safeExtractColumn(column, columnSummary)}
95
+ {#each orderedColumns as column (column.identifier ?? column.id)}
100
96
  <th
101
97
  role="columnheader"
102
- class={`header-cell ${summary.type ?? ''} ${compact ? 'header-cell--compact' : ''}`}
98
+ class={`header-cell ${column.type ?? ''} ${compact ? 'header-cell--compact' : ''}`}
103
99
  style:color={headerFontColor}
104
100
  style:background={headerColor}
105
- style:text-align={column.align ?? (['sparkline', 'sparkbar', 'sparkarea', 'bar'].includes(column.contentType) ? 'center' : undefined)}
101
+ style:text-align={getHeaderAlignment(column)}
106
102
  style:cursor={sortable ? 'pointer' : 'auto'}
107
103
  onclick={sortable ? sortClick(column.id) : undefined}
108
104
  aria-sort={getAriaSortValue(column.id)}
109
105
  >
110
- <div class={`header-title ${wrapTitles || column.wrapTitle ? 'header-title--wrap' : ''} ${wrapTitles || column.wrapTitle ? getWrapTitleAlignment(column) : ''}`.trim()}>
111
- <span class={`header-title__text ${wrapTitles || column.wrapTitle ? 'header-title__text--wrap' : ''}`}>
112
- {resolveHeaderTitle(column, summary)}
113
- {#if column.description}
114
- <span class="header-title__info" title={column.description}>ⓘ</span>
115
- {/if}
116
- </span>
106
+ <span class={`header-title__text ${wrapTitles || column.wrapTitle ? 'header-title__text--wrap' : ''}`}>
107
+ {resolveHeaderTitle(column)}
108
+ {#if column.description}
109
+ <span class="header-title__info" title={column.description}>ⓘ</span>
110
+ {/if}
111
+ </span>
112
+ {#if sortObj.col === column.id}
117
113
  <span class="header-sort-indicator">
118
- {#if sortObj.col === column.id}
119
- <SortIcon ascending={sortObj.ascending ?? undefined} />
120
- {:else}
121
- <span class="header-sort-placeholder"><SortIcon ascending /></span>
122
- {/if}
114
+ <SortIcon ascending={sortObj.ascending ?? undefined} />
123
115
  </span>
124
- </div>
116
+ {/if}
125
117
  </th>
126
118
  {/each}
127
119
  {#if link}
@@ -178,40 +170,24 @@
178
170
  }
179
171
 
180
172
  .header-cell {
173
+ position: relative;
181
174
  padding: 2px 13px 2px 6px;
182
175
  vertical-align: bottom;
183
176
  }
184
177
 
185
- .header-cell--compact {
186
- padding: 1px 6px 1px 1px;
187
- font-size: 12px;
178
+ .header-cell:first-child {
179
+ padding-left: 3px;
188
180
  }
189
181
 
190
- .header-title {
191
- display: flex;
192
- align-items: flex-end;
193
- justify-content: space-between;
194
- gap: 4px;
195
- }
196
-
197
- .header-title--wrap {
198
- align-items: stretch;
199
- }
200
-
201
- .header-title--align-end {
202
- justify-content: flex-end;
203
- }
204
-
205
- .header-title--align-center {
206
- justify-content: center;
207
- }
208
-
209
- .header-title--align-start {
210
- justify-content: flex-start;
182
+ .header-cell--compact {
183
+ padding: 1px 16.5px 1px 1px;
184
+ font-size: 12px;
211
185
  }
212
186
 
213
187
  .header-title__text {
214
188
  display: inline-block;
189
+ max-width: 100%;
190
+ text-align: inherit;
215
191
  letter-spacing: -0.015em;
216
192
  }
217
193
 
@@ -227,14 +203,13 @@
227
203
  }
228
204
 
229
205
  .header-sort-indicator {
206
+ position: absolute;
207
+ right: 1px;
208
+ bottom: 4px;
230
209
  display: inline-flex;
231
210
  align-items: center;
232
211
  }
233
212
 
234
- .header-sort-placeholder {
235
- visibility: hidden;
236
- }
237
-
238
213
  .header-link-cell {
239
214
  width: 24px;
240
215
  }
@@ -2,8 +2,7 @@
2
2
  import chroma from 'chroma-js'
3
3
  import InlineDelta from './InlineDelta.svelte'
4
4
  import TableCell from './TableCell.svelte'
5
- import {safeExtractColumn} from '../component-utilities/tableUtils'
6
- import {formatValue, getFormatObjectFromString} from '../component-utilities/formatting.js'
5
+ import {formatFromField} from '../component-utilities/format.ts'
7
6
  import {getThemeStores} from '../component-utilities/themeStores'
8
7
 
9
8
  interface Props {
@@ -13,7 +12,7 @@
13
12
  rowNumbers?: boolean | string
14
13
  rowLines?: boolean | string
15
14
  index?: number
16
- columnSummary?: any[]
15
+ columnLookup?: Record<string, any>
17
16
  grouped?: boolean
18
17
  groupType?: 'accordion' | 'section'
19
18
  groupColumn?: string
@@ -26,7 +25,7 @@
26
25
  let {
27
26
  displayedData = [], rowShading: rowShadingProp = undefined, link = undefined,
28
27
  rowNumbers: rowNumbersProp = undefined, rowLines: rowLinesProp = undefined, index = 0,
29
- columnSummary = [], grouped = false, groupType = undefined, groupColumn = undefined,
28
+ columnLookup = {}, grouped = false, groupType = undefined, groupColumn = undefined,
30
29
  rowSpan = 1, groupNamePosition = 'middle', orderedColumns = [], compact: compactProp = undefined,
31
30
  }: Props = $props()
32
31
 
@@ -124,10 +123,9 @@
124
123
  {/if}
125
124
 
126
125
  {#each orderedColumns as column, k (k)}
127
- {@const summary = safeExtractColumn(column, columnSummary)}
128
- {@const scaleSummary = column.scaleColumn ? columnSummary.find((d) => d.id === column.scaleColumn) : summary}
129
- {@const columnMin = column.colorMin ?? scaleSummary?.columnUnitSummary?.min}
130
- {@const columnMax = column.colorMax ?? scaleSummary?.columnUnitSummary?.max}
126
+ {@const scaleSummary = column.scaleColumn ? columnLookup[column.scaleColumn] : column}
127
+ {@const columnMin = column.colorMin ?? scaleSummary?.stats?.min}
128
+ {@const columnMax = column.colorMax ?? scaleSummary?.stats?.max}
131
129
  {@const colorScale = column.contentType === 'colorscale'
132
130
  ? computeColorScale(column, columnMin, columnMax)
133
131
  : undefined}
@@ -145,21 +143,15 @@
145
143
  if (contentContrast < backgroundContrast) return $theme.colors['base-100']
146
144
  return $theme.colors['base-content']
147
145
  })()}
148
- {@const columnFormat = (() => {
149
- if (column.fmt) return getFormatObjectFromString(column.fmt, summary.format?.valueType)
150
- if (column.fmtColumn && row[column.fmtColumn]) {
151
- return getFormatObjectFromString(row[column.fmtColumn], summary.format?.valueType)
152
- }
153
- return summary.format
154
- })()}
155
146
  {@const paddingLeft = k === 0 && grouped && groupType === 'accordion' && !rowNumbers ? '28px' : undefined}
156
- {@const shouldShow = !(groupType === 'section' && groupColumn === summary.id && i !== 0)}
147
+ {@const shouldShow = !(groupType === 'section' && groupColumn === column.id && i !== 0)}
157
148
  <TableCell
158
- class={summary?.type}
149
+ class={column?.type}
159
150
  {compact}
160
151
  verticalAlign={groupType === 'section' ? groupNamePosition : undefined}
161
- rowSpan={groupType === 'section' && groupColumn === summary.id && i === 0 ? rowSpan : 1}
152
+ rowSpan={groupType === 'section' && groupColumn === column.id && i === 0 ? rowSpan : 1}
162
153
  {paddingLeft}
154
+ align={column.align}
163
155
  wrap={column.wrap}
164
156
  cellColor={formattedColor}
165
157
  fontColor={fontColor}
@@ -185,21 +177,12 @@
185
177
  >
186
178
  {#if column.linkLabel != undefined}
187
179
  {#if row[column.linkLabel] != undefined}
188
- {@const labelSummary = safeExtractColumn({id: column.linkLabel}, columnSummary)}
189
- {formatValue(
190
- row[column.linkLabel],
191
- column.fmt ? getFormatObjectFromString(column.fmt, labelSummary.format?.valueType) : labelSummary.format,
192
- labelSummary.columnUnitSummary,
193
- )}
180
+ {formatFromField(columnLookup[column.linkLabel]?.field, row[column.linkLabel])}
194
181
  {:else}
195
182
  {column.linkLabel}
196
183
  {/if}
197
184
  {:else}
198
- {formatValue(
199
- row[column.id],
200
- column.fmt ? getFormatObjectFromString(column.fmt, summary.format?.valueType) : summary.format,
201
- summary.columnUnitSummary,
202
- )}
185
+ {formatFromField(column.field, row[column.id])}
203
186
  {/if}
204
187
  </a>
205
188
  {/if}
@@ -207,8 +190,7 @@
207
190
  <InlineDelta
208
191
  value={row[column.id]}
209
192
  downIsGood={column.downIsGood}
210
- formatObject={columnFormat}
211
- columnUnitSummary={summary.columnUnitSummary}
193
+ field={column.field}
212
194
  showValue={column.showValue}
213
195
  showSymbol={column.deltaSymbol}
214
196
  align={column.align}
@@ -217,13 +199,7 @@
217
199
  chip={column.chip}
218
200
  />
219
201
  {:else}
220
- {#if row[column.id] === null || row[column.id] === undefined}
221
-
222
- {:else if summary.type === 'number'}
223
- {formatValue(row[column.id], columnFormat, summary.columnUnitSummary)}
224
- {:else}
225
- {formatValue(row[column.id], columnFormat, summary.columnUnitSummary)}
226
- {/if}
202
+ {formatFromField(column.field, row[column.id])}
227
203
  {/if}
228
204
  </TableCell>
229
205
  {/each}
@@ -1,14 +1,13 @@
1
1
  <script lang="ts">
2
2
  import InlineDelta from './InlineDelta.svelte'
3
- import {aggregateColumn, safeExtractColumn} from '../component-utilities/tableUtils'
4
- import {formatValue, getFormatObjectFromString} from '../component-utilities/formatting.js'
3
+ import {summarizeColumn, type SummaryMetric} from '../component-utilities/dataSummary.ts'
4
+ import {formatFromField} from '../component-utilities/format.ts'
5
5
  import TableCell from './TableCell.svelte'
6
- import {toBoolean} from '../component-utilities/convert'
6
+ import {toBoolean} from '../component-utilities/inputUtils'
7
7
 
8
8
  interface Props {
9
9
  groupName?: string
10
10
  currentGroupData?: any[]
11
- columnSummary?: any[]
12
11
  rowColor?: string
13
12
  groupBy?: string
14
13
  groupType?: 'accordion' | 'section'
@@ -19,12 +18,22 @@
19
18
  }
20
19
 
21
20
  let {
22
- groupName = undefined, currentGroupData = [], columnSummary = [], rowColor = undefined,
21
+ groupName = undefined, currentGroupData = [], rowColor = undefined,
23
22
  groupBy = undefined, groupType = undefined, rowNumbers: rowNumbersProp = undefined,
24
23
  fontColor = undefined, orderedColumns = [], compact = undefined,
25
24
  }: Props = $props()
26
25
 
27
26
  let rowNumbers = $derived(toBoolean(rowNumbersProp) ?? false)
27
+
28
+ const SUPPORTED_METRICS: SummaryMetric[] = ['sum', 'mean', 'median', 'min', 'max', 'count', 'countDistinct']
29
+
30
+ const getAggregateValue = (rows: Record<string, unknown>[], column: any) => {
31
+ let metric = column?.totalAgg as SummaryMetric | undefined
32
+ if (!metric && String(column?.type || '').toLowerCase() === 'number') metric = 'sum'
33
+ if (!metric || !SUPPORTED_METRICS.includes(metric)) return '-'
34
+ let summary = summarizeColumn(rows, column.field ?? {name: column.id, type: column.type}, [metric])
35
+ return summary[metric] ?? null
36
+ }
28
37
  </script>
29
38
 
30
39
  <tr class="subtotal-row" style:background-color={rowColor} style:color={fontColor}>
@@ -32,21 +41,13 @@
32
41
  <TableCell class="index" {compact}></TableCell>
33
42
  {/if}
34
43
  {#each orderedColumns as column (column.id)}
35
- {@const summary = safeExtractColumn(column, columnSummary)}
36
- {@const baseFormat = column.fmt ? getFormatObjectFromString(column.fmt, summary.format?.valueType) : summary.format}
37
- {@const format = (() => {
38
- if (column.subtotalFmt) return getFormatObjectFromString(column.subtotalFmt)
39
- if (column.totalFmt) return getFormatObjectFromString(column.totalFmt)
40
- return baseFormat
41
- })()}
42
- <TableCell class={summary.type} {compact} align={column.align}>
44
+ <TableCell class={column.type} {compact} align={column.align}>
43
45
  {#if column.id !== groupBy}
44
46
  {#if column.contentType === 'delta'}
45
47
  <InlineDelta
46
- value={aggregateColumn(currentGroupData, column.id, column.totalAgg, summary.type, column.weightCol)}
48
+ value={getAggregateValue(currentGroupData, column)}
47
49
  downIsGood={column.downIsGood}
48
- formatObject={baseFormat}
49
- columnUnitSummary={summary.columnUnitSummary}
50
+ field={column.field}
50
51
  showValue={column.showValue}
51
52
  showSymbol={column.deltaSymbol}
52
53
  align={column.align}
@@ -55,11 +56,7 @@
55
56
  chip={column.chip}
56
57
  />
57
58
  {:else}
58
- {formatValue(
59
- aggregateColumn(currentGroupData, column.id, column.totalAgg, summary.type, column.weightCol),
60
- format,
61
- summary.columnUnitSummary,
62
- )}
59
+ {formatFromField(column.field, getAggregateValue(currentGroupData, column))}
63
60
  {/if}
64
61
  {:else if groupType === 'section'}
65
62
  {groupName}
@@ -1,13 +1,12 @@
1
1
  <script lang="ts">
2
2
  import InlineDelta from './InlineDelta.svelte'
3
3
  import TableCell from './TableCell.svelte'
4
- import {safeExtractColumn, weightedMean} from '../component-utilities/tableUtils'
5
- import {formatValue, getFormatObjectFromString} from '../component-utilities/formatting.js'
4
+ import {summarizeColumn, type SummaryMetric} from '../component-utilities/dataSummary.ts'
5
+ import {formatFromField} from '../component-utilities/format.ts'
6
6
 
7
7
  interface Props {
8
8
  data?: any[]
9
9
  rowNumbers?: boolean | string
10
- columnSummary?: any[]
11
10
  rowColor?: string
12
11
  fontColor?: string
13
12
  groupType?: 'accordion' | 'section'
@@ -16,7 +15,7 @@
16
15
  }
17
16
 
18
17
  let {
19
- data = [], rowNumbers: rowNumbersProp = undefined, columnSummary = [], rowColor = undefined,
18
+ data = [], rowNumbers: rowNumbersProp = undefined, rowColor = undefined,
20
19
  fontColor = undefined, groupType = undefined, orderedColumns = [], compact: compactProp = undefined,
21
20
  }: Props = $props()
22
21
 
@@ -32,6 +31,16 @@
32
31
 
33
32
  let rowNumbers = $derived(toBool(rowNumbersProp))
34
33
  let compact = $derived(toBool(compactProp))
34
+
35
+ const SUPPORTED_METRICS: SummaryMetric[] = ['sum', 'mean', 'median', 'min', 'max', 'count', 'countDistinct']
36
+
37
+ const getAggregateValue = (rows: Record<string, unknown>[], column: any, aggType: string | undefined) => {
38
+ let metric = aggType as SummaryMetric | undefined
39
+ if (!metric && String(column?.type || '').toLowerCase() === 'number') metric = 'sum'
40
+ if (!metric || !SUPPORTED_METRICS.includes(metric)) return '-'
41
+ let summary = summarizeColumn(rows, column.field ?? {name: column.id, type: column.type}, [metric])
42
+ return summary[metric] ?? null
43
+ }
35
44
  </script>
36
45
 
37
46
  <tr class="total-row" style:background-color={rowColor} style:color={fontColor}>
@@ -40,49 +49,30 @@
40
49
  {/if}
41
50
 
42
51
  {#each orderedColumns as column (column.id)}
43
- {@const summary = safeExtractColumn(column, columnSummary)}
44
- {@const format = (() => {
45
- if (column.totalFmt) return getFormatObjectFromString(column.totalFmt)
46
- if (column.fmt) return getFormatObjectFromString(column.fmt, summary.format?.valueType)
47
- return summary.format
48
- })()}
49
52
  {@const totalAgg = column.totalAgg ?? 'sum'}
50
53
  <TableCell
51
54
  {compact}
52
- dataType={summary.type}
55
+ dataType={column.type}
53
56
  align={column.align}
54
57
  height={column.height}
55
58
  width={column.width}
56
59
  wrap={column.wrap}
57
60
  topBorder="1px solid rgba(107, 114, 128, 0.5)"
58
61
  >
59
- {#if ['sum', 'mean', 'weightedMean', 'median', 'min', 'max', 'count', 'countDistinct'].includes(totalAgg)}
60
- {#if column.contentType === 'delta'}
61
- <InlineDelta
62
- value={totalAgg === 'weightedMean' ? weightedMean(data, column.id, column.weightCol) : summary.columnUnitSummary?.[totalAgg]}
63
- downIsGood={column.downIsGood}
64
- formatObject={format}
65
- columnUnitSummary={summary.columnUnitSummary}
66
- showValue={column.showValue}
67
- showSymbol={column.deltaSymbol}
68
- align={column.align}
69
- neutralMin={column.neutralMin ?? 0}
70
- neutralMax={column.neutralMax ?? 0}
71
- chip={column.chip}
72
- />
73
- {:else}
74
- {formatValue(
75
- totalAgg === 'weightedMean' ? weightedMean(data, column.id, column.weightCol) : summary.columnUnitSummary?.[totalAgg],
76
- format,
77
- summary.columnUnitSummary,
78
- )}
79
- {/if}
62
+ {#if column.contentType === 'delta'}
63
+ <InlineDelta
64
+ value={getAggregateValue(data, column, totalAgg)}
65
+ downIsGood={column.downIsGood}
66
+ field={column.field}
67
+ showValue={column.showValue}
68
+ showSymbol={column.deltaSymbol}
69
+ align={column.align}
70
+ neutralMin={column.neutralMin ?? 0}
71
+ neutralMax={column.neutralMax ?? 0}
72
+ chip={column.chip}
73
+ />
80
74
  {:else}
81
- {#if column.totalFmt}
82
- {formatValue(totalAgg, format, summary.columnUnitSummary)}
83
- {:else}
84
- {totalAgg}
85
- {/if}
75
+ {formatFromField(column.field, getAggregateValue(data, column, totalAgg))}
86
76
  {/if}
87
77
  </TableCell>
88
78
  {/each}