diff --git a/demo/demo.js b/demo/demo.js index 4c5607d67..11cfe0b01 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -2877,6 +2877,51 @@ var demos = { }, Legend: { + CustomLegend: { + options: { + data: { + columns: [ + ["data1", 100], + ["data2", 300], + ["data3", 200] + ], + type: "pie" + }, + legend: { + show: false + } + }, + func: function(chart) { + function toggle(id) { chart.toggle(id); } + +d3.select(".chart_area") + .insert("div", ".chart") + .attr("class", "legend") + .selectAll("span") + .data(["data1", "data2", "data3"]) + .enter() + .append("span") + .attr('data-id', function(id) { + return id; + }) + .html(function(id) { + return id; + }) + .each(function(id) { + d3.select(this) + .style('background-color', chart.color(id)); + }) + .on("mouseover", function(event, id) { + chart.focus(id); + }) + .on("mouseout", function(event, id) { + chart.revert(); + }) + .on("click", function(event, id) { + chart.toggle(id); + }); + } + }, HideLegend: { options: { data: { @@ -2890,6 +2935,49 @@ var demos = { } } }, + LegendItemTileType: [ + { + options: { + data: { + columns: [ + ["data1", 100], + ["data2", 300], + ["data3", 200] + ], + type: "pie" + }, + legend: { + item: { + tile: { + type: "circle", + r: 7 + }, + } + } + }, + }, + { + options: { + data: { + columns: [ + ["data1", 100], + ["data2", 300], + ["data3", 200] + ], + type: "pie" + }, + legend: { + item: { + tile: { + type: "rectangle", + width: 15, + height: 15 + }, + } + } + } + } + ], LegendPosition: { options: { data: { @@ -2961,51 +3049,6 @@ var demos = { } } }, - CustomLegend: { - options: { - data: { - columns: [ - ["data1", 100], - ["data2", 300], - ["data3", 200] - ], - type: "pie" - }, - legend: { - show: false - } - }, - func: function(chart) { - function toggle(id) { chart.toggle(id); } - -d3.select(".chart_area") - .insert("div", ".chart") - .attr("class", "legend") - .selectAll("span") - .data(["data1", "data2", "data3"]) - .enter() - .append("span") - .attr('data-id', function(id) { - return id; - }) - .html(function(id) { - return id; - }) - .each(function(id) { - d3.select(this) - .style('background-color', chart.color(id)); - }) - .on("mouseover", function(event, id) { - chart.focus(id); - }) - .on("mouseout", function(event, id) { - chart.revert(); - }) - .on("click", function(event, id) { - chart.toggle(id); - }); - } - }, usePoint: { options: { data: { diff --git a/src/ChartInternal/internals/legend.ts b/src/ChartInternal/internals/legend.ts index 5b5d3541b..eae988388 100644 --- a/src/ChartInternal/internals/legend.ts +++ b/src/ChartInternal/internals/legend.ts @@ -454,15 +454,26 @@ export default { updateLegendElement(targetIds: string[], options): void { const $$ = this; const {config, state, $el: {legend}, $T} = $$; + const legendType = config.legend_item_tile_type; + const isRectangle = legendType !== "circle"; + const legendItemR = config.legend_item_tile_r; + + const itemTileSize = { + width: isRectangle ? config.legend_item_tile_width : legendItemR * 2, + height: isRectangle ? config.legend_item_tile_height : legendItemR * 2 + }; + const paddingTop = 4; const paddingRight = 10; const posMin = 10; - const tileWidth = config.legend_item_tile_width + 5; + const tileWidth = itemTileSize.width + 5; + let maxWidth = 0; let maxHeight = 0; let xForLegend; let yForLegend; let totalLength = 0; + const offsets = {}; const widths = {}; const heights = {}; @@ -570,10 +581,10 @@ export default { yForLegend = id => maxHeight * steps[id]; } - const xForLegendText = (id, i?: number) => xForLegend(id, i) + 4 + config.legend_item_tile_width; + const xForLegendText = (id, i?: number) => xForLegend(id, i) + 4 + itemTileSize.width; const xForLegendRect = (id, i?: number) => xForLegend(id, i); const x1ForLegendTile = (id, i?: number) => xForLegend(id, i) - 2; - const x2ForLegendTile = (id, i?: number) => xForLegend(id, i) - 2 + config.legend_item_tile_width; + const x2ForLegendTile = (id, i?: number) => xForLegend(id, i) - 2 + itemTileSize.width; const yForLegendText = (id, i?: number) => yForLegend(id, i) + 9; const yForLegendRect = (id, i?: number) => yForLegend(id, i) - 5; @@ -643,15 +654,26 @@ export default { return nodeName === "use" ? `#${state.datetimeId}-point${id}` : undefined; }); } else { - l.append("line") + l.append(isRectangle ? "line" : legendType) .attr("class", $LEGEND.legendItemTile) .style("stroke", getColor) .style("pointer-events", $$.getStylePropValue("none")) - .attr("x1", isLegendRightOrInset ? x1ForLegendTile : pos) - .attr("y1", isLegendRightOrInset ? pos : yForLegendTile) - .attr("x2", isLegendRightOrInset ? x2ForLegendTile : pos) - .attr("y2", isLegendRightOrInset ? pos : yForLegendTile) - .attr("stroke-width", config.legend_item_tile_height); + .call(selection => { + if (legendType === "circle") { + selection + .attr("r", legendItemR) + .style("fill", getColor) + .attr("cx", isLegendRightOrInset ? x2ForLegendTile : pos) + .attr("cy", isLegendRightOrInset ? pos : yForLegendTile); + } else if (isRectangle) { + selection + .attr("stroke-width", itemTileSize.height) + .attr("x1", isLegendRightOrInset ? x1ForLegendTile : pos) + .attr("y1", isLegendRightOrInset ? pos : yForLegendTile) + .attr("x2", isLegendRightOrInset ? x2ForLegendTile : pos) + .attr("y2", isLegendRightOrInset ? pos : yForLegendTile); + } + }); } // Set background for inset legend @@ -723,15 +745,28 @@ export default { .attr("height", height); }); } else { - const tiles = legend.selectAll(`line.${$LEGEND.legendItemTile}`) + const tiles = legend.selectAll(`.${$LEGEND.legendItemTile}`) .data(targetIdz); $T(tiles, withTransition) .style("stroke", getColor) - .attr("x1", x1ForLegendTile) - .attr("y1", yForLegendTile) - .attr("x2", x2ForLegendTile) - .attr("y2", yForLegendTile); + .call(selection => { + if (legendType === "circle") { + selection + .attr("cx", d => { + const x2 = x2ForLegendTile(d); + + return x2 - ((x2 - x1ForLegendTile(d)) / 2); + }) + .attr("cy", yForLegendTile); + } else if (isRectangle) { + selection + .attr("x1", x1ForLegendTile) + .attr("y1", yForLegendTile) + .attr("x2", x2ForLegendTile) + .attr("y2", yForLegendTile); + } + }); } if (background) { diff --git a/src/config/Options/common/legend.ts b/src/config/Options/common/legend.ts index 8d4bbd79d..20f154643 100644 --- a/src/config/Options/common/legend.ts +++ b/src/config/Options/common/legend.ts @@ -43,9 +43,15 @@ export default { * @property {Function} [legend.item.onclick=undefined] Set click event handler to the legend item. * @property {Function} [legend.item.onover=undefined] Set mouse/touch over event handler to the legend item. * @property {Function} [legend.item.onout=undefined] Set mouse/touch out event handler to the legend item. - * @property {number} [legend.item.tile.width=10] Set width of item tile element - * @property {number} [legend.item.tile.height=10] Set height of item tile element + * @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.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.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) * @see [Demo: usePoint](https://naver.github.io/billboard.js/demo/#Legend.usePoint) @@ -85,8 +91,15 @@ export default { * * // set tile's size * tile: { - * width: 20, + * // set tile type + * type: "circle" // or "rectangle" (default) + * + * // width & height, are only applicable for 'rectangle' legend type + * width: 15, * height: 15 + * + * // radis is only applicable for 'circle' legend type + * r: 10 * } * }, * usePoint: true @@ -108,5 +121,7 @@ export default { legend_padding: 0, legend_item_tile_width: 10, legend_item_tile_height: 10, + legend_item_tile_r: 5, + legend_item_tile_type: <"rectangle"|"circle"> "rectangle", legend_usePoint: false }; diff --git a/test/internals/legend-spec.ts b/test/internals/legend-spec.ts index c8a3d01e3..e378ff7ac 100644 --- a/test/internals/legend-spec.ts +++ b/test/internals/legend-spec.ts @@ -708,4 +708,62 @@ describe("LEGEND", () => { }, 50); }); }); + + describe("item.tile.type option", () => { + before(() => { + args = { + data: { + columns: [ + ["data1", 100], + ["data2", 300], + ["data3", 200] + ], + type: "pie", // for ESM specify as: pie() + }, + legend: { + item: { + tile: { + type: "circle" + }, + } + } + }; + }); + + it("should item tile's shapes are 'circle'?", () => { + const legendItems = chart.$.legend.selectAll("circle"); + + expect(legendItems.size()).to.be.equal(chart.data().length); + + legendItems.each(function() { + expect(+this.getAttribute("r")).to.be.equal(5); + }); + }); + + it("set options: legend.item.tile.r=7", () => { + args.legend.item.tile.r = 7; + }); + + it("check 'circle' item's radius", () => { + const legendItems = chart.$.legend.selectAll("circle"); + + expect(legendItems.size()).to.be.equal(chart.data().length); + + legendItems.each(function() { + expect(+this.getAttribute("r")).to.be.equal(args.legend.item.tile.r); + }); + }); + + it("set options: legend.item.tile='rectangle'", () => { + args.legend.item.tile = { + type: "rectangle" + }; + }); + + it("should item tile's shapes are 'rectangle'?", () => { + const legendItems = chart.$.legend.selectAll("line"); + + expect(legendItems.size()).to.be.equal(chart.data().length); + }); + }); }); diff --git a/types/options.d.ts b/types/options.d.ts index db4400c94..912cb4c1b 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -1000,14 +1000,24 @@ export interface LegendOptions { */ tile?: { /** - * Tile width. + * Set width for 'rectangle' legend item tile element. */ width?: number; /** - * Tile height + * Set height for 'rectangle' legend item tile element. */ height?: number; + + /** + * Set legend item shape type. + */ + type?: "circle" | "recntangle"; + + /** + * Set the radius for 'circle' legend item tile type. + */ + r?: number; }; /** * Set click event handler to the legend item.