From 3229fac54ee5490ca63f10dc16748dde071c4ae6 Mon Sep 17 00:00:00 2001 From: Anan Zhuang Date: Sat, 20 Jul 2024 00:03:19 +0000 Subject: [PATCH] solid area color and add interative selection Signed-off-by: Anan Zhuang --- .../vega/build_spec_vega_lite.ts | 4 +- .../vega/components/encoding.ts | 49 +++++++++++++------ .../visualizations/vega/components/mark.ts | 36 +++++++++++--- .../vega/components/selection.ts | 32 ++++++++++++ .../visualizations/vega/utils/helpers.ts | 2 +- .../public/visualizations/vega/utils/types.ts | 7 +++ .../vislib/area/to_expression.ts | 19 ------- 7 files changed, 106 insertions(+), 43 deletions(-) create mode 100644 src/plugins/vis_builder/public/visualizations/vega/components/selection.ts diff --git a/src/plugins/vis_builder/public/visualizations/vega/build_spec_vega_lite.ts b/src/plugins/vis_builder/public/visualizations/vega/build_spec_vega_lite.ts index c457f8f17b5..c802dab0e7d 100644 --- a/src/plugins/vis_builder/public/visualizations/vega/build_spec_vega_lite.ts +++ b/src/plugins/vis_builder/public/visualizations/vega/build_spec_vega_lite.ts @@ -7,6 +7,7 @@ import { buildEncoding } from './components/encoding'; import { buildMark } from './components/mark'; import { buildTooltip } from './components/tooltip'; import { buildLegend } from './components/legend'; +import { buildSelection } from './components/selection'; import { StyleState } from '../../application/utils/state_management'; import { VegaLiteSpec, AxisFormats } from './utils/types'; @@ -43,11 +44,12 @@ export const buildVegaSpecViaVegaLite = ( }; // Build the base Vega-Lite specification - const baseSpec: VegaSpec = { + const baseSpec: VegaLiteSpec = { $schema: 'https://vega.github.io/schema/vega-lite/v5.json', data: { values: transformedData }, mark: buildMark(type), encoding: buildEncoding(dimensions, formats), + selection: buildSelection(), }; // Handle special case for line charts with dot size diff --git a/src/plugins/vis_builder/public/visualizations/vega/components/encoding.ts b/src/plugins/vis_builder/public/visualizations/vega/components/encoding.ts index 282aee58990..de52798395d 100644 --- a/src/plugins/vis_builder/public/visualizations/vega/components/encoding.ts +++ b/src/plugins/vis_builder/public/visualizations/vega/components/encoding.ts @@ -4,17 +4,32 @@ */ import { mapFieldTypeToVegaType } from '../utils/helpers'; -import { AxisFormats } from '../utils/types'; +import { AxisFormat, AxisFormats } from '../utils/types'; -interface EncodingChannel { - field: string; - type: string; +interface BaseEncodingChannel { + field?: string; + type?: string; +} + +interface AxisEncodingChannel extends BaseEncodingChannel { axis?: { title: string }; +} + +interface ColorEncodingChannel extends BaseEncodingChannel { legend?: { title: string | null }; } +interface OpacityEncodingChannel extends BaseEncodingChannel { + condition: { selection: string; value: number }; + value: number; +} + interface VegaEncoding { - [key: string]: EncodingChannel; + x?: AxisEncodingChannel; + y?: AxisEncodingChannel; + color?: ColorEncodingChannel; + opacity?: OpacityEncodingChannel; + [key: string]: BaseEncodingChannel | undefined; } interface VegaScale { @@ -25,7 +40,7 @@ interface VegaScale { field: string; filter?: string; }; - range: string; + range?: string; padding?: number; nice?: boolean; zero?: boolean; @@ -41,11 +56,9 @@ interface VegaScale { */ export const buildEncoding = ( dimensions: any, - formats: any, + formats: AxisFormats, isVega: boolean = false ): VegaEncoding | VegaScale[] => { - const { xAxisFormat, xAxisLabel, yAxisFormat, yAxisLabel, zAxisFormat } = formats; - if (isVega) { return buildVegaScales(dimensions, formats); } @@ -57,10 +70,10 @@ export const buildEncoding = ( * Builds encoding configuration for Vega-Lite specifications. * * @param {any} dimensions - The dimensions of the data. - * @param {any} formats - The formatting information for axes. + * @param {AxisFormats} formats - The formatting information for axes. * @returns {VegaEncoding} The Vega-Lite encoding configuration. */ -const buildVegaLiteEncoding = (dimensions: any, formats: any): VegaEncoding => { +const buildVegaLiteEncoding = (dimensions: any, formats: AxisFormats): VegaEncoding => { const { xAxisFormat, xAxisLabel, yAxisFormat, yAxisLabel, zAxisFormat } = formats; const encoding: VegaEncoding = {}; @@ -77,6 +90,12 @@ const buildVegaLiteEncoding = (dimensions: any, formats: any): VegaEncoding => { encoding.color = buildColorEncoding('series', mapFieldTypeToVegaType(zAxisFormat?.id || '')); } + // Always add opacity encoding + encoding.opacity = { + condition: { selection: 'legend_selection', value: 1 }, + value: 0.2, + }; + return encoding; }; @@ -129,13 +148,13 @@ const buildVegaScales = (dimensions: any, formats: any): VegaScale[] => { const buildAxisEncoding = ( field: string, dimension: any[] | undefined, - axisFormat: AxisFormat, - axisLabel: string + axisFormat?: AxisFormat, + axisLabel?: string ): EncodingChannel => { return { field, - type: dimension ? mapFieldTypeToVegaType(axisFormat.id) : 'ordinal', - axis: { title: axisLabel }, + type: dimension ? mapFieldTypeToVegaType(axisFormat?.id) : 'ordinal', + axis: { title: axisLabel ? axisLabel : '' }, }; }; diff --git a/src/plugins/vis_builder/public/visualizations/vega/components/mark.ts b/src/plugins/vis_builder/public/visualizations/vega/components/mark.ts index b46f11d7081..bebcff2b88b 100644 --- a/src/plugins/vis_builder/public/visualizations/vega/components/mark.ts +++ b/src/plugins/vis_builder/public/visualizations/vega/components/mark.ts @@ -5,11 +5,28 @@ import { mapChartTypeToVegaType } from '../utils/helpers'; -type VegaMarkType = 'line' | 'rect' | 'area' | 'symbol' | 'bar' | 'point' | 'circle' | 'square'; +type VegaMarkType = + | 'line' + | 'rect' + | 'area' + | 'symbol' + | 'bar' + | 'point' + | 'circle' + | 'square' + | 'group'; interface VegaMark { type: VegaMarkType; - from?: { data: string }; + from?: { + data?: string; + facet?: { + name?: string; + data?: string; + groupby?: string; + filter?: string; + }; + }; encode?: { enter?: Record; update?: Record; @@ -66,16 +83,19 @@ export const buildMark = ( * @returns {VegaLiteMark} The Vega-Lite mark configuration. */ const buildMarkForVegaLite = (vegaType: VegaMarkType): VegaLiteMark => { + const baseMark = { + opacity: { condition: { selection: 'hover', value: 1 }, value: 0.3 }, + }; switch (vegaType) { case 'line': - return { type: 'line', point: true }; + return { ...baseMark, type: 'line', point: true }; case 'area': - return { type: 'area', line: true }; + return { ...baseMark, type: 'area', line: true, opacity: 1, fillOpacity: 1 }; case 'rect': case 'bar': - return { type: 'bar' }; + return { ...baseMark, type: 'bar' }; default: - return { type: vegaType }; + return { ...baseMark, type: vegaType }; } }; @@ -184,7 +204,9 @@ const buildMarkForArea = (): VegaMark[] => [ y: { scale: 'yscale', field: 'y' }, y2: { scale: 'yscale', value: 0 }, fill: { scale: 'color', field: 'series' }, - fillOpacity: { value: 0.7 }, + fillOpacity: { value: 1 }, + stroke: { scale: 'color', field: 'series' }, + strokeOpacity: { value: 1 }, }, }, }, diff --git a/src/plugins/vis_builder/public/visualizations/vega/components/selection.ts b/src/plugins/vis_builder/public/visualizations/vega/components/selection.ts new file mode 100644 index 00000000000..37e2fec8927 --- /dev/null +++ b/src/plugins/vis_builder/public/visualizations/vega/components/selection.ts @@ -0,0 +1,32 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Define types for selection configurations +interface SelectionConfig { + type: 'single' | 'multi'; + fields: string[]; + bind: 'legend' | { [key: string]: any }; +} + +interface VegaLiteSelection { + [key: string]: SelectionConfig; +} + +/** + * Builds a selection configuration for Vega-Lite specifications. + * This function creates a multi-selection bound to the legend, + * allowing for interactive filtering of data based on legend items. + * + * @returns {VegaLiteSelection} The selection configuration object. + */ +export const buildSelection = (): VegaLiteSelection => { + return { + legend_selection: { + type: 'multi', // Allows multiple items to be selected + fields: ['series'], // The field to be used for selection (typically the series field) + bind: 'legend', // Binds the selection to the chart's legend + }, + }; +}; diff --git a/src/plugins/vis_builder/public/visualizations/vega/utils/helpers.ts b/src/plugins/vis_builder/public/visualizations/vega/utils/helpers.ts index bfd7814f9ee..5b5e513dd0a 100644 --- a/src/plugins/vis_builder/public/visualizations/vega/utils/helpers.ts +++ b/src/plugins/vis_builder/public/visualizations/vega/utils/helpers.ts @@ -15,7 +15,7 @@ import { AxisFormats } from './types'; * @param {Array} group - The group containing axis information */ const setAxisProperties = (converted: any, group: any[]): void => { - const axes: Array = ['xAxis', 'yAxis', 'zAxis']; + const axes = ['xAxis', 'yAxis', 'zAxis']; const properties = ['Format', 'Label']; axes.forEach((axis) => { diff --git a/src/plugins/vis_builder/public/visualizations/vega/utils/types.ts b/src/plugins/vis_builder/public/visualizations/vega/utils/types.ts index f24901e2c50..a2b62bf7e60 100644 --- a/src/plugins/vis_builder/public/visualizations/vega/utils/types.ts +++ b/src/plugins/vis_builder/public/visualizations/vega/utils/types.ts @@ -43,6 +43,13 @@ export interface VegaLiteSpec { legend?: any; [key: string]: any; }; + selection?: { + legend_selection?: { + type: string; + fields: string[]; + bind: string; + }; + }; } // Define a more general VegaSpec interface diff --git a/src/plugins/vis_builder/public/visualizations/vislib/area/to_expression.ts b/src/plugins/vis_builder/public/visualizations/vislib/area/to_expression.ts index 6908b1e8e20..3561f9ae58d 100644 --- a/src/plugins/vis_builder/public/visualizations/vislib/area/to_expression.ts +++ b/src/plugins/vis_builder/public/visualizations/vislib/area/to_expression.ts @@ -32,22 +32,6 @@ export const toExpression = async ( const params = getPipelineParams(); const dimensions = await buildVislibDimensions(vis, params); const valueAxes = getValueAxes(dimensions.y); - const seriesParams = [ - { - "show": true, - "type": "area", - "mode": "stacked", - "data": { - "label": "Count", - "id": "1" - }, - "drawLinesBetweenPoints": true, - "lineWidth": 2, - "showCircles": true, - "interpolate": "linear", - "valueAxis": "ValueAxis-1" - } -] // TODO: what do we want to put in this "vis config"? const visConfig = { @@ -57,11 +41,8 @@ export const toExpression = async ( addTooltip, dimensions, valueAxes, - seriesParams }; - - if (useVega === true) { const rawDataFn = buildExpressionFunction('rawData', {}); const dataExpression = buildExpression([...expressionFns, rawDataFn]).toString();