From c4a954dcc93f8923bc5d067fe415879e9a83f0cd Mon Sep 17 00:00:00 2001 From: Chenxu Liu Date: Sun, 22 Oct 2023 23:43:20 -0700 Subject: [PATCH] Make 'sort' parameter appear in a higher level, as part of the encoding. --- build/vega-lite-schema.json | 19 ++++++++++++------- src/channel.ts | 8 +++++++- src/channeldef.ts | 9 +++++++-- src/compile/mark/encode/tooltip.ts | 22 ++++++++++------------ src/encoding.ts | 6 ++++++ 5 files changed, 42 insertions(+), 22 deletions(-) diff --git a/build/vega-lite-schema.json b/build/vega-lite-schema.json index 37e90ec59d..83c8c13c80 100644 --- a/build/vega-lite-schema.json +++ b/build/vega-lite-schema.json @@ -5064,6 +5064,10 @@ ], "description": "The tooltip text to show upon mouse hover. Specifying `tooltip` encoding overrides [the `tooltip` property in the mark definition](https://vega.github.io/vega-lite/docs/mark.html#mark-def).\n\nSee the [`tooltip`](https://vega.github.io/vega-lite/docs/tooltip.html) documentation for a detailed discussion about tooltip in Vega-Lite." }, + "sort_tooltip": { + "$ref": "#/definitions/TooltipSort", + "description": "Parameter to control whether and how the tooltip is sorted. It's users' responsibilities to make sure to put non-sortable fields or the fields they do not want to sort at the beginning or the end of the tooltip array, otherwise the order will be messed up." + }, "url": { "anyOf": [ { @@ -27227,6 +27231,14 @@ ] } }, + "TooltipSort": { + "value": { + "enum": [ + "ascending", + "descending" + ] + } + }, "TooltipFieldDef": { "additionalProperties": false, "properties": { @@ -27306,13 +27318,6 @@ "filter": { "$ref": "#/definitions/TooltipFilter", "description": "The tooltip filter object to control what values to show/hide in the tooltip." - }, - "sorted": { - "enum": [ - "ascending", - "descending" - ], - "description": "Parameter to control whether and how the tooltip is sorted. The first defined 'sorted' parameter that appears in the tooltip array controls the sorting order, and any defined parameters after that will simply be ignored. " } }, "type": "object" diff --git a/src/channel.ts b/src/channel.ts index 289cc693c3..67e845e70b 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -67,6 +67,8 @@ export const DETAIL = 'detail' as const; export const KEY = 'key' as const; export const TOOLTIP = 'tooltip' as const; + +export const SORT_TOOLTIP = 'sort_tooltip' as const; export const HREF = 'href' as const; export const URL = 'url' as const; @@ -154,7 +156,8 @@ const UNIT_CHANNEL_INDEX: Flag = { tooltip: 1, href: 1, url: 1, - description: 1 + description: 1, + sort_tooltip: 1 }; export type ColorChannel = 'color' | 'fill' | 'stroke'; @@ -431,6 +434,7 @@ const { // href has neither format, nor scale text: _t, tooltip: _tt, + sort_tooltip: _stt, href: _hr, url: _u, description: _al, @@ -532,6 +536,7 @@ function getSupportedMark(channel: ExtendedChannel): SupportedMark { case DETAIL: case KEY: case TOOLTIP: + case SORT_TOOLTIP: case HREF: case ORDER: // TODO: revise (order might not support rect, which is not stackable?) case OPACITY: @@ -641,6 +646,7 @@ export function rangeType(channel: ExtendedChannel): RangeType { // TEXT, TOOLTIP, URL, and HREF have no scale but have discrete output [falls through] case TEXT: case TOOLTIP: + case SORT_TOOLTIP: case HREF: case URL: case DESCRIPTION: diff --git a/src/channeldef.ts b/src/channeldef.ts index 8377429ab1..9926810624 100644 --- a/src/channeldef.ts +++ b/src/channeldef.ts @@ -33,6 +33,7 @@ import { ROW, SHAPE, SIZE, + SORT_TOOLTIP, STROKE, STROKEDASH, STROKEOPACITY, @@ -651,11 +652,14 @@ export interface TooltipFilter { export interface TooltipFieldDef extends StringFieldDef { filter?: TooltipFilter - sorted?: "ascending" | "descending" }; +export interface TooltipSort { + value: "ascending" | "descending" +} + export function isTooltipFieldDef(fieldDef: FieldDef): fieldDef is TooltipFieldDef { - return "filter" in fieldDef || "sorted" in fieldDef; + return "filter" in fieldDef; } @@ -1280,6 +1284,7 @@ export function channelCompatibility( case DETAIL: case KEY: case TOOLTIP: + case SORT_TOOLTIP: case HREF: case URL: case ANGLE: diff --git a/src/compile/mark/encode/tooltip.ts b/src/compile/mark/encode/tooltip.ts index 7e91fd70a4..8f73511c94 100644 --- a/src/compile/mark/encode/tooltip.ts +++ b/src/compile/mark/encode/tooltip.ts @@ -11,6 +11,7 @@ import { isTypedFieldDef, SecondaryFieldDef, TooltipFilter, + TooltipSort, TypedFieldDef, vgField } from '../../../channeldef'; @@ -29,7 +30,8 @@ export function tooltip(model: UnitModel, opt: {reactiveGeom?: boolean} = {}) { const {encoding, markDef, config, stack} = model; const channelDef = encoding.tooltip; if (isArray(channelDef)) { - return {tooltip: tooltipRefForEncoding({tooltip: channelDef}, stack, config, opt)}; + const sort_tooltip: TooltipSort | undefined = "sort_tooltip" in encoding ? encoding.sort_tooltip : undefined; + return {tooltip: tooltipRefForEncoding({tooltip: channelDef, sort_tooltip: sort_tooltip}, stack, config, opt)}; } else { const datum = opt.reactiveGeom ? 'datum.datum' : 'datum'; return wrapCondition(model, channelDef, 'tooltip', cDef => { @@ -74,10 +76,13 @@ export function tooltipData( config: Config, {reactiveGeom}: {reactiveGeom?: boolean} = {} ) { + const toSkip = {}; const expr = reactiveGeom ? 'datum.datum' : 'datum'; const tuples: {channel: Channel; key: string; value: string}[] = []; - var sort_tooltip: boolean = false; + if (encoding.sort_tooltip != undefined && (encoding.sort_tooltip.value == "ascending" || encoding.sort_tooltip.value == "descending")) { + tuples.push({channel: "sort_tooltip", key: "tooltip_sort_placeholder", value: (encoding.sort_tooltip.value == "ascending" ? "0" : "1")}); // Encode ascending as "0", descending as "1" + } function add(fDef: TypedFieldDef | SecondaryFieldDef, channel: Channel) { const mainChannel = getMainRangeChannel(channel); @@ -128,16 +133,9 @@ export function tooltipData( // New feature: add filter for each tooltip field. if (isTooltipFieldDef(fieldDef)) { - if ("filter" in fieldDef) { - const filter: TooltipFilter = fieldDef.filter; - const comp_value = filter.literal; - value = `${value} ${filter.operator} ${comp_value} ? ${value} : ${expr + '[""]'}`; // Trigeer 'undefined' when evaluating the tooltip value such that it is filtered out by vega-tooltip. - } - - if ("sorted" in fieldDef && !sort_tooltip) { - tuples.push({channel, key: "tooltip_sort_placeholder", value: (fieldDef.sorted == "ascending" ? "0" : "1")}); // Encode ascending as "0", descending as "1" - sort_tooltip = true; - } + const filter: TooltipFilter = fieldDef.filter; + const comp_value = filter.literal; + value = `${value} ${filter.operator} ${comp_value} ? ${value} : ${expr + '[""]'}`; // Trigeer 'undefined' when evaluating the tooltip value such that it is filtered out by vega-tooltip. } tuples.push({channel, key, value}); diff --git a/src/encoding.ts b/src/encoding.ts index f29c7a3644..73c6c27dd4 100644 --- a/src/encoding.ts +++ b/src/encoding.ts @@ -30,6 +30,7 @@ import { RADIUS2, SHAPE, SIZE, + SORT_TOOLTIP, STROKE, STROKEDASH, STROKEOPACITY, @@ -83,6 +84,7 @@ import { TextDef, title, TooltipFieldDef, + TooltipSort, TypedFieldDef, vgField } from './channeldef'; @@ -323,6 +325,8 @@ export interface Encoding { * __Note__: In aggregate plots, `order` field should be `aggregate`d to avoid creating additional aggregation grouping. */ order?: OrderFieldDef | OrderFieldDef[] | OrderValueDef; + + sort_tooltip?: TooltipSort; } export interface EncodingWithFacet extends Encoding, EncodingFacetMapping { } @@ -738,6 +742,8 @@ export function pathGroupingFields(mark: Mark, encoding: Encoding): stri // tooltip fields should not be added to group by [falls through] case TOOLTIP: return details; + case SORT_TOOLTIP: + return details; case ORDER: // order should not group line / trail