diff --git a/__tests__/integration/snapshots/interaction/commits-point-legend-filter/step0.png b/__tests__/integration/snapshots/interaction/commits-point-legend-filter/step0.png new file mode 100644 index 0000000000..369851d925 Binary files /dev/null and b/__tests__/integration/snapshots/interaction/commits-point-legend-filter/step0.png differ diff --git a/__tests__/integration/snapshots/interaction/settle-weather-cell-brush-filter/step0.png b/__tests__/integration/snapshots/interaction/settle-weather-cell-brush-filter/step0.png index 90545715cc..6e04917664 100644 Binary files a/__tests__/integration/snapshots/interaction/settle-weather-cell-brush-filter/step0.png and b/__tests__/integration/snapshots/interaction/settle-weather-cell-brush-filter/step0.png differ diff --git a/__tests__/integration/snapshots/interaction/settle-weather-cell-brush-filter/step1.png b/__tests__/integration/snapshots/interaction/settle-weather-cell-brush-filter/step1.png index 90545715cc..6e04917664 100644 Binary files a/__tests__/integration/snapshots/interaction/settle-weather-cell-brush-filter/step1.png and b/__tests__/integration/snapshots/interaction/settle-weather-cell-brush-filter/step1.png differ diff --git a/__tests__/integration/snapshots/interaction/settle-weather-legend-filter/step0.png b/__tests__/integration/snapshots/interaction/settle-weather-legend-filter/step0.png new file mode 100644 index 0000000000..f608472ef4 Binary files /dev/null and b/__tests__/integration/snapshots/interaction/settle-weather-legend-filter/step0.png differ diff --git a/__tests__/integration/snapshots/interaction/settle-weather-legend-filter/step1.png b/__tests__/integration/snapshots/interaction/settle-weather-legend-filter/step1.png new file mode 100644 index 0000000000..b1e3489bb2 Binary files /dev/null and b/__tests__/integration/snapshots/interaction/settle-weather-legend-filter/step1.png differ diff --git a/__tests__/integration/snapshots/interaction/unemployment-choropleth/step0.png b/__tests__/integration/snapshots/interaction/unemployment-choropleth/step0.png index fc382871b3..96e7c7e2ab 100644 Binary files a/__tests__/integration/snapshots/interaction/unemployment-choropleth/step0.png and b/__tests__/integration/snapshots/interaction/unemployment-choropleth/step0.png differ diff --git a/__tests__/integration/snapshots/static/aaplLineMissingDataTrial.png b/__tests__/integration/snapshots/static/aaplLineMissingDataTrial.png index dfa86dbf46..f74de07b28 100644 Binary files a/__tests__/integration/snapshots/static/aaplLineMissingDataTrial.png and b/__tests__/integration/snapshots/static/aaplLineMissingDataTrial.png differ diff --git a/__tests__/integration/snapshots/static/barleyLineTrail.png b/__tests__/integration/snapshots/static/barleyLineTrail.png index d2df21dc25..ec6fb6db9a 100644 Binary files a/__tests__/integration/snapshots/static/barleyLineTrail.png and b/__tests__/integration/snapshots/static/barleyLineTrail.png differ diff --git a/__tests__/integration/snapshots/static/bodyPointScatterPlot.png b/__tests__/integration/snapshots/static/bodyPointScatterPlot.png index 591844e213..ca8ed606ef 100644 Binary files a/__tests__/integration/snapshots/static/bodyPointScatterPlot.png and b/__tests__/integration/snapshots/static/bodyPointScatterPlot.png differ diff --git a/__tests__/integration/snapshots/static/bodyPointScatterPlotOpacity.png b/__tests__/integration/snapshots/static/bodyPointScatterPlotOpacity.png index 1899e07e55..9108d5a367 100644 Binary files a/__tests__/integration/snapshots/static/bodyPointScatterPlotOpacity.png and b/__tests__/integration/snapshots/static/bodyPointScatterPlotOpacity.png differ diff --git a/__tests__/integration/snapshots/static/bodyPointScatterPlotSizeOpacity.png b/__tests__/integration/snapshots/static/bodyPointScatterPlotSizeOpacity.png index 0f4e7933f0..fe85840a3c 100644 Binary files a/__tests__/integration/snapshots/static/bodyPointScatterPlotSizeOpacity.png and b/__tests__/integration/snapshots/static/bodyPointScatterPlotSizeOpacity.png differ diff --git a/__tests__/integration/snapshots/static/cars3LineParallelHorizontal.png b/__tests__/integration/snapshots/static/cars3LineParallelHorizontal.png index 3b1fb71200..228a366f65 100644 Binary files a/__tests__/integration/snapshots/static/cars3LineParallelHorizontal.png and b/__tests__/integration/snapshots/static/cars3LineParallelHorizontal.png differ diff --git a/__tests__/integration/snapshots/static/cars3LineRadar.png b/__tests__/integration/snapshots/static/cars3LineRadar.png index be9e7d3182..ea3dc6150c 100644 Binary files a/__tests__/integration/snapshots/static/cars3LineRadar.png and b/__tests__/integration/snapshots/static/cars3LineRadar.png differ diff --git a/__tests__/integration/snapshots/static/commitsPointGrouped.png b/__tests__/integration/snapshots/static/commitsPointGrouped.png index 6f50d162aa..8d0ccf8e51 100644 Binary files a/__tests__/integration/snapshots/static/commitsPointGrouped.png and b/__tests__/integration/snapshots/static/commitsPointGrouped.png differ diff --git a/__tests__/integration/snapshots/static/commitsPointGroupedConstant.png b/__tests__/integration/snapshots/static/commitsPointGroupedConstant.png index d31b2a2a8d..01e196d7a2 100644 Binary files a/__tests__/integration/snapshots/static/commitsPointGroupedConstant.png and b/__tests__/integration/snapshots/static/commitsPointGroupedConstant.png differ diff --git a/__tests__/integration/snapshots/static/commitsPointGroupedLegendFlexCenter.png b/__tests__/integration/snapshots/static/commitsPointGroupedLegendFlexCenter.png index ad2ea3a82d..5cecd6c5a8 100644 Binary files a/__tests__/integration/snapshots/static/commitsPointGroupedLegendFlexCenter.png and b/__tests__/integration/snapshots/static/commitsPointGroupedLegendFlexCenter.png differ diff --git a/__tests__/integration/snapshots/static/commitsPointGroupedLegendFlexRight.png b/__tests__/integration/snapshots/static/commitsPointGroupedLegendFlexRight.png index bdc5002774..b7a0264872 100644 Binary files a/__tests__/integration/snapshots/static/commitsPointGroupedLegendFlexRight.png and b/__tests__/integration/snapshots/static/commitsPointGroupedLegendFlexRight.png differ diff --git a/__tests__/integration/snapshots/static/commitsPointGroupedLegendIndependent.png b/__tests__/integration/snapshots/static/commitsPointGroupedLegendIndependent.png index a7d95b4962..4b39f3f967 100644 Binary files a/__tests__/integration/snapshots/static/commitsPointGroupedLegendIndependent.png and b/__tests__/integration/snapshots/static/commitsPointGroupedLegendIndependent.png differ diff --git a/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionBottom.png b/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionBottom.png index 87217472e3..c5889c259d 100644 Binary files a/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionBottom.png and b/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionBottom.png differ diff --git a/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionLeft.png b/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionLeft.png index 450d0bc0fc..5f46a92dae 100644 Binary files a/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionLeft.png and b/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionLeft.png differ diff --git a/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionRight.png b/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionRight.png index c410398032..90f112437e 100644 Binary files a/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionRight.png and b/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionRight.png differ diff --git a/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionRightLength.png b/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionRightLength.png index 31338be65c..c4ec0cc45d 100644 Binary files a/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionRightLength.png and b/__tests__/integration/snapshots/static/commitsPointGroupedLegendPositionRightLength.png differ diff --git a/__tests__/integration/snapshots/static/countriesBubbleLegendSize.png b/__tests__/integration/snapshots/static/countriesBubbleLegendSize.png index 3c68491f19..05cb544883 100644 Binary files a/__tests__/integration/snapshots/static/countriesBubbleLegendSize.png and b/__tests__/integration/snapshots/static/countriesBubbleLegendSize.png differ diff --git a/__tests__/integration/snapshots/static/disastersPointBubble.png b/__tests__/integration/snapshots/static/disastersPointBubble.png index 524831bd88..afeaa0cc2b 100644 Binary files a/__tests__/integration/snapshots/static/disastersPointBubble.png and b/__tests__/integration/snapshots/static/disastersPointBubble.png differ diff --git a/__tests__/integration/snapshots/static/disastersPointBubbleLegendRight.png b/__tests__/integration/snapshots/static/disastersPointBubbleLegendRight.png index d80556450b..9bb893ce6a 100644 Binary files a/__tests__/integration/snapshots/static/disastersPointBubbleLegendRight.png and b/__tests__/integration/snapshots/static/disastersPointBubbleLegendRight.png differ diff --git a/__tests__/integration/snapshots/static/haleChoroplethWorld.png b/__tests__/integration/snapshots/static/haleChoroplethWorld.png index 3ccf3f3586..9f1342b7a0 100644 Binary files a/__tests__/integration/snapshots/static/haleChoroplethWorld.png and b/__tests__/integration/snapshots/static/haleChoroplethWorld.png differ diff --git a/__tests__/integration/snapshots/static/moviesPointBin.png b/__tests__/integration/snapshots/static/moviesPointBin.png index 86b52ec3ba..fe6da71c10 100644 Binary files a/__tests__/integration/snapshots/static/moviesPointBin.png and b/__tests__/integration/snapshots/static/moviesPointBin.png differ diff --git a/__tests__/integration/snapshots/static/moviesRectBin.png b/__tests__/integration/snapshots/static/moviesRectBin.png index 173f9e869f..5c9e1da97c 100644 Binary files a/__tests__/integration/snapshots/static/moviesRectBin.png and b/__tests__/integration/snapshots/static/moviesRectBin.png differ diff --git a/__tests__/integration/snapshots/static/moviesRectBinOpacity.png b/__tests__/integration/snapshots/static/moviesRectBinOpacity.png index f7332ca95e..71d2adad49 100644 Binary files a/__tests__/integration/snapshots/static/moviesRectBinOpacity.png and b/__tests__/integration/snapshots/static/moviesRectBinOpacity.png differ diff --git a/__tests__/integration/snapshots/static/question8IntervalRadialGradientColor.png b/__tests__/integration/snapshots/static/question8IntervalRadialGradientColor.png index 58e9ab7787..d0a6b82131 100644 Binary files a/__tests__/integration/snapshots/static/question8IntervalRadialGradientColor.png and b/__tests__/integration/snapshots/static/question8IntervalRadialGradientColor.png differ diff --git a/__tests__/integration/snapshots/static/salaryHeatmapScaleLinear.png b/__tests__/integration/snapshots/static/salaryHeatmapScaleLinear.png index b0c227480a..5878a5d3eb 100644 Binary files a/__tests__/integration/snapshots/static/salaryHeatmapScaleLinear.png and b/__tests__/integration/snapshots/static/salaryHeatmapScaleLinear.png differ diff --git a/__tests__/integration/snapshots/static/settleWeatherCellGrouped.png b/__tests__/integration/snapshots/static/settleWeatherCellGrouped.png index a64bb8cddd..b5a50d625d 100644 Binary files a/__tests__/integration/snapshots/static/settleWeatherCellGrouped.png and b/__tests__/integration/snapshots/static/settleWeatherCellGrouped.png differ diff --git a/__tests__/integration/snapshots/static/settleWeatherCellLineXY.png b/__tests__/integration/snapshots/static/settleWeatherCellLineXY.png index 21dffa8681..493ab48731 100644 Binary files a/__tests__/integration/snapshots/static/settleWeatherCellLineXY.png and b/__tests__/integration/snapshots/static/settleWeatherCellLineXY.png differ diff --git a/__tests__/integration/snapshots/static/stocksLineSeriesGradient.png b/__tests__/integration/snapshots/static/stocksLineSeriesGradient.png index 8f6433f802..2498f4420f 100644 Binary files a/__tests__/integration/snapshots/static/stocksLineSeriesGradient.png and b/__tests__/integration/snapshots/static/stocksLineSeriesGradient.png differ diff --git a/__tests__/integration/snapshots/static/unemploymentChoropleth.png b/__tests__/integration/snapshots/static/unemploymentChoropleth.png index aeeb2839e2..852becd95e 100644 Binary files a/__tests__/integration/snapshots/static/unemploymentChoropleth.png and b/__tests__/integration/snapshots/static/unemploymentChoropleth.png differ diff --git a/__tests__/integration/snapshots/static/vaccinesCellScaleRelation.png b/__tests__/integration/snapshots/static/vaccinesCellScaleRelation.png index 176cb07067..53cec4655d 100644 Binary files a/__tests__/integration/snapshots/static/vaccinesCellScaleRelation.png and b/__tests__/integration/snapshots/static/vaccinesCellScaleRelation.png differ diff --git a/__tests__/integration/snapshots/static/vaccinesCellScaleRelationAutoPaddingTickFilter.png b/__tests__/integration/snapshots/static/vaccinesCellScaleRelationAutoPaddingTickFilter.png index 9363757d14..cf1a72666d 100644 Binary files a/__tests__/integration/snapshots/static/vaccinesCellScaleRelationAutoPaddingTickFilter.png and b/__tests__/integration/snapshots/static/vaccinesCellScaleRelationAutoPaddingTickFilter.png differ diff --git a/__tests__/plots/interaction/commits-point-legend-filter.ts b/__tests__/plots/interaction/commits-point-legend-filter.ts new file mode 100644 index 0000000000..ec4b94b18f --- /dev/null +++ b/__tests__/plots/interaction/commits-point-legend-filter.ts @@ -0,0 +1,54 @@ +import { G2Spec } from '../../../src'; +import { CONTINUOUS_LEGEND_CLASS_NAME } from '../../../src/interaction/legendFilter'; + +export function commitsPointLegendFilter(): G2Spec { + return { + type: 'point', + height: 300, + inset: 10, + padding: 'auto', + frame: true, + data: { + type: 'fetch', + value: 'data/commits.csv', + }, + encode: { + x: (d) => d.time.getUTCHours(), + y: (d) => d.time.getUTCDay(), + size: 'count', + shape: 'point', + color: 'count', + }, + transform: [ + { type: 'group', size: 'sum', color: 'sum' }, + { type: 'sortY' }, + ], + scale: { + y: { type: 'point' }, + x: { tickCount: 24 }, + color: { palette: 'rdBu' }, + }, + axis: { + x: { title: 'time (hours)' }, + y: { title: 'time (day)', grid: true }, + }, + }; +} + +export function dispatchValueChange(legend, values) { + legend.setSelection(...values); +} + +commitsPointLegendFilter.steps = ({ canvas }) => { + const { document } = canvas; + const [legend] = document.getElementsByClassName( + CONTINUOUS_LEGEND_CLASS_NAME, + ); + return [ + { + changeState: () => { + dispatchValueChange(legend, [20, 60]); + }, + }, + ]; +}; diff --git a/__tests__/plots/interaction/index.ts b/__tests__/plots/interaction/index.ts index 8bcbd73fa8..940e3cab2e 100644 --- a/__tests__/plots/interaction/index.ts +++ b/__tests__/plots/interaction/index.ts @@ -65,3 +65,5 @@ export { weatherLineLegendMark } from './weather-line-legend-mark'; export { countriesAnnotationSliderFilter } from './countries-annotation-slider-filter'; export { unemploymentAreaLegendFilterPages } from './unemployment-area-legend-filter-pages'; export { mockAreaSliderFilterLabel } from './mock-area-slider-filter-label'; +export { commitsPointLegendFilter } from './commits-point-legend-filter'; +export { settleWeatherLegendFilter } from './seattle-weather-legend-filter'; diff --git a/__tests__/plots/interaction/seattle-weather-legend-filter.ts b/__tests__/plots/interaction/seattle-weather-legend-filter.ts new file mode 100644 index 0000000000..141b55c6c5 --- /dev/null +++ b/__tests__/plots/interaction/seattle-weather-legend-filter.ts @@ -0,0 +1,45 @@ +import { G2Spec } from '../../../src'; +import { CONTINUOUS_LEGEND_CLASS_NAME } from '../../../src/interaction/legendFilter'; +import { dispatchValueChange } from './commits-point-legend-filter'; + +export function settleWeatherLegendFilter(): G2Spec { + return { + type: 'cell', + height: 330, + data: { + type: 'fetch', + value: 'data/seattle-weather.csv', + }, + encode: { + x: (d) => new Date(d.date).getUTCDate(), + y: (d) => new Date(d.date).getUTCMonth(), + color: 'temp_max', + }, + transform: [{ type: 'group', color: 'max' }], + scale: { + x: { compare: (a, b) => +a - +b }, + y: { compare: (a, b) => +a - +b }, + }, + legend: { color: { layout: { justifyContent: 'flex-start' } } }, + style: { inset: 0.5 }, + }; +} + +settleWeatherLegendFilter.steps = ({ canvas }) => { + const { document } = canvas; + const [legend] = document.getElementsByClassName( + CONTINUOUS_LEGEND_CLASS_NAME, + ); + return [ + { + changeState: () => { + dispatchValueChange(legend, [10, 30]); + }, + }, + { + changeState: () => { + dispatchValueChange(legend, [5, 36.4]); + }, + }, + ]; +}; diff --git a/__tests__/plots/static/commits-point-grouped.ts b/__tests__/plots/static/commits-point-grouped.ts index 5d74787d04..ca064b1f50 100644 --- a/__tests__/plots/static/commits-point-grouped.ts +++ b/__tests__/plots/static/commits-point-grouped.ts @@ -28,11 +28,5 @@ export function commitsPointGrouped(): G2Spec { x: { title: 'time (hours)' }, y: { title: 'time (day)', grid: true }, }, - // viewStyle: { - // viewFill: '#4e79a7', - // plotFill: '#f28e2c', - // mainFill: '#e15759', - // contentFill: '#76b7b2', - // }, }; } diff --git a/__tests__/plots/static/seattle-weather-cell-grouped.ts b/__tests__/plots/static/seattle-weather-cell-grouped.ts index cbffe3744b..a44ec89c84 100644 --- a/__tests__/plots/static/seattle-weather-cell-grouped.ts +++ b/__tests__/plots/static/seattle-weather-cell-grouped.ts @@ -8,26 +8,17 @@ export function settleWeatherCellGrouped(): G2Spec { type: 'fetch', value: 'data/seattle-weather.csv', }, - transform: [{ type: 'group', color: 'max' }], encode: { x: (d) => new Date(d.date).getUTCDate(), y: (d) => new Date(d.date).getUTCMonth(), color: 'temp_max', }, - legend: { - color: { - layout: { - justifyContent: 'flex-start', - }, - }, - }, - style: { - inset: 0.5, - }, + transform: [{ type: 'group', color: 'max' }], scale: { - color: { - palette: 'gnBu', - }, + x: { compare: (a, b) => +a - +b }, + y: { compare: (a, b) => +a - +b }, }, + legend: { color: { layout: { justifyContent: 'flex-start' } } }, + style: { inset: 0.5 }, }; } diff --git a/package.json b/package.json index 7933af8d11..4ab0259a1d 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@antv/g": "^5.18.6", "@antv/g-canvas": "^1.11.5", "@antv/g-plugin-dragndrop": "^1.8.3", - "@antv/gui": "0.5.0-alpha.26", + "@antv/gui": "0.5.0-alpha.28", "@antv/path-util": "^3.0.1", "@antv/scale": "^0.4.7", "@antv/util": "^3.3.2", diff --git a/src/component/legendContinuous.ts b/src/component/legendContinuous.ts index ae3fd6ae89..f52534a390 100644 --- a/src/component/legendContinuous.ts +++ b/src/component/legendContinuous.ts @@ -1,6 +1,6 @@ import { DisplayObject, parseColor } from '@antv/g'; import { Continuous } from '@antv/gui'; -import { Quantile, Quantize, Threshold } from '@antv/scale'; +import { Constant, Quantile, Quantize, Threshold } from '@antv/scale'; import { format } from 'd3-format'; import type { FlexLayout, @@ -43,12 +43,9 @@ type Config = { color: string[]; data: any[]; labelFilter?: (datum: any, index: number) => boolean; + domain?: [number, number]; }; -function calculateFinalSize(size: number, defaultSize: number): number { - return Math.min(size, defaultSize); -} - function updateShapeDimensions( shape: Shape, finalSize: number, @@ -154,8 +151,14 @@ function getLinearConfig( const scale = colorScale || createColorScale(definedScale, defaultColor); const [min, max] = rangeOf(scale); + const [domainMin, domainMax] = rangeOf( + [colorScale, sizeScale, opacityScale] + .filter((d) => d !== undefined) + .find((d) => !(d instanceof Constant)), + ); return { ...shape, + domain: [domainMin, domainMax], data: scale.getTicks().map((value) => ({ value })), color: new Array(length).fill(0).map((d, i) => { const value = ((max - min) / (length - 1)) * i + min; @@ -269,6 +272,7 @@ export const LegendContinuous: GCC = (options) => { layoutWrapper.appendChild( new Continuous({ + className: 'legend-continuous', style: finalStyle, }), ); diff --git a/src/interaction/legendFilter.ts b/src/interaction/legendFilter.ts index 2ad3a495ba..11c594a138 100644 --- a/src/interaction/legendFilter.ts +++ b/src/interaction/legendFilter.ts @@ -1,10 +1,12 @@ import { DisplayObject } from '@antv/g'; -import { deepMix } from '@antv/util'; +import { deepMix, throttle } from '@antv/util'; import { subObject } from '../utils/helper'; import { useState, setCursor, restoreCursor } from './utils'; export const CATEGORY_LEGEND_CLASS_NAME = 'legend-category'; +export const CONTINUOUS_LEGEND_CLASS_NAME = 'legend-continuous'; + export const LEGEND_ITEMS_CLASS_NAME = 'items-item'; export const LEGEND_MAKER_CLASS_NAME = 'legend-category-item-marker'; @@ -27,6 +29,10 @@ export function legendsOf(root) { return root.getElementsByClassName(CATEGORY_LEGEND_CLASS_NAME); } +export function legendsContinuousOf(root) { + return root.getElementsByClassName(CONTINUOUS_LEGEND_CLASS_NAME); +} + export function dataOf(root) { // legend -> layout -> container let parent = root.parentNode; @@ -44,7 +50,7 @@ export function attributesOf(root) { return child.attributes; } -function legendFilter( +function legendFilterOrdinal( root: DisplayObject, { legends, // given the root of chart returns legends to be manipulated @@ -175,61 +181,102 @@ function legendFilter( }; } +function legendFilterContinuous(_, { legend, filter, emitter, channel }) { + const onValueChange = ({ detail: { value } }) => { + filter(value); + emitter.emit({ + nativeEvent: true, + data: { + channel, + values: value, + }, + }); + }; + legend.addEventListener('valuechange', onValueChange); + return () => { + legend.removeEventListener('valuechange', onValueChange); + }; +} + export function LegendFilter() { return (context, _, emitter) => { const { container, view, options: viewOptions, update } = context; - const legends = legendsOf(container); - - const filter = async (channel, value) => { - const { scale } = view; - const { [channel]: scaleOrdinal } = scale; - const { marks } = viewOptions; - // Add filter transform for every marks, - // which will skip for mark without color channel. - const newMarks = marks.map((mark) => { - if (mark.type === 'legends') return mark; - const { transform = [] } = mark; - const newTransform = [ - { type: 'filter', [channel]: value }, - ...transform, - ]; - return deepMix({}, mark, { - transform: newTransform, - //Set domain of scale to preserve legends. - scale: { - [channel]: { - domain: scaleOrdinal.getOptions().domain, - }, - }, - legend: { - [channel]: { preserve: true }, - }, + const legends = [ + ...legendsOf(container), + ...legendsContinuousOf(container), + ]; + + const filter = throttle( + async (channel, value, ordinal: boolean, channels) => { + const { marks } = viewOptions; + // Add filter transform for every marks, + // which will skip for mark without color channel. + const newMarks = marks.map((mark) => { + if (mark.type === 'legends') return mark; + + // Inset after aggregate transform, such as group, and bin. + const { transform = [] } = mark; + const index = transform.findIndex( + ({ type }) => type.startsWith('group') || type.startsWith('bin'), + ); + const newTransform = [...transform]; + newTransform.splice(index + 1, 0, { + type: 'filter', + [channel]: { value, ordinal }, + }); + + // Set domain of scale to preserve encoding. + const newScale = Object.fromEntries( + channels.map((channel) => [ + channel, + { domain: view.scale[channel].getOptions().domain }, + ]), + ); + + return deepMix({}, mark, { + transform: newTransform, + scale: newScale, + ...(!ordinal && { animate: false }), + legend: { [channel]: { preserve: true } }, + }); }); - }); - const newOptions = { - ...viewOptions, - marks: newMarks, - }; - return update(newOptions); - }; + const newOptions = { + ...viewOptions, + marks: newMarks, + }; + await update(newOptions); + }, + 50, + { trailing: true }, + ); const removes = legends.map((legend) => { const { name: channel, domain } = dataOf(legend).scales[0]; - return legendFilter(container, { - legends: itemsOf, - marker: markerOf, - label: labelOf, - datum: (d) => { - const { __data__: datum } = d; - const { index } = datum; - return domain[index]; - }, - filter: (value) => filter(channel, value), - state: legend.attributes.state, - channel, - emitter, - }); + const channels = dataOf(legend).scales.map((d) => d.name); + if (legend.className === CATEGORY_LEGEND_CLASS_NAME) { + return legendFilterOrdinal(container, { + legends: itemsOf, + marker: markerOf, + label: labelOf, + datum: (d) => { + const { __data__: datum } = d; + const { index } = datum; + return domain[index]; + }, + filter: (value) => filter(channel, value, true, channels), + state: legend.attributes.state, + channel, + emitter, + }); + } else { + return legendFilterContinuous(container, { + legend, + filter: (value) => filter(channel, value, false, channels), + emitter, + channel, + }); + } }); return () => { removes.forEach((remove) => remove()); diff --git a/src/transform/filter.ts b/src/transform/filter.ts index d6af63d658..a9fc86214e 100644 --- a/src/transform/filter.ts +++ b/src/transform/filter.ts @@ -6,6 +6,11 @@ import { columnOf } from './utils/helper'; export type FilterOptions = Omit; +function normalizeValue(value) { + if (typeof value === 'object') return [value.value, value.ordinal]; + else return [value, true]; +} + /** * The Filter transform filter channels. */ @@ -13,15 +18,21 @@ export const Filter: TC = (options = {}) => { return (I, mark) => { const { encode, data } = mark; const filters = Object.entries(options) - .map(([key, value]) => { + .map(([key, v]) => { const [V] = columnOf(encode, key); // Skip empty channel. if (!V) return null; + const [value, ordinal = true] = normalizeValue(v); if (typeof value === 'function') return (i) => value(V[i]); - const expectedValues = Array.isArray(value) ? value : [value]; - // Skip empty expected values. - if (expectedValues.length === 0) return null; - return (i) => expectedValues.includes(V[i]); + if (ordinal) { + const expectedValues = Array.isArray(value) ? value : [value]; + // Skip empty expected values. + if (expectedValues.length === 0) return null; + return (i) => expectedValues.includes(V[i]); + } else { + const [start, end] = value; + return (i) => V[i] >= start && V[i] <= end; + } }) .filter(defined);