diff --git a/src/compile/selection/project.ts b/src/compile/selection/project.ts index 38a29ec0c67..d70fc7c68c2 100644 --- a/src/compile/selection/project.ts +++ b/src/compile/selection/project.ts @@ -63,6 +63,7 @@ const project: SelectionCompiler = { }; const type = selCmpt.type; + const cfg = model.config.selection[type]; const init = selDef.value !== undefined ? (array(selDef.value as any) as SelectionInitMapping[] | SelectionInitIntervalMapping[]) @@ -70,32 +71,36 @@ const project: SelectionCompiler = { // If no explicit projection (either fields or encodings) is specified, set some defaults. // If an initial value is set, try to infer projections. - // Otherwise, use the default configuration. let {fields, encodings} = isObject(selDef.select) ? selDef.select : ({} as BaseSelectionConfig); - if (!fields && !encodings) { - const cfg = model.config.selection[type]; + if (!fields && !encodings && init) { + for (const initVal of init) { + // initVal may be a scalar value to smoothen varParam -> pointSelection gradient. + if (!isObject(initVal)) { + continue; + } - if (init) { - for (const initVal of init) { - for (const key of keys(initVal)) { - if (isSingleDefUnitChannel(key)) { - (encodings || (encodings = [])).push(key as SingleDefUnitChannel); + for (const key of keys(initVal)) { + if (isSingleDefUnitChannel(key)) { + (encodings || (encodings = [])).push(key as SingleDefUnitChannel); + } else { + if (type === 'interval') { + log.warn(log.message.INTERVAL_INITIALIZED_WITH_X_Y); + encodings = cfg.encodings; } else { - if (type === 'interval') { - log.warn(log.message.INTERVAL_INITIALIZED_WITH_X_Y); - encodings = cfg.encodings; - } else { - (fields || (fields = [])).push(key); - } + (fields || (fields = [])).push(key); } } } - } else { - encodings = cfg.encodings; - fields = cfg.fields; } } + // We break this out as a separate if block (instead of an else condition) + // to account for unprojected point selections that have scalar initial values + if (!fields && !encodings) { + encodings = cfg.encodings; + fields = cfg.fields; + } + for (const channel of encodings ?? []) { const fieldDef = model.fieldDef(channel); if (fieldDef) { @@ -161,7 +166,9 @@ const project: SelectionCompiler = { if (init) { selCmpt.init = (init as any).map((v: SelectionInitMapping | SelectionInitIntervalMapping) => { - return proj.items.map(p => (v[p.channel] !== undefined ? v[p.channel] : v[p.field])); + // Selections can be initialized either with a full object that maps projections to values + // or scalar values to smoothen the abstraction gradient from variable params to point selections. + return proj.items.map(p => (isObject(v) ? (v[p.channel] !== undefined ? v[p.channel] : v[p.field]) : v)); }); } diff --git a/src/selection.ts b/src/selection.ts index adb593d688f..6412807052a 100644 --- a/src/selection.ts +++ b/src/selection.ts @@ -194,7 +194,11 @@ export interface SelectionParameter { * * __See also:__ [`init`](https://vega.github.io/vega-lite/docs/init.html) documentation. */ - value?: T extends 'point' ? SelectionInitMapping[] : T extends 'interval' ? SelectionInitIntervalMapping : never; + value?: T extends 'point' + ? SelectionInit | SelectionInitMapping[] + : T extends 'interval' + ? SelectionInitIntervalMapping + : never; /** * When set, a selection is populated by input elements (also known as dynamic query widgets) diff --git a/test/compile/selection/point.test.ts b/test/compile/selection/point.test.ts index 8859385b84c..0ffa0297421 100644 --- a/test/compile/selection/point.test.ts +++ b/test/compile/selection/point.test.ts @@ -78,6 +78,18 @@ describe('Multi Selection', () => { fields: ['nested.a', 'nested.b'], clear: false } + }, + // Seven ensures a smooth abstraction gradient for "value" var params -> point selections + { + name: 'seven', + value: 50, + select: {type: 'point', fields: ['Horsepower']} + }, + // Eight ensures this smooth "value" gradient logic doesn't kick in on unprojected point selections + { + name: 'eight', + value: 75, + select: 'point' } ])); @@ -271,7 +283,27 @@ describe('Multi Selection', () => { } ] }, - {name: 'six_store'} + {name: 'six_store'}, + { + name: 'seven_store', + values: [ + { + unit: '', + fields: [{type: 'E', field: 'Horsepower'}], + values: [50] + } + ] + }, + { + name: 'eight_store', + values: [ + { + unit: '', + fields: [{type: 'E', field: '_vgsid_'}], + values: [75] + } + ] + } ]); });