Skip to content

Commit 68aefcc

Browse files
Add ability to inspect row, column and value from table viz menu (#12986)
1 parent ccee660 commit 68aefcc

File tree

3 files changed

+95
-10
lines changed

3 files changed

+95
-10
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@
99
- [Type annotations are now visible in the graph editor][12751]
1010
- [Component Browser shows methods with respect to possible type casts][12751]
1111
- [Add option to browse cloud for secret values][12953]
12+
- [Add ability to inspect column, row and value from right click on table
13+
viz][12986]
1214

1315
[12774]: https://github.com/enso-org/enso/pull/12774
1416
[12778]: https://github.com/enso-org/enso/pull/12778
1517
[12850]: https://github.com/enso-org/enso/pull/12850
1618
[12913]: https://github.com/enso-org/enso/pull/12913
1719
[12751]: https://github.com/enso-org/enso/pull/12751
1820
[12953]: https://github.com/enso-org/enso/pull/12953
21+
[12986]: https://github.com/enso-org/enso/pull/12986
1922

2023
#### Enso Standard Library
2124

app/gui/src/project-view/components/shared/AgGridTableView.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ import type {
7878
ColumnResizedEvent,
7979
ColumnVisibleEvent,
8080
FirstDataRenderedEvent,
81+
GetContextMenuItems,
82+
GetContextMenuItemsParams,
8183
GetRowIdFunc,
8284
GridApi,
8385
GridReadyEvent,
@@ -128,6 +130,9 @@ const props = defineProps<{
128130
rowCount?: number
129131
isServerSideModel?: boolean
130132
gridIdHash?: string | null
133+
getContextMenuItems?: (
134+
params: GetContextMenuItemsParams,
135+
) => (MenuItemDef | string)[] | GetContextMenuItems
131136
}>()
132137
const emit = defineEmits<{
133138
cellEditingStarted: [event: CellEditingStartedEvent]
@@ -373,6 +378,7 @@ const { AgGridVue } = await import('./AgGridTableView/AgGridVue')
373378
:processDataFromClipboard="processDataFromClipboard"
374379
:allowContextMenuWithControlKey="true"
375380
:cacheBlockSize="rowModelType === 'clientSide' ? undefined : 1000"
381+
:getContextMenuItems="getContextMenuItems"
376382
:getRowHeight="rowModelType === 'clientSide' ? getRowHeight : null"
377383
@gridReady="onGridReady"
378384
@firstDataRendered="updateColumnWidths"

app/gui/src/project-view/components/visualizations/TableVisualization.vue

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@ import {
77
} from '@/components/visualizations/TableVisualization/tableVizToolbar'
88
import { Ast } from '@/util/ast'
99
import { Pattern } from '@/util/ast/match'
10+
import { Icon } from '@/util/iconMetadata/iconName'
1011
import { useVisualizationConfig } from '@/util/visualizationBuiltins'
1112
import type {
1213
CellClassParams,
1314
CellDoubleClickedEvent,
1415
ColDef,
1516
ColumnVisibleEvent,
17+
GetContextMenuItems,
18+
GetContextMenuItemsParams,
1619
ICellRendererParams,
1720
IServerSideDatasource,
1821
IServerSideGetRowsRequest,
1922
ITooltipParams,
23+
MenuItemDef,
2024
SetFilterValuesFuncParams,
2125
SortChangedEvent,
2226
} from 'ag-grid-enterprise'
@@ -159,19 +163,92 @@ const defaultColDef: Ref<ColDef> = ref({
159163
cellRenderer: cellRenderer,
160164
cellClass: cellClass,
161165
cellStyle: { 'padding-left': 0, 'border-right': '1px solid #C0C0C0' },
162-
contextMenuItems: [
163-
commonContextMenuActions.copy,
164-
commonContextMenuActions.copyWithHeaders,
165-
'separator',
166-
'export',
167-
],
168166
} satisfies ColDef)
169167
const rowData = ref<Record<string, any>[]>([])
170168
const columnDefs: Ref<ColDef[]> = ref([])
171169
const nodeType = ref<string | undefined>(undefined)
172170
const grid = ref<
173171
ComponentInstance<typeof AgGridTableView> & ComponentExposed<typeof AgGridTableView>
174172
>()
173+
174+
const getSvgTemplate = (icon: Icon) =>
175+
`<svg viewBox="0 0 16 16" width="16" height="16"> <use xlink:href="${icons}#${icon}"/> </svg>`
176+
177+
const getContextMenuItems = (
178+
params: GetContextMenuItemsParams,
179+
): (MenuItemDef | string)[] | GetContextMenuItems => {
180+
const colId = params.column ? params.column.getColId() : null
181+
const { rowIndex } = params.node ?? {}
182+
183+
const actions = [
184+
{ name: 'Get Column', action: 'at', colId, icon: 'select_column' },
185+
{ name: 'Get Row', action: 'get_row', rowIndex, icon: 'select_row' },
186+
{ name: 'Get Value', action: 'get_value', colId, rowIndex, icon: 'local_scope4' },
187+
]
188+
189+
const createMenuItem = ({ name, action, colId, rowIndex, icon }: (typeof actions)[number]) => ({
190+
name,
191+
action: () => createValueNode(colId, rowIndex, action),
192+
icon: getSvgTemplate(icon as Icon),
193+
})
194+
195+
return [
196+
commonContextMenuActions.copy,
197+
commonContextMenuActions.copyWithHeaders,
198+
'separator',
199+
'export',
200+
...actions.map(createMenuItem),
201+
]
202+
}
203+
204+
function getAstValuePattern(value?: string | number, action?: string) {
205+
if (action && value != null) {
206+
return Pattern.new<Ast.Expression>((ast) =>
207+
Ast.App.positional(
208+
Ast.PropertyAccess.new(ast.module, ast, Ast.identifier(action)!),
209+
typeof value === 'number' ?
210+
Ast.tryNumberToEnso(value, ast.module)!
211+
: Ast.TextLiteral.new(value, ast.module),
212+
),
213+
)
214+
}
215+
}
216+
217+
function getAstGetValuePattern(columnId?: string, rowIndex?: number, action?: string) {
218+
if (action && columnId && rowIndex != undefined) {
219+
const pattern = Pattern.parseExpression('__ __')
220+
return Pattern.new<Ast.Expression>((ast) =>
221+
Ast.App.positional(
222+
Ast.PropertyAccess.new(ast.module, ast, Ast.identifier('get_value')!),
223+
pattern.instantiateCopied([
224+
Ast.TextLiteral.new(columnId as string, ast.module),
225+
Ast.tryNumberToEnso(rowIndex as number, ast.module)!,
226+
]),
227+
),
228+
)
229+
}
230+
}
231+
232+
function createValueNode(columnId?: string | null, rowIndex?: number | null, action?: string) {
233+
let pattern
234+
if (action === 'at' && columnId != null) {
235+
pattern = getAstValuePattern(columnId, action)
236+
}
237+
if (action === 'get_row' && rowIndex != null) {
238+
pattern = getAstValuePattern(rowIndex, action)
239+
}
240+
if (action === 'get_value' && columnId != null && rowIndex != null) {
241+
pattern = getAstGetValuePattern(columnId, rowIndex, action)
242+
}
243+
244+
if (pattern) {
245+
config.createNodes({
246+
content: pattern,
247+
commit: true,
248+
})
249+
}
250+
}
251+
175252
const allRowCount = computed(() =>
176253
typeof props.data === 'object' && 'all_rows_count' in props.data ? props.data.all_rows_count : 0,
177254
)
@@ -277,13 +354,13 @@ const isCreateNewNodeEnabled = computed(
277354
278355
const numberFormatGroupped = new Intl.NumberFormat(undefined, {
279356
style: 'decimal',
280-
maximumFractionDigits: 12,
357+
maximumSignificantDigits: 16,
281358
useGrouping: true,
282359
})
283360
284361
const numberFormat = new Intl.NumberFormat(undefined, {
285362
style: 'decimal',
286-
maximumFractionDigits: 12,
363+
maximumSignificantDigits: 16,
287364
useGrouping: false,
288365
})
289366
@@ -579,8 +656,6 @@ function toField(
579656
const showDataQuality =
580657
dataQualityMetrics.filter((obj) => (Object.values(obj)[0] as number) > 0).length > 0
581658
582-
const getSvgTemplate = (icon: string) =>
583-
`<svg viewBox="0 0 16 16" width="16" height="16"> <use xlink:href="${icons}#${icon}"/> </svg>`
584659
const svgTemplateWarning = showDataQuality ? getSvgTemplate('warning') : ''
585660
const menu = `<span data-ref="eMenu" class="ag-header-icon ag-header-cell-menu-button"> </span>`
586661
const filterButton = `<span data-ref="eFilterButton" class="ag-header-icon ag-header-cell-filter-button" aria-hidden="true"></span>`
@@ -1087,6 +1162,7 @@ config.setToolbar(
10871162
:isServerSideModel="isSSRM"
10881163
:statusBar="statusBar"
10891164
:gridIdHash="tableVersionHash"
1165+
:getContextMenuItems="getContextMenuItems"
10901166
@sortOrFilterUpdated="checkSortAndFilter"
10911167
@columnStateChanged="onColumnStateChange"
10921168
/>

0 commit comments

Comments
 (0)