diff --git a/build/vega-lite-schema.json b/build/vega-lite-schema.json index 1a24cfa023..b28ff8edc9 100644 --- a/build/vega-lite-schema.json +++ b/build/vega-lite-schema.json @@ -512,6 +512,48 @@ "minimum": 0, "type": "number" }, + "cursor": { + "description": "The mouse cursor used over the mark. Any valid [CSS cursor type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.", + "enum": [ + "auto", + "default", + "none", + "context-menu", + "help", + "pointer", + "progress", + "wait", + "cell", + "crosshair", + "text", + "vertical-text", + "alias", + "copy", + "move", + "no-drop", + "not-allowed", + "e-resize", + "n-resize", + "ne-resize", + "nw-resize", + "s-resize", + "se-resize", + "sw-resize", + "w-resize", + "ew-resize", + "ns-resize", + "nesw-resize", + "nwse-resize", + "col-resize", + "row-resize", + "all-scroll", + "zoom-in", + "zoom-out", + "grab", + "grabbing" + ], + "type": "string" + }, "discreteBandSize": { "description": "The size of the bars. If unspecified, the default size is `bandSize-1`,\nwhich provides 1 pixel offset between bars.", "minimum": 0, @@ -563,6 +605,11 @@ ], "description": "The font weight (e.g., `\"bold\"`)." }, + "href": { + "description": "A URL to load upon mouse click. If defined, the mark acts as a hyperlink.", + "format": "uri", + "type": "string" + }, "interpolate": { "$ref": "#/definitions/Interpolate", "description": "The line interpolation method to use for line and area marks. One of the following:\n- `\"linear\"`: piecewise linear segments, as in a polyline.\n- `\"linear-closed\"`: close the linear segments to form a polygon.\n- `\"step\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"step-before\"`: alternate between vertical and horizontal segments, as in a step function.\n- `\"step-after\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"basis\"`: a B-spline, with control point duplication on the ends.\n- `\"basis-open\"`: an open B-spline; may not intersect the start or end.\n- `\"basis-closed\"`: a closed B-spline, as in a loop.\n- `\"cardinal\"`: a Cardinal spline, with control point duplication on the ends.\n- `\"cardinal-open\"`: an open Cardinal spline; may not intersect the start or end, but will intersect other control points.\n- `\"cardinal-closed\"`: a closed Cardinal spline, as in a loop.\n- `\"bundle\"`: equivalent to basis, except the tension parameter is used to straighten the spline.\n- `\"monotone\"`: cubic interpolation that preserves monotonicity in y." @@ -780,6 +827,54 @@ "$ref": "#/definitions/CompositeUnitSpecAlias", "description": "Unit spec that can have a composite mark." }, + "Conditional": { + "additionalProperties": false, + "properties": { + "aggregate": { + "$ref": "#/definitions/Aggregate", + "description": "Aggregation function for the field\n(e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).\n\n__Default value:__ `undefined` (None)" + }, + "bin": { + "anyOf": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/BinParams" + } + ], + "description": "A flag for binning a `quantitative` field, or [an object defining binning parameters](bin.html#params).\nIf `true`, default [binning parameters](bin.html) will be applied.\n\n__Default value:__ `false`" + }, + "field": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/RepeatRef" + } + ], + "description": "__Required.__ A string defining the name of the field from which to pull a data value\nor an object defining iterated values from the [`repeat`](repeat.html) operator.\n\n__Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects (e.g., `\"field\": \"foo.bar\"` and `\"field\": \"foo['bar']\"`).\nIf field names contain dots or brackets but are not nested, you can use `\\\\` to escape dots and brackets (e.g., `\"a\\\\.b\"` and `\"a\\\\[0\\\\]\"`).\nSee more details about escaping in the [field documentation](field.html).\n\n__Note:__ `field` is not required if `aggregate` is `count`." + }, + "selection": { + "$ref": "#/definitions/SelectionOperand", + "description": "A [selection name](selection.html), or a series of [composed selections](selection.html#compose)." + }, + "timeUnit": { + "$ref": "#/definitions/TimeUnit", + "description": "Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.\nor [a temporal field that gets casted as ordinal](type.html#cast).\n\n__Default value:__ `undefined` (None)" + }, + "type": { + "$ref": "#/definitions/Type", + "description": "The encoded field's type of measurement (`\"quantitative\"`, `\"temporal\"`, `\"ordinal\"`, or `\"nominal\"`)." + } + }, + "required": [ + "selection", + "type" + ], + "type": "object" + }, "Conditional": { "additionalProperties": false, "properties": { @@ -1256,6 +1351,17 @@ ], "description": "Additional levels of detail for grouping data in aggregate views and\nin line and area marks without mapping data to a specific visual channel." }, + "href": { + "anyOf": [ + { + "$ref": "#/definitions/FieldDefWithCondition" + }, + { + "$ref": "#/definitions/ValueDefWithCondition" + } + ], + "description": "A URL to load upon mouse click." + }, "opacity": { "anyOf": [ { @@ -1404,6 +1510,17 @@ ], "description": "Additional levels of detail for grouping data in aggregate views and\nin line and area marks without mapping data to a specific visual channel." }, + "href": { + "anyOf": [ + { + "$ref": "#/definitions/FieldDefWithCondition" + }, + { + "$ref": "#/definitions/ValueDefWithCondition" + } + ], + "description": "A URL to load upon mouse click." + }, "opacity": { "anyOf": [ { @@ -1668,6 +1785,64 @@ ], "type": "object" }, + "FieldDefWithCondition": { + "additionalProperties": false, + "description": "A FieldDef with Condition\n{\n condition: {value: ...},\n field: ...,\n ...\n}", + "properties": { + "aggregate": { + "$ref": "#/definitions/Aggregate", + "description": "Aggregation function for the field\n(e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).\n\n__Default value:__ `undefined` (None)" + }, + "bin": { + "anyOf": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/BinParams" + } + ], + "description": "A flag for binning a `quantitative` field, or [an object defining binning parameters](bin.html#params).\nIf `true`, default [binning parameters](bin.html) will be applied.\n\n__Default value:__ `false`" + }, + "condition": { + "anyOf": [ + { + "$ref": "#/definitions/Conditional" + }, + { + "items": { + "$ref": "#/definitions/Conditional" + }, + "type": "array" + } + ], + "description": "One or more value definition(s) with a selection predicate.\n\n__Note:__ A field definition's `condition` property can only contain [value definitions](encoding.html#value-def)\nsince Vega-Lite only allows at mosty one encoded field per encoding channel." + }, + "field": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/RepeatRef" + } + ], + "description": "__Required.__ A string defining the name of the field from which to pull a data value\nor an object defining iterated values from the [`repeat`](repeat.html) operator.\n\n__Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects (e.g., `\"field\": \"foo.bar\"` and `\"field\": \"foo['bar']\"`).\nIf field names contain dots or brackets but are not nested, you can use `\\\\` to escape dots and brackets (e.g., `\"a\\\\.b\"` and `\"a\\\\[0\\\\]\"`).\nSee more details about escaping in the [field documentation](field.html).\n\n__Note:__ `field` is not required if `aggregate` is `count`." + }, + "timeUnit": { + "$ref": "#/definitions/TimeUnit", + "description": "Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.\nor [a temporal field that gets casted as ordinal](type.html#cast).\n\n__Default value:__ `undefined` (None)" + }, + "type": { + "$ref": "#/definitions/Type", + "description": "The encoded field's type of measurement (`\"quantitative\"`, `\"temporal\"`, `\"ordinal\"`, or `\"nominal\"`)." + } + }, + "required": [ + "type" + ], + "type": "object" + }, "MarkPropFieldDefWithCondition": { "additionalProperties": false, "description": "A FieldDef with Condition\n{\n condition: {value: ...},\n field: ...,\n ...\n}", @@ -3084,6 +3259,48 @@ "description": "Default color. Note that `fill` and `stroke` have higher precedence than `color` and will override `color`.\n\n__Default value:__ `\"#4682b4\"`\n\n__Note:__ This property cannot be used in a [style config](mark.html#style-config).", "type": "string" }, + "cursor": { + "description": "The mouse cursor used over the mark. Any valid [CSS cursor type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.", + "enum": [ + "auto", + "default", + "none", + "context-menu", + "help", + "pointer", + "progress", + "wait", + "cell", + "crosshair", + "text", + "vertical-text", + "alias", + "copy", + "move", + "no-drop", + "not-allowed", + "e-resize", + "n-resize", + "ne-resize", + "nw-resize", + "s-resize", + "se-resize", + "sw-resize", + "w-resize", + "ew-resize", + "ns-resize", + "nesw-resize", + "nwse-resize", + "col-resize", + "row-resize", + "all-scroll", + "zoom-in", + "zoom-out", + "grab", + "grabbing" + ], + "type": "string" + }, "dx": { "description": "The horizontal offset, in pixels, between the text label and its anchor point. The offset is applied after rotation by the _angle_ property.", "type": "number" @@ -3130,6 +3347,11 @@ ], "description": "The font weight (e.g., `\"bold\"`)." }, + "href": { + "description": "A URL to load upon mouse click. If defined, the mark acts as a hyperlink.", + "format": "uri", + "type": "string" + }, "interpolate": { "$ref": "#/definitions/Interpolate", "description": "The line interpolation method to use for line and area marks. One of the following:\n- `\"linear\"`: piecewise linear segments, as in a polyline.\n- `\"linear-closed\"`: close the linear segments to form a polygon.\n- `\"step\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"step-before\"`: alternate between vertical and horizontal segments, as in a step function.\n- `\"step-after\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"basis\"`: a B-spline, with control point duplication on the ends.\n- `\"basis-open\"`: an open B-spline; may not intersect the start or end.\n- `\"basis-closed\"`: a closed B-spline, as in a loop.\n- `\"cardinal\"`: a Cardinal spline, with control point duplication on the ends.\n- `\"cardinal-open\"`: an open Cardinal spline; may not intersect the start or end, but will intersect other control points.\n- `\"cardinal-closed\"`: a closed Cardinal spline, as in a loop.\n- `\"bundle\"`: equivalent to basis, except the tension parameter is used to straighten the spline.\n- `\"monotone\"`: cubic interpolation that preserves monotonicity in y." @@ -3230,6 +3452,48 @@ "description": "Default color. Note that `fill` and `stroke` have higher precedence than `color` and will override `color`.\n\n__Default value:__ `\"#4682b4\"`\n\n__Note:__ This property cannot be used in a [style config](mark.html#style-config).", "type": "string" }, + "cursor": { + "description": "The mouse cursor used over the mark. Any valid [CSS cursor type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.", + "enum": [ + "auto", + "default", + "none", + "context-menu", + "help", + "pointer", + "progress", + "wait", + "cell", + "crosshair", + "text", + "vertical-text", + "alias", + "copy", + "move", + "no-drop", + "not-allowed", + "e-resize", + "n-resize", + "ne-resize", + "nw-resize", + "s-resize", + "se-resize", + "sw-resize", + "w-resize", + "ew-resize", + "ns-resize", + "nesw-resize", + "nwse-resize", + "col-resize", + "row-resize", + "all-scroll", + "zoom-in", + "zoom-out", + "grab", + "grabbing" + ], + "type": "string" + }, "dx": { "description": "The horizontal offset, in pixels, between the text label and its anchor point. The offset is applied after rotation by the _angle_ property.", "type": "number" @@ -3276,6 +3540,11 @@ ], "description": "The font weight (e.g., `\"bold\"`)." }, + "href": { + "description": "A URL to load upon mouse click. If defined, the mark acts as a hyperlink.", + "format": "uri", + "type": "string" + }, "interpolate": { "$ref": "#/definitions/Interpolate", "description": "The line interpolation method to use for line and area marks. One of the following:\n- `\"linear\"`: piecewise linear segments, as in a polyline.\n- `\"linear-closed\"`: close the linear segments to form a polygon.\n- `\"step\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"step-before\"`: alternate between vertical and horizontal segments, as in a step function.\n- `\"step-after\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"basis\"`: a B-spline, with control point duplication on the ends.\n- `\"basis-open\"`: an open B-spline; may not intersect the start or end.\n- `\"basis-closed\"`: a closed B-spline, as in a loop.\n- `\"cardinal\"`: a Cardinal spline, with control point duplication on the ends.\n- `\"cardinal-open\"`: an open Cardinal spline; may not intersect the start or end, but will intersect other control points.\n- `\"cardinal-closed\"`: a closed Cardinal spline, as in a loop.\n- `\"bundle\"`: equivalent to basis, except the tension parameter is used to straighten the spline.\n- `\"monotone\"`: cubic interpolation that preserves monotonicity in y." @@ -4371,7 +4640,8 @@ "color", "opacity", "text", - "tooltip" + "tooltip", + "href" ], "type": "string" }, @@ -4576,6 +4846,48 @@ "description": "Default color. Note that `fill` and `stroke` have higher precedence than `color` and will override `color`.\n\n__Default value:__ `\"#4682b4\"`\n\n__Note:__ This property cannot be used in a [style config](mark.html#style-config).", "type": "string" }, + "cursor": { + "description": "The mouse cursor used over the mark. Any valid [CSS cursor type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.", + "enum": [ + "auto", + "default", + "none", + "context-menu", + "help", + "pointer", + "progress", + "wait", + "cell", + "crosshair", + "text", + "vertical-text", + "alias", + "copy", + "move", + "no-drop", + "not-allowed", + "e-resize", + "n-resize", + "ne-resize", + "nw-resize", + "s-resize", + "se-resize", + "sw-resize", + "w-resize", + "ew-resize", + "ns-resize", + "nesw-resize", + "nwse-resize", + "col-resize", + "row-resize", + "all-scroll", + "zoom-in", + "zoom-out", + "grab", + "grabbing" + ], + "type": "string" + }, "dx": { "description": "The horizontal offset, in pixels, between the text label and its anchor point. The offset is applied after rotation by the _angle_ property.", "type": "number" @@ -4622,6 +4934,11 @@ ], "description": "The font weight (e.g., `\"bold\"`)." }, + "href": { + "description": "A URL to load upon mouse click. If defined, the mark acts as a hyperlink.", + "format": "uri", + "type": "string" + }, "interpolate": { "$ref": "#/definitions/Interpolate", "description": "The line interpolation method to use for line and area marks. One of the following:\n- `\"linear\"`: piecewise linear segments, as in a polyline.\n- `\"linear-closed\"`: close the linear segments to form a polygon.\n- `\"step\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"step-before\"`: alternate between vertical and horizontal segments, as in a step function.\n- `\"step-after\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"basis\"`: a B-spline, with control point duplication on the ends.\n- `\"basis-open\"`: an open B-spline; may not intersect the start or end.\n- `\"basis-closed\"`: a closed B-spline, as in a loop.\n- `\"cardinal\"`: a Cardinal spline, with control point duplication on the ends.\n- `\"cardinal-open\"`: an open Cardinal spline; may not intersect the start or end, but will intersect other control points.\n- `\"cardinal-closed\"`: a closed Cardinal spline, as in a loop.\n- `\"bundle\"`: equivalent to basis, except the tension parameter is used to straighten the spline.\n- `\"monotone\"`: cubic interpolation that preserves monotonicity in y." @@ -4727,6 +5044,48 @@ "description": "Default color. Note that `fill` and `stroke` have higher precedence than `color` and will override `color`.\n\n__Default value:__ `\"#4682b4\"`\n\n__Note:__ This property cannot be used in a [style config](mark.html#style-config).", "type": "string" }, + "cursor": { + "description": "The mouse cursor used over the mark. Any valid [CSS cursor type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.", + "enum": [ + "auto", + "default", + "none", + "context-menu", + "help", + "pointer", + "progress", + "wait", + "cell", + "crosshair", + "text", + "vertical-text", + "alias", + "copy", + "move", + "no-drop", + "not-allowed", + "e-resize", + "n-resize", + "ne-resize", + "nw-resize", + "s-resize", + "se-resize", + "sw-resize", + "w-resize", + "ew-resize", + "ns-resize", + "nesw-resize", + "nwse-resize", + "col-resize", + "row-resize", + "all-scroll", + "zoom-in", + "zoom-out", + "grab", + "grabbing" + ], + "type": "string" + }, "dx": { "description": "The horizontal offset, in pixels, between the text label and its anchor point. The offset is applied after rotation by the _angle_ property.", "type": "number" @@ -4773,6 +5132,11 @@ ], "description": "The font weight (e.g., `\"bold\"`)." }, + "href": { + "description": "A URL to load upon mouse click. If defined, the mark acts as a hyperlink.", + "format": "uri", + "type": "string" + }, "interpolate": { "$ref": "#/definitions/Interpolate", "description": "The line interpolation method to use for line and area marks. One of the following:\n- `\"linear\"`: piecewise linear segments, as in a polyline.\n- `\"linear-closed\"`: close the linear segments to form a polygon.\n- `\"step\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"step-before\"`: alternate between vertical and horizontal segments, as in a step function.\n- `\"step-after\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"basis\"`: a B-spline, with control point duplication on the ends.\n- `\"basis-open\"`: an open B-spline; may not intersect the start or end.\n- `\"basis-closed\"`: a closed B-spline, as in a loop.\n- `\"cardinal\"`: a Cardinal spline, with control point duplication on the ends.\n- `\"cardinal-open\"`: an open Cardinal spline; may not intersect the start or end, but will intersect other control points.\n- `\"cardinal-closed\"`: a closed Cardinal spline, as in a loop.\n- `\"bundle\"`: equivalent to basis, except the tension parameter is used to straighten the spline.\n- `\"monotone\"`: cubic interpolation that preserves monotonicity in y." @@ -5596,6 +5960,38 @@ ], "type": "object" }, + "ValueDefWithCondition": { + "additionalProperties": false, + "description": "A ValueDef with Condition\n{\n condition: {field: ...} | {value: ...},\n value: ...,\n}", + "properties": { + "condition": { + "anyOf": [ + { + "$ref": "#/definitions/Conditional" + }, + { + "$ref": "#/definitions/Conditional" + }, + { + "items": { + "$ref": "#/definitions/Conditional" + }, + "type": "array" + } + ], + "description": "A field definition or one or more value definition(s) with a selection predicate." + }, + "value": { + "description": "A constant value in visual domain.", + "type": [ + "number", + "string", + "boolean" + ] + } + }, + "type": "object" + }, "MarkPropValueDefWithCondition": { "additionalProperties": false, "description": "A ValueDef with Condition\n{\n condition: {field: ...} | {value: ...},\n value: ...,\n}", @@ -5934,6 +6330,48 @@ "$ref": "#/definitions/VerticalAlign", "description": "The vertical alignment of the text. One of `\"top\"`, `\"middle\"`, `\"bottom\"`.\n\n__Default value:__ `\"middle\"`" }, + "cursor": { + "description": "The mouse cursor used over the mark. Any valid [CSS cursor type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.", + "enum": [ + "auto", + "default", + "none", + "context-menu", + "help", + "pointer", + "progress", + "wait", + "cell", + "crosshair", + "text", + "vertical-text", + "alias", + "copy", + "move", + "no-drop", + "not-allowed", + "e-resize", + "n-resize", + "ne-resize", + "nw-resize", + "s-resize", + "se-resize", + "sw-resize", + "w-resize", + "ew-resize", + "ns-resize", + "nesw-resize", + "nwse-resize", + "col-resize", + "row-resize", + "all-scroll", + "zoom-in", + "zoom-out", + "grab", + "grabbing" + ], + "type": "string" + }, "dx": { "description": "The horizontal offset, in pixels, between the text label and its anchor point. The offset is applied after rotation by the _angle_ property.", "type": "number" @@ -5976,6 +6414,11 @@ ], "description": "The font weight (e.g., `\"bold\"`)." }, + "href": { + "description": "A URL to load upon mouse click. If defined, the mark acts as a hyperlink.", + "format": "uri", + "type": "string" + }, "interpolate": { "$ref": "#/definitions/Interpolate", "description": "The line interpolation method to use for line and area marks. One of the following:\n- `\"linear\"`: piecewise linear segments, as in a polyline.\n- `\"linear-closed\"`: close the linear segments to form a polygon.\n- `\"step\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"step-before\"`: alternate between vertical and horizontal segments, as in a step function.\n- `\"step-after\"`: alternate between horizontal and vertical segments, as in a step function.\n- `\"basis\"`: a B-spline, with control point duplication on the ends.\n- `\"basis-open\"`: an open B-spline; may not intersect the start or end.\n- `\"basis-closed\"`: a closed B-spline, as in a loop.\n- `\"cardinal\"`: a Cardinal spline, with control point duplication on the ends.\n- `\"cardinal-open\"`: an open Cardinal spline; may not intersect the start or end, but will intersect other control points.\n- `\"cardinal-closed\"`: a closed Cardinal spline, as in a loop.\n- `\"bundle\"`: equivalent to basis, except the tension parameter is used to straighten the spline.\n- `\"monotone\"`: cubic interpolation that preserves monotonicity in y." diff --git a/scripts/rename-schema.sh b/scripts/rename-schema.sh index 5beb9c08d3..472daed2b6 100755 --- a/scripts/rename-schema.sh +++ b/scripts/rename-schema.sh @@ -12,6 +12,9 @@ perl -pi -e s,'GenericHConcatSpec','HConcatSpec',g build/vega perl -pi -e s,'GenericUnitSpec','FacetedCompositeUnitSpecAlias',g build/vega-lite-schema.json perl -pi -e s,'GenericUnitSpec','CompositeUnitSpecAlias',g build/vega-lite-schema.json +perl -pi -e s,'FieldDefWithCondition','FieldDefWithCondition',g build/vega-lite-schema.json +perl -pi -e s,'ValueDefWithCondition','ValueDefWithCondition',g build/vega-lite-schema.json + perl -pi -e s,'FieldDefWithCondition','TextFieldDefWithCondition',g build/vega-lite-schema.json perl -pi -e s,'ValueDefWithCondition','TextValueDefWithCondition',g build/vega-lite-schema.json diff --git a/site/docs/encoding.md b/site/docs/encoding.md index 11363b1ea3..a4010edddb 100644 --- a/site/docs/encoding.md +++ b/site/docs/encoding.md @@ -30,6 +30,9 @@ The `encoding` property of a single view specification represents the mapping be "text": ..., "tooltip": ..., + // Hyperlink Channel + "href": ..., + // Order Channel "order": ..., @@ -51,7 +54,8 @@ The keys in the `encoding` object are encoding channels. Vega-lite supports the - [Position Channels](#position): `x`, `y`, `x2`, `y2` - [Mark Property Channels](#mark-prop): `color`, `opacity`, `shape`, `size` -- [Text and Tooltip Channels](#text): `text`, `tooltip` +- [Text and Tooltip Channels](#text): `text`, `tooltip` +- [Hyperlink Channel](#href): `href` - [Level of Detail Channel](#detail): `detail` - [Order Channel](#order): `order` - [Facet Channels](#facet): `row`, `column` @@ -204,6 +208,29 @@ In addition to the constant `value`, [value definitions](#value-def) of `text` a {% include table.html props="condition" source="TextValueDefWithCondition" %} +{:#href} +## Hyperlink Channel + +By setting the `href` channel, a mark becomes a hyperlink. The specified URL is loaded upon a muse click. The [`cursor` mark property](mark.html#hyperlink) can be set to `pointer` to serve as affordance for hyperlinks. + +{% include table.html props="href" source="Encoding" %} + + +{:#href-field-def} +### Hyperlink Field Definition + +In addition to [`field`](field.html), [`type`](type.html), [`bin`](bin.html), [`timeUnit`](timeunit.html) and [`aggregate`](aggregate.html), +[field definitions](#field-def) for the `href` channel can include the `condition` property to specify conditional logic. + +{% include table.html props="condition" source="FieldDefWithCondition" %} + +{:#href-value-def} +### Hyperlink Value Definition + +In addition to the constant `value`, [value definitions](#value-def) of the `href` channel can include the `condition` property to specify conditional logic. + +{% include table.html props="condition" source="ValueDefWithCondition" %} + {:#detail} ## Level of Detail Channel diff --git a/site/docs/mark/mark.md b/site/docs/mark/mark.md index 4b2037f299..cb0c76574c 100644 --- a/site/docs/mark/mark.md +++ b/site/docs/mark/mark.md @@ -125,11 +125,17 @@ The rest of this section describe groups of properties supported by the `mark` c {% include table.html props="opacity,fillOpacity,strokeOpacity" source="MarkConfig" %} - ### Stroke Style {% include table.html props="strokeWidth,strokeDash,strokeDashOffset" source="MarkConfig" %} +{:#hyperlink} +### Hyperlink Properties + +Marks can act as hyperlinks when the `href` property or [channel](encoding.html#href) is defined. A `cursor` property can also be provided to serve as affordance for the links. + +{% include table.html props="href,cursor" source="MarkConfig" %} + {:#interpolate} diff --git a/src/channel.ts b/src/channel.ts index 46e25808d2..369c9e5cb4 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -32,6 +32,7 @@ export namespace Channel { export const ORDER: 'order' = 'order'; export const DETAIL: 'detail' = 'detail'; export const TOOLTIP: 'tooltip' = 'tooltip'; + export const HREF: 'href' = 'href'; } export type Channel = keyof Encoding | keyof FacetMapping; @@ -50,6 +51,7 @@ export const DETAIL = Channel.DETAIL; export const ORDER = Channel.ORDER; export const OPACITY = Channel.OPACITY; export const TOOLTIP = Channel.TOOLTIP; +export const HREF = Channel.HREF; const UNIT_CHANNEL_INDEX: Flag> = { x: 1, @@ -63,7 +65,8 @@ const UNIT_CHANNEL_INDEX: Flag> = { opacity: 1, text: 1, detail: 1, - tooltip: 1 + tooltip: 1, + href: 1, }; const FACET_CHANNEL_INDEX: Flag> = { @@ -93,7 +96,7 @@ export const SINGLE_DEF_CHANNELS: SingleDefChannel[] = flagKeys(SINGLE_DEF_CHANN // Using the following line leads to TypeError: Cannot read property 'elementTypes' of undefined // when running the schema generator // export type SingleDefChannel = typeof SINGLE_DEF_CHANNELS[0]; -export type SingleDefChannel = 'x' | 'y' | 'x2' | 'y2' | 'row' | 'column' | 'size' | 'shape' | 'color' | 'opacity' | 'text' | 'tooltip'; +export type SingleDefChannel = 'x' | 'y' | 'x2' | 'y2' | 'row' | 'column' | 'size' | 'shape' | 'color' | 'opacity' | 'text' | 'tooltip' | 'href'; @@ -124,9 +127,10 @@ export type PositionScaleChannel = typeof POSITION_SCALE_CHANNELS[0]; // NON_POSITION_SCALE_CHANNEL = SCALE_CHANNELS without X, Y const { - // x2 and y2 share the same scale as x and y - // text and tooltip has format instead of scale - text: _t, tooltip: _tt, + // x2 and y2 share the same scale as x and y + // text and tooltip have format instead of scale, + // href has neither format, nor scale + text: _t, tooltip: _tt, href: _hr, // detail and order have no scale detail: _dd, order: _oo, ...NONPOSITION_SCALE_CHANNEL_INDEX @@ -159,7 +163,6 @@ export interface SupportedMark { line?: boolean; area?: boolean; text?: boolean; - tooltip?: boolean; } /** @@ -184,6 +187,7 @@ export function getSupportedMark(channel: Channel): SupportedMark { case COLOR: case DETAIL: case TOOLTIP: + case HREF: case ORDER: // TODO: revise (order might not support rect, which is not stackable?) case OPACITY: case ROW: @@ -223,9 +227,10 @@ export function rangeType(channel: Channel): RangeType { case ROW: case COLUMN: case SHAPE: - // TEXT and TOOLTIP have no scale but have discrete output + // TEXT, TOOLTIP, and HREF have no scale but have discrete output case TEXT: case TOOLTIP: + case HREF: return 'discrete'; // Color can be either continuous or discrete, depending on scale type. diff --git a/src/compile/mark/area.ts b/src/compile/mark/area.ts index 92035b1019..b0a6d60a89 100644 --- a/src/compile/mark/area.ts +++ b/src/compile/mark/area.ts @@ -14,6 +14,7 @@ export const area: MarkCompiler = { ...mixins.color(model), ...mixins.text(model, 'tooltip'), + ...mixins.text(model, 'href'), ...mixins.nonPosition('opacity', model), }; } diff --git a/src/compile/mark/bar.ts b/src/compile/mark/bar.ts index 3bc755c39e..c2d0532fc6 100644 --- a/src/compile/mark/bar.ts +++ b/src/compile/mark/bar.ts @@ -24,6 +24,7 @@ export const bar: MarkCompiler = { ...y(model, stack), ...mixins.color(model), ...mixins.text(model, 'tooltip'), + ...mixins.text(model, 'href'), ...mixins.nonPosition('opacity', model) }; } diff --git a/src/compile/mark/line.ts b/src/compile/mark/line.ts index 05eb69f058..56c62cc8e3 100644 --- a/src/compile/mark/line.ts +++ b/src/compile/mark/line.ts @@ -15,6 +15,7 @@ export const line: MarkCompiler = { ...mixins.pointPosition('y', model, ref.mid(height)), ...mixins.color(model), ...mixins.text(model, 'tooltip'), + ...mixins.text(model, 'href'), ...mixins.nonPosition('opacity', model), ...mixins.nonPosition('size', model, { vgChannel: 'strokeWidth' // VL's line size is strokeWidth diff --git a/src/compile/mark/mark.ts b/src/compile/mark/mark.ts index 6fefcfb690..d7bb85eeda 100644 --- a/src/compile/mark/mark.ts +++ b/src/compile/mark/mark.ts @@ -154,9 +154,10 @@ export function pathGroupingFields(encoding: Encoding): string[] { case 'y': case 'order': case 'tooltip': + case 'href': case 'x2': case 'y2': - // TODO: case 'href', 'cursor': + // TODO: case 'cursor': // text, shape, shouldn't be a part of line/area case 'text': diff --git a/src/compile/mark/mixins.ts b/src/compile/mark/mixins.ts index 197f1ecda3..614970dd56 100644 --- a/src/compile/mark/mixins.ts +++ b/src/compile/mark/mixins.ts @@ -100,7 +100,7 @@ function wrapCondition( } } -export function text(model: UnitModel, channel: 'text' | 'tooltip' = 'text') { +export function text(model: UnitModel, channel: 'text' | 'tooltip' | 'href' = 'text') { const channelDef = model.encoding[channel]; return wrapCondition(model, channelDef, channel, (cDef) => ref.text(cDef, model.config)); } diff --git a/src/compile/mark/point.ts b/src/compile/mark/point.ts index cba61122f2..abf9f56232 100644 --- a/src/compile/mark/point.ts +++ b/src/compile/mark/point.ts @@ -17,6 +17,7 @@ function encodeEntry(model: UnitModel, fixedShape?: 'circle' | 'square') { ...mixins.color(model), ...mixins.text(model, 'tooltip'), + ...mixins.text(model, 'href'), ...mixins.nonPosition('size', model), ...shapeMixins(model, config, fixedShape), ...mixins.nonPosition('opacity', model), diff --git a/src/compile/mark/rect.ts b/src/compile/mark/rect.ts index b69f4c2099..77d36bfc80 100644 --- a/src/compile/mark/rect.ts +++ b/src/compile/mark/rect.ts @@ -17,6 +17,7 @@ export const rect: MarkCompiler = { ...y(model), ...mixins.color(model), ...mixins.text(model, 'tooltip'), + ...mixins.text(model, 'href'), ...mixins.nonPosition('opacity', model), }; } diff --git a/src/compile/mark/rule.ts b/src/compile/mark/rule.ts index d8e88d4c86..2aff4237e3 100644 --- a/src/compile/mark/rule.ts +++ b/src/compile/mark/rule.ts @@ -23,6 +23,7 @@ export const rule: MarkCompiler = { ...mixins.color(model), ...mixins.text(model, 'tooltip'), + ...mixins.text(model, 'href'), ...mixins.nonPosition('opacity', model), ...mixins.nonPosition('size', model, { vgChannel: 'strokeWidth' // VL's rule size is strokeWidth diff --git a/src/compile/mark/text.ts b/src/compile/mark/text.ts index e875f0a27b..18662a27b3 100644 --- a/src/compile/mark/text.ts +++ b/src/compile/mark/text.ts @@ -26,6 +26,7 @@ export const text: MarkCompiler = { ...mixins.text(model), ...mixins.color(model), ...mixins.text(model, 'tooltip'), + ...mixins.text(model, 'href'), ...mixins.nonPosition('opacity', model), ...mixins.nonPosition('size', model, { vgChannel: 'fontSize' // VL's text size is fontSize diff --git a/src/compile/mark/tick.ts b/src/compile/mark/tick.ts index 9f4b63c427..b6dd450d73 100644 --- a/src/compile/mark/tick.ts +++ b/src/compile/mark/tick.ts @@ -28,6 +28,8 @@ export const tick: MarkCompiler = { [vgThicknessChannel]: {value: config.tick.thickness}, ...mixins.color(model), + ...mixins.text(model, 'tooltip'), + ...mixins.text(model, 'href'), ...mixins.nonPosition('opacity', model), }; } diff --git a/src/encoding.ts b/src/encoding.ts index 750a30420c..8f68654660 100644 --- a/src/encoding.ts +++ b/src/encoding.ts @@ -98,6 +98,11 @@ export interface Encoding { */ tooltip?: FieldDefWithCondition> | ValueDefWithCondition>; + /** + * A URL to load upon mouse click. + */ + href?: FieldDefWithCondition> | ValueDefWithCondition>; + /** * Stack order for stacked marks or order of data points in line marks for connected scatter plots. * diff --git a/src/fielddef.ts b/src/fielddef.ts index daf2ec9a2c..9dc0c7997e 100644 --- a/src/fielddef.ts +++ b/src/fielddef.ts @@ -535,6 +535,7 @@ export function channelCompatibility(fieldDef: FieldDef, channel: Channel case 'text': case 'detail': case 'tooltip': + case 'href': return COMPATIBLE; case 'opacity': diff --git a/src/vega.schema.ts b/src/vega.schema.ts index f30018f0e1..e5cb7f641f 100644 --- a/src/vega.schema.ts +++ b/src/vega.schema.ts @@ -193,7 +193,7 @@ export interface VgSignal { push?: string; } -export type VgEncodeChannel = 'x'|'x2'|'xc'|'width'|'y'|'y2'|'yc'|'height'|'opacity'|'fill'|'fillOpacity'|'stroke'|'strokeWidth'|'strokeOpacity'|'strokeDash'|'strokeDashOffset'|'cursor'|'clip'|'size'|'shape'|'path'|'innerRadius'|'outerRadius'|'startAngle'|'endAngle'|'interpolate'|'tension'|'orient'|'url'|'align'|'baseline'|'text'|'dir'|'ellipsis'|'limit'|'dx'|'dy'|'radius'|'theta'|'angle'|'font'|'fontSize'|'fontWeight'|'fontStyle'; +export type VgEncodeChannel = 'x'|'x2'|'xc'|'width'|'y'|'y2'|'yc'|'height'|'opacity'|'fill'|'fillOpacity'|'stroke'|'strokeWidth'|'strokeOpacity'|'strokeDash'|'strokeDashOffset'|'cursor'|'clip'|'size'|'shape'|'path'|'innerRadius'|'outerRadius'|'startAngle'|'endAngle'|'interpolate'|'tension'|'orient'|'url'|'align'|'baseline'|'text'|'dir'|'ellipsis'|'limit'|'dx'|'dy'|'radius'|'theta'|'angle'|'font'|'fontSize'|'fontWeight'|'fontStyle'|'tooltip'|'href'|'cursor'; export type VgEncodeEntry = { [k in VgEncodeChannel]?: VgValueRef | (VgValueRef & {test?: string})[]; }; @@ -1056,6 +1056,18 @@ export interface VgMarkConfig { * Placeholder text if the `text` channel is not specified */ text?: string; + + /** + * A URL to load upon mouse click. If defined, the mark acts as a hyperlink. + * + * @format uri + */ + href?: string; + + /** + * The mouse cursor used over the mark. Any valid [CSS cursor type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used. + */ + cursor?: 'auto' | 'default' | 'none' | 'context-menu' | 'help' | 'pointer' | 'progress' | 'wait' | 'cell' | 'crosshair' | 'text' | 'vertical-text' | 'alias' | 'copy' | 'move' | 'no-drop' | 'not-allowed' | 'e-resize' | 'n-resize' | 'ne-resize' | 'nw-resize' | 's-resize' | 'se-resize' | 'sw-resize' | 'w-resize' | 'ew-resize' | 'ns-resize' | 'nesw-resize' | 'nwse-resize' | 'col-resize' | 'row-resize' | 'all-scroll' | 'zoom-in' | 'zoom-out' | 'grab' | 'grabbing'; } const VG_MARK_CONFIG_INDEX: Flag = { @@ -1084,17 +1096,18 @@ const VG_MARK_CONFIG_INDEX: Flag = { font: 1, fontSize: 1, fontWeight: 1, - fontStyle: 1 + fontStyle: 1, + cursor: 1, + href: 1, // commented below are vg channel that do not have mark config. // 'x'|'x2'|'xc'|'width'|'y'|'y2'|'yc'|'height' - // cursor: 1, // clip: 1, // dir: 1, // ellipsis: 1, // endAngle: 1, - // path: 1, // innerRadius: 1, // outerRadius: 1, + // path: 1, // startAngle: 1, // url: 1, }; diff --git a/test/channel.test.ts b/test/channel.test.ts index b1b787daaa..f22ca07c48 100644 --- a/test/channel.test.ts +++ b/test/channel.test.ts @@ -18,7 +18,7 @@ describe('channel', () => { describe('SCALE_CHANNELS', () => { it('should be UNIT_CHANNELS without X2, Y2, ORDER, DETAIL, TEXT, LABEL, TOOLTIP', () => { - assert.deepEqual(SCALE_CHANNELS, without(UNIT_CHANNELS, ['x2', 'y2', 'order', 'detail', 'text', 'label', 'tooltip'])); + assert.deepEqual(SCALE_CHANNELS, without(UNIT_CHANNELS, ['x2', 'y2', 'order', 'detail', 'text', 'label', 'tooltip', 'href'])); }); }); diff --git a/test/compile/mark/mark.test.ts b/test/compile/mark/mark.test.ts index 4449ddc52e..64b6ececa9 100644 --- a/test/compile/mark/mark.test.ts +++ b/test/compile/mark/mark.test.ts @@ -105,21 +105,6 @@ describe('Mark', function() { assert.equal(markGroup[0].from.data, 'main'); }); }); - - describe('Bar with tooltip', () => { - it('should pass tooltip value to encoding', () => { - const model = parseUnitModelWithScaleAndLayoutSize({ - "mark": "bar", - "encoding": { - "x": {"type": "quantitative", "field": "Cost__Other", "aggregate": "sum"}, - "y": {"bin": true, "type": "quantitative", "field": "Cost__Total_$"}, - "tooltip": {"value": "foo"} - } - }); - const markGroup = parseMarkGroup(model); - assert.equal(markGroup[0].encode.update.tooltip.value, 'foo'); - }); - }); }); describe('getPathSort', () => { diff --git a/test/compile/mark/point.test.ts b/test/compile/mark/point.test.ts index 08a9965f16..b3cf9d77d6 100644 --- a/test/compile/mark/point.test.ts +++ b/test/compile/mark/point.test.ts @@ -243,6 +243,34 @@ describe('Mark: Point', function() { }); }); + + describe('with tooltip', () => { + const model = parseUnitModelWithScaleAndLayoutSize({ + "mark": "point", + "encoding": { + "tooltip": {"value": "foo"} + } + }); + const props = point.encodeEntry(model); + + it('should pass tooltip value to encoding', () => { + assert.deepEqual(props.tooltip, {value: "foo"}); + }); + }); + + describe('with href', () => { + const model = parseUnitModelWithScaleAndLayoutSize({ + "mark": "point", + "encoding": { + "href": {"value": "https://idl.cs.washington.edu/"} + } + }); + const props = point.encodeEntry(model); + + it('should pass href value to encoding', () => { + assert.deepEqual(props.href, {value: 'https://idl.cs.washington.edu/'}); + }); + }); }); describe('Mark: Square', function() {