From 18f5c4a19478514a473bce86e65e85dc4005e9f1 Mon Sep 17 00:00:00 2001 From: Jae Sung Park Date: Thu, 22 Jun 2023 18:41:14 +0900 Subject: [PATCH] feat(legend): Intent to ship legend.item.interaction.dblclick - Implement double click interaction and option - Reenforce legend click interaction on API doc Close #1404 --- demo/chart.js | 4 +- demo/demo.js | 60 +++++++++++++++++++++++++++ src/ChartInternal/internals/legend.ts | 23 ++++++---- src/config/Options/common/legend.ts | 29 +++++++++++-- test/internals/legend-spec.ts | 25 +++++++++++ types/options.d.ts | 20 ++++++++- 6 files changed, 148 insertions(+), 13 deletions(-) diff --git a/demo/chart.js b/demo/chart.js index 900a85749..4d47886ae 100644 --- a/demo/chart.js +++ b/demo/chart.js @@ -157,7 +157,9 @@ var billboardDemo = { .replace(/([A-Z]+)/g, " $1"); // set description - this.$description.innerHTML = demos[type[0]][type[1]].description || ""; + let desc = demos[type[0]][type[1]]; + this.$description.innerHTML = desc.description || (Array.isArray(desc) && desc[0].description) || ""; + this.$codeArea.style.display = "block"; // remove selected class diff --git a/demo/demo.js b/demo/demo.js index 42dc85e2a..0a9d038ee 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -3194,6 +3194,66 @@ d3.select(".chart_area") } } }, + LegendItemInteraction: [ + { + description: "Single click + AltKey(Win)/optionKey(Mac)
or Double click legend item to show/hide data series", + options: { + title: { + text: "Single click" + }, + data: { + columns: [ + ["data1", 300, 350, 300], + ["data2", 130, 100, 140], + ["data3", 230, 200, 240] + ], + type: "line" + } + } + }, + { + options: { + title: { + text: "Double click" + }, + data: { + columns: [ + ["data1", 300, 350, 300], + ["data2", 130, 100, 140], + ["data3", 230, 200, 240] + ], + type: "line" + }, + legend: { + item: { + interaction: { + dblclick: true + } + } + } + } + }, + { + options: { + title: { + text: "Interaction=false" + }, + data: { + columns: [ + ["data1", 300, 350, 300], + ["data2", 130, 100, 140], + ["data3", 230, 200, 240] + ], + type: "line" + }, + legend: { + item: { + interaction: false + } + } + } + }, + ], LegendItemTileType: [ { options: { diff --git a/src/ChartInternal/internals/legend.ts b/src/ChartInternal/internals/legend.ts index bdf03403b..c5fada148 100644 --- a/src/ChartInternal/internals/legend.ts +++ b/src/ChartInternal/internals/legend.ts @@ -394,7 +394,7 @@ export default { const isTouch = state.inputType === "touch"; const hasGauge = $$.hasType("gauge"); const useCssRule = config.boost_useCssRule; - const hasInteraction = config.legend_item_interaction; + const interaction = config.legend_item_interaction; item .attr("class", function(id) { @@ -421,12 +421,21 @@ export default { } item - .on("click", hasInteraction || isFunction(config.legend_item_onclick) ? + .on(interaction.dblclick ? "dblclick" : "click", interaction || isFunction(config.legend_item_onclick) ? function(event, id) { if (!callFn(config.legend_item_onclick, api, id)) { - if (event.altKey) { - api.hide(); - api.show(id); + const {altKey, target, type} = event; + + if (type === "dblclick" || altKey) { + // when focused legend is clicked(with altKey or double clicked), reset all hiding. + if (state.hiddenTargetIds.length && + target.parentNode.getAttribute("class").indexOf($LEGEND.legendItemHidden) === -1 + ) { + api.show(); + } else { + api.hide(); + api.show(id); + } } else { api.toggle(id); @@ -439,7 +448,7 @@ export default { } : null); !isTouch && item - .on("mouseout", hasInteraction || isFunction(config.legend_item_onout) ? + .on("mouseout", interaction || isFunction(config.legend_item_onout) ? function(event, id) { if (!callFn(config.legend_item_onout, api, id)) { d3Select(this).classed($FOCUS.legendItemFocused, false); @@ -451,7 +460,7 @@ export default { $$.api.revert(); } } : null) - .on("mouseover", hasInteraction || isFunction(config.legend_item_onover) ? + .on("mouseover", interaction || isFunction(config.legend_item_onover) ? function(event, id) { if (!callFn(config.legend_item_onover, api, id)) { d3Select(this).classed($FOCUS.legendItemFocused, true); diff --git a/src/config/Options/common/legend.ts b/src/config/Options/common/legend.ts index 6b7030d73..26248bd55 100644 --- a/src/config/Options/common/legend.ts +++ b/src/config/Options/common/legend.ts @@ -40,21 +40,34 @@ export default { * - defines the max step the legend has (e.g. If 2 set and legend has 3 legend item, the legend 2 columns). * @property {boolean} [legend.equally=false] Set to all items have same width size. * @property {number} [legend.padding=0] Set padding value - * @property {boolean} [legend.item.interaction=true] Set legend item interaction.
+ * @property {boolean} [legend.item.interaction=true] Set legend item interaction. * - **NOTE:** * - This setting will not have effect on `.toggle()` method. * - `legend.item.onXXX` listener options will work if set, regardless of this option value. + * @property {boolean} [legend.item.interaction.dblclick=false] Set legend item to interact on double click. + * - **NOTE:** + * - Double clicking will make focused clicked dataseries only, hiding all others. + * - for single click case, `click + altKey(Win)/optionKey(Mac OS)` to have same effect. + * - To return initial state(which all dataseries are showing), double click current focused legend item again. + * - for single click case, `click + altKey(Win)/optionKey(Mac OS)` to have same effect. + * - In this case, default `click` interaction will be disabled. * @property {Function} [legend.item.onclick=undefined] Set click event handler to the legend item. + * - **NOTE:** + * - When set, default `click` interaction will be disabled. + * - When `interaction.dblclick=true` is set, will be called on double click. * @property {Function} [legend.item.onover=undefined] Set mouse/touch over event handler to the legend item. + * - **NOTE:** When set, default `mouseover` interaction will be disabled. * @property {Function} [legend.item.onout=undefined] Set mouse/touch out event handler to the legend item. + * - **NOTE:** When set, default `mouseout` interaction will be disabled. * @property {number} [legend.item.tile.width=10] Set width for 'rectangle' legend item tile element. - * @property {number} [legend.item.tile.height=10] ㄹ + * @property {number} [legend.item.tile.height=10] Set height for 'rectangle' legend item tile element. * @property {number} [legend.item.tile.r=5] Set the radius for 'circle' legend item tile type. * @property {string} [legend.item.tile.type="rectangle"] Set legend item shape type.
* - **Available Values:** * - circle * - rectangle * @property {boolean} [legend.usePoint=false] Whether to use custom points in legend. + * @see [Demo: item.interaction](https://naver.github.io/billboard.js/demo/#Legend.LegendItemInteraction) * @see [Demo: item.tile.type](https://naver.github.io/billboard.js/demo/#Legend.LegendItemTileType) * @see [Demo: position](https://naver.github.io/billboard.js/demo/#Legend.LegendPosition) * @see [Demo: contents.template](https://naver.github.io/billboard.js/demo/#Legend.LegendTemplate1) @@ -92,6 +105,13 @@ export default { * // will disable default interaction * interaction: false, * + * // set legend interact on double click + * // by double clicking, will make focused clicked dataseries only, hiding all others. + * interaction: { + * dblclick: true + * } + * + * // when set below callback, will disable corresponding default interactions * onclick: function(id) { ... }, * onover: function(id) { ... }, * onout: function(id) { ... }, @@ -120,7 +140,10 @@ export default { legend_inset_x: 10, legend_inset_y: 0, legend_inset_step: undefined, - legend_item_interaction: true, + legend_item_interaction: true, + legend_item_dblclick: false, legend_item_onclick: undefined, legend_item_onover: undefined, legend_item_onout: undefined, diff --git a/test/internals/legend-spec.ts b/test/internals/legend-spec.ts index 28adcdc90..1a5d6c901 100644 --- a/test/internals/legend-spec.ts +++ b/test/internals/legend-spec.ts @@ -9,6 +9,7 @@ import {expect} from "chai"; import {select as d3Select} from "d3-selection"; import util from "../assets/util"; import {$FOCUS, $LEGEND} from "../../src/config/classes"; +import {fireEvent} from "../assets/helper"; describe("LEGEND", () => { let chart; @@ -850,5 +851,29 @@ describe("LEGEND", () => { expect(item.style("cursor")).to.be.equal("pointer"); }); }); + + it("set options: legend.item.interaction.dblclik=true", () => { + args.legend.item.interaction = { + dblclick: true + }; + }); + + it("check dblclick interaction", () => { + const {$: {legend}, internal: {state}} = chart; + + chart.data().forEach(({id}) => { + const item = legend.select(`.bb-legend-item-${id}`).node(); + + // when double click + fireEvent(item, "dblclick", undefined, chart); + + expect(state.hiddenTargetIds.length && state.hiddenTargetIds.indexOf(id) === -1).to.be.true; + + // when double click again, it should return to initial state + fireEvent(item, "dblclick", undefined, chart); + + expect(state.hiddenTargetIds).to.be.empty; + }); + }); }); }); diff --git a/types/options.d.ts b/types/options.d.ts index 95949de54..8371385c2 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -475,22 +475,38 @@ export interface LegendOptions { * Set legend item interaction. * - **NOTE:** * - This setting will not have effect on `.toggle()` method. - * - `legend.item.onXXX` listener options will work if set, regardless of this option value. + * - `legend.item.onXXX` listener options will work if set, regardless of this option value. */ - interaction?: boolean; + interaction?: boolean | { + /** + * Set legend item to interact on double click. + * - **NOTE:** + * - Double clicking will make focused clicked dataseries only, hiding all others. + * - for single click case, `click + altKey(Win)/optionKey(Mac OS)` to have same effect. + * - To return initial state(which all dataseries are showing), double click current focused legend item again. + * - for single click case, `click + altKey(Win)/optionKey(Mac OS)` to have same effect. + * - In this case, default `click` interaction will be disabled. + */ + dblclick?: boolean; + }; /** * Set click event handler to the legend item. + * - **NOTE:** + * - When set, default `click` interaction will be disabled. + * - When `interaction.dblclick=true` is set, will be called on double click. */ onclick?(this: Chart, id: string): void; /** * Set mouseover event handler to the legend item. + * - **NOTE:** When set, default `mouseover` interaction will be disabled. */ onover?(this: Chart, id: string): void; /** * Set mouseout event handler to the legend item. + * - **NOTE:** When set, default `mouseout` interaction will be disabled. */ onout?(this: Chart, id: string): void; };