Skip to content

Commit f2c1864

Browse files
committed
#406: plotly.js v2.22.0: implement basic multi-legends.
- extend SubplotId with Legend case - getting, setting, and updating multiple Legend objects on the Layout - setting Legend anchor on traces
1 parent 2c3fb24 commit f2c1864

File tree

14 files changed

+164
-67
lines changed

14 files changed

+164
-67
lines changed

src/Plotly.NET/CSharpLayer/GenericChartExtensions.fs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,6 @@ module GenericChartExtensions =
461461
(
462462
[<Optional; DefaultParameterValue(null)>] ?Title: Title,
463463
[<Optional; DefaultParameterValue(null)>] ?ShowLegend: bool,
464-
[<Optional; DefaultParameterValue(null)>] ?Legend: Legend,
465464
[<Optional; DefaultParameterValue(null)>] ?Margin: Margin,
466465
[<Optional; DefaultParameterValue(null)>] ?AutoSize: bool,
467466
[<Optional; DefaultParameterValue(null)>] ?Width: int,
@@ -532,7 +531,6 @@ module GenericChartExtensions =
532531
|> Chart.withLayoutStyle (
533532
?Title = Title,
534533
?ShowLegend = ShowLegend,
535-
?Legend = Legend,
536534
?Margin = Margin,
537535
?AutoSize = AutoSize,
538536
?Width = Width,
@@ -611,11 +609,8 @@ module GenericChartExtensions =
611609

612610
// Set the LayoutGrid options of a Chart
613611
[<CompiledName("WithLegend")>]
614-
member this.WithLegend(legend: Legend) =
615-
let layout =
616-
GenericChart.getLayout this |> Layout.setLegend legend
617-
618-
GenericChart.setLayout layout this
612+
member this.WithLegend(legend: Legend, [<Optional; DefaultParameterValue(null)>] ?Id: int) =
613+
this |> Chart.withLegend (legend, ?Id = Id)
619614

620615
/// Sets a map for the given chart (will only work with traces supporting geo, e.g. choropleth, scattergeo)
621616
[<CompiledName("WithMap")>]

src/Plotly.NET/ChartAPI/Chart.fs

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ type Chart =
154154
static member withColorAxisAnchor(id: int) =
155155
fun (ch: GenericChart) -> ch |> GenericChart.mapTrace (Trace.setColorAxisAnchor id)
156156

157+
/// <summary>
158+
/// Sets the legend id for the chart's trace(s).
159+
/// </summary>
160+
/// <param name="id">The new Legend id for the chart's trace(s)</param>
161+
[<CompiledName("WithLegendAnchor")>]
162+
static member withLegendAnchor(id: int) =
163+
fun (ch: GenericChart) -> ch |> GenericChart.mapTrace (Trace.setLegendAnchor id)
164+
157165
/// <summary>
158166
/// Sets the marker for the chart's trace(s).
159167
/// </summary>
@@ -742,7 +750,6 @@ type Chart =
742750
/// </summary>
743751
/// <param name="Title">Sets the title of the layout.</param>
744752
/// <param name="ShowLegend">Determines whether or not a legend is drawn. Default is `true` if there is a trace to show and any of these: a) Two or more traces would by default be shown in the legend. b) One pie trace is shown in the legend. c) One trace is explicitly given with `showlegend: true`.</param>
745-
/// <param name="Legend">Sets the legend styles of the layout.</param>
746753
/// <param name="Margin">Sets the margins around the layout.</param>
747754
/// <param name="AutoSize">Determines whether or not a layout width or height that has been left undefined by the user is initialized on each relayout. Note that, regardless of this attribute, an undefined layout width or height is always initialized on the first call to plot.</param>
748755
/// <param name="Width">Sets the plot's width (in px).</param>
@@ -820,7 +827,6 @@ type Chart =
820827
(
821828
[<Optional; DefaultParameterValue(null)>] ?Title: Title,
822829
[<Optional; DefaultParameterValue(null)>] ?ShowLegend: bool,
823-
[<Optional; DefaultParameterValue(null)>] ?Legend: Legend,
824830
[<Optional; DefaultParameterValue(null)>] ?Margin: Margin,
825831
[<Optional; DefaultParameterValue(null)>] ?AutoSize: bool,
826832
[<Optional; DefaultParameterValue(null)>] ?Width: int,
@@ -900,7 +906,6 @@ type Chart =
900906
Layout.init (
901907
?Title = Title,
902908
?ShowLegend = ShowLegend,
903-
?Legend = Legend,
904909
?Margin = Margin,
905910
?AutoSize = AutoSize,
906911
?Width = Width,
@@ -2495,32 +2500,44 @@ type Chart =
24952500
ch |> Chart.withLayoutGrid grid)
24962501

24972502
/// <summary>
2498-
/// Sets the Legend for the chart's layout.
2503+
/// Sets the given Legend with the given id on the input chart's layout.
24992504
/// </summary>
2500-
/// <param name="legend">The new Legend for the chart's layout</param>
2501-
/// <param name="Combine">Whether or not to combine the objects if there is already a Legend object set (default is false)</param>
2505+
/// <param name="legend">The Legend to set on the chart's layout</param>
2506+
/// <param name="id">The target Legend id with which the Legend should be set.</param>
2507+
/// <param name="Combine">Whether or not to combine the objects if there is already an Legend set (default is false)</param>
25022508
[<CompiledName("SetLegend")>]
2503-
static member setLegend(legend: Legend, ?Combine: bool) =
2504-
let combine = defaultArg Combine false
2509+
static member setLegend
2510+
(
2511+
legend: Legend,
2512+
id: StyleParam.SubPlotId,
2513+
[<Optional; DefaultParameterValue(null)>] ?Combine: bool
2514+
) =
2515+
let combine = defaultArg Combine false
25052516

2506-
(fun (ch: GenericChart) ->
2507-
if combine then
2508-
ch |> GenericChart.mapLayout (Layout.updateLegend legend)
2509-
else
2510-
ch |> GenericChart.mapLayout (Layout.setLegend legend))
2517+
(fun (ch: GenericChart) ->
2518+
if combine then
2519+
ch |> GenericChart.mapLayout (Layout.updateLegendById(id, legend))
2520+
else
2521+
ch |> GenericChart.mapLayout (Layout.setLegend(id,legend))
2522+
)
25112523

25122524
/// <summary>
2513-
/// Sets the Legend for the chart's layout
2525+
/// Sets the given Legend on the input chart's layout, optionally passing a target Legend id.
25142526
///
2515-
/// If there is already a Legend set, the objects are combined.
2527+
/// If there is already an Legend set at the given id, the Legend objects are combined.
25162528
/// </summary>
2517-
/// <param name="legend">The new Legend for the chart's layout</param>
2529+
/// <param name="legend">The Legend to set on the chart's layout</param>
2530+
/// <param name="Id">The target Legend id with which the Legend should be set. Default is 1.</param>
25182531
[<CompiledName("WithLegend")>]
2519-
static member withLegend(legend: Legend) =
2520-
(fun (ch: GenericChart) -> ch |> Chart.setLegend (legend, true))
2532+
static member withLegend(legend: Legend, [<Optional; DefaultParameterValue(null)>] ?Id: int) =
2533+
let id =
2534+
Id |> Option.defaultValue 1 |> StyleParam.SubPlotId.Legend
2535+
2536+
fun (ch: GenericChart) ->
2537+
ch |> Chart.setLegend (legend, id, Combine = true)
25212538

25222539
/// <summary>
2523-
/// Sets the given Legend styles on the input chart's Legend.
2540+
/// Sets the given Legend styles on the input chart's Legend, optionally passing a target Legend id.
25242541
///
25252542
/// If there is already a Legend set , the styles are applied to it. If there is no Legend present, a new Legend object with the given styles will be set.
25262543
/// </summary>
@@ -2542,10 +2559,12 @@ type Chart =
25422559
/// <param name="TraceOrder">Determines the order at which the legend items are displayed. If "normal", the items are displayed top-to-bottom in the same order as the input data. If "reversed", the items are displayed in the opposite order as "normal". If "grouped", the items are displayed in groups (when a trace `legendgroup` is provided). if "grouped+reversed", the items are displayed in the opposite order as "grouped".</param>
25432560
/// <param name="UIRevision">Controls persistence of legend-driven changes in trace and pie label visibility. Defaults to `layout.uirevision`.</param>
25442561
/// <param name="VerticalAlign">Sets the vertical alignment of the symbols with respect to their associated text.</param>
2562+
/// <param name="Visible">Determines whether or not this legend is visible.</param>
25452563
/// <param name="X">Sets the x position (in normalized coordinates) of the legend. Defaults to "1.02" for vertical legends and defaults to "0" for horizontal legends.</param>
25462564
/// <param name="XAnchor">Sets the legend's horizontal position anchor. This anchor binds the `x` position to the "left", "center" or "right" of the legend. Value "auto" anchors legends to the right for `x` values greater than or equal to 2/3, anchors legends to the left for `x` values less than or equal to 1/3 and anchors legends with respect to their center otherwise.</param>
25472565
/// <param name="Y">Sets the y position (in normalized coordinates) of the legend. Defaults to "1" for vertical legends, defaults to "-0.1" for horizontal legends on graphs w/o range sliders and defaults to "1.1" for horizontal legends on graph with one or multiple range sliders.</param>
25482566
/// <param name="YAnchor">Sets the legend's vertical position anchor This anchor binds the `y` position to the "top", "middle" or "bottom" of the legend. Value "auto" anchors legends at their bottom for `y` values less than or equal to 1/3, anchors legends to at their top for `y` values greater than or equal to 2/3 and anchors legends with respect to their middle otherwise.</param>
2567+
/// <param name="Id">The target Legend id on which the styles should be applied. Default is 1.</param>
25492568
[<CompiledName("WithLegendStyle")>]
25502569
static member withLegendStyle
25512570
(
@@ -2567,10 +2586,12 @@ type Chart =
25672586
[<Optional; DefaultParameterValue(null)>] ?TraceOrder: StyleParam.TraceOrder,
25682587
[<Optional; DefaultParameterValue(null)>] ?UIRevision: string,
25692588
[<Optional; DefaultParameterValue(null)>] ?VerticalAlign: StyleParam.VerticalAlign,
2589+
[<Optional; DefaultParameterValue(null)>] ?Visible: bool,
25702590
[<Optional; DefaultParameterValue(null)>] ?X: float,
25712591
[<Optional; DefaultParameterValue(null)>] ?XAnchor: StyleParam.XAnchorPosition,
25722592
[<Optional; DefaultParameterValue(null)>] ?Y: float,
2573-
[<Optional; DefaultParameterValue(null)>] ?YAnchor: StyleParam.YAnchorPosition
2593+
[<Optional; DefaultParameterValue(null)>] ?YAnchor: StyleParam.YAnchorPosition,
2594+
[<Optional; DefaultParameterValue(null)>] ?Id: int
25742595
) =
25752596
(fun (ch: GenericChart) ->
25762597
let legend =
@@ -2593,13 +2614,14 @@ type Chart =
25932614
?TraceOrder = TraceOrder,
25942615
?UIRevision = UIRevision,
25952616
?VerticalAlign = VerticalAlign,
2617+
?Visible = Visible,
25962618
?X = X,
25972619
?XAnchor = XAnchor,
25982620
?Y = Y,
25992621
?YAnchor = YAnchor
26002622
)
26012623

2602-
ch |> Chart.withLegend legend)
2624+
ch |> Chart.withLegend(legend, ?Id = Id))
26032625

26042626
/// <summary>
26052627
///

src/Plotly.NET/CommonAbstractions/StyleParams.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ module StyleParam =
217217
| Scene of int
218218
| Carpet of string
219219
| Smith of int
220+
| Legend of int
220221

221222
static member toString =
222223
function
@@ -267,6 +268,11 @@ module StyleParam =
267268
else
268269
sprintf "smith%i" id
269270
| Carpet id -> id
271+
| Legend id ->
272+
if id < 2 then
273+
"legend"
274+
else
275+
sprintf "legend%i" id
270276

271277
static member convert = SubPlotId.toString >> box
272278
override this.ToString() = this |> SubPlotId.toString

src/Plotly.NET/Globals.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ open Giraffe.ViewEngine
77

88
/// The plotly js version loaded from cdn in rendered html docs
99
[<Literal>]
10-
let PLOTLYJS_VERSION = "2.21.0"
10+
let PLOTLYJS_VERSION = "2.22.0"
1111

1212
[<Literal>]
1313
let SCRIPT_TEMPLATE =

src/Plotly.NET/Layout/Layout.fs

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ type Layout() =
1515
/// </summary>
1616
/// <param name="Title">Sets the title of the layout.</param>
1717
/// <param name="ShowLegend">Determines whether or not a legend is drawn. Default is `true` if there is a trace to show and any of these: a) Two or more traces would by default be shown in the legend. b) One pie trace is shown in the legend. c) One trace is explicitly given with `showlegend: true`.</param>
18-
/// <param name="Legend">Sets the legend styles of the layout.</param>
1918
/// <param name="Margin">Sets the margins around the layout.</param>
2019
/// <param name="AutoSize">Determines whether or not a layout width or height that has been left undefined by the user is initialized on each relayout. Note that, regardless of this attribute, an undefined layout width or height is always initialized on the first call to plot.</param>
2120
/// <param name="Width">Sets the plot's width (in px).</param>
@@ -92,7 +91,6 @@ type Layout() =
9291
(
9392
[<Optional; DefaultParameterValue(null)>] ?Title: Title,
9493
[<Optional; DefaultParameterValue(null)>] ?ShowLegend: bool,
95-
[<Optional; DefaultParameterValue(null)>] ?Legend: Legend,
9694
[<Optional; DefaultParameterValue(null)>] ?Margin: Margin,
9795
[<Optional; DefaultParameterValue(null)>] ?AutoSize: bool,
9896
[<Optional; DefaultParameterValue(null)>] ?Width: int,
@@ -170,7 +168,6 @@ type Layout() =
170168
|> Layout.style (
171169
?Title = Title,
172170
?ShowLegend = ShowLegend,
173-
?Legend = Legend,
174171
?Margin = Margin,
175172
?AutoSize = AutoSize,
176173
?Width = Width,
@@ -250,7 +247,6 @@ type Layout() =
250247
/// </summary>
251248
/// <param name="Title">Sets the title of the layout.</param>
252249
/// <param name="ShowLegend">Determines whether or not a legend is drawn. Default is `true` if there is a trace to show and any of these: a) Two or more traces would by default be shown in the legend. b) One pie trace is shown in the legend. c) One trace is explicitly given with `showlegend: true`.</param>
253-
/// <param name="Legend">Sets the legend styles of the layout.</param>
254250
/// <param name="Margin">Sets the margins around the layout.</param>
255251
/// <param name="AutoSize">Determines whether or not a layout width or height that has been left undefined by the user is initialized on each relayout. Note that, regardless of this attribute, an undefined layout width or height is always initialized on the first call to plot.</param>
256252
/// <param name="Width">Sets the plot's width (in px).</param>
@@ -327,7 +323,6 @@ type Layout() =
327323
(
328324
[<Optional; DefaultParameterValue(null)>] ?Title: Title,
329325
[<Optional; DefaultParameterValue(null)>] ?ShowLegend: bool,
330-
[<Optional; DefaultParameterValue(null)>] ?Legend: Legend,
331326
[<Optional; DefaultParameterValue(null)>] ?Margin: Margin,
332327
[<Optional; DefaultParameterValue(null)>] ?AutoSize: bool,
333328
[<Optional; DefaultParameterValue(null)>] ?Width: int,
@@ -405,7 +400,6 @@ type Layout() =
405400

406401
Title |> DynObj.setValueOpt layout "title"
407402
ShowLegend |> DynObj.setValueOpt layout "showlegend"
408-
Legend |> DynObj.setValueOpt layout "legend"
409403
Margin |> DynObj.setValueOpt layout "margin"
410404
AutoSize |> DynObj.setValueOpt layout "autosize"
411405
Width |> DynObj.setValueOpt layout "width"
@@ -945,30 +939,57 @@ type Layout() =
945939
layout |> Layout.setLayoutGrid combined)
946940

947941
/// <summary>
948-
/// Returns the legend object of the given layout.
949-
///
950-
/// If there is no legend set, returns an empty Legend object.
942+
/// Returns Some(Legend) if there is an Legend object set on the layout with the given id, and None otherwise.
951943
/// </summary>
952-
/// <param name="layout">The layout to get the legend from</param>
953-
static member getLegend(layout: Layout) =
954-
layout |> Layout.tryGetTypedMember<Legend> "legend" |> Option.defaultValue (Legend.init ())
944+
/// <param name="id">The target Legend id</param>
945+
static member tryGetLegendById(id: StyleParam.SubPlotId) =
946+
(fun (layout: Layout) -> layout.TryGetTypedValue<Legend>(StyleParam.SubPlotId.toString id))
955947

956948
/// <summary>
957-
/// Returns a function that sets the Legend object of the given trace.
949+
/// Combines the given Legend object with the one already present on the layout.
958950
/// </summary>
959-
/// <param name="legend">The new Legend object</param>
960-
static member setLegend(legend: Legend) =
951+
/// <param name="id">The target Legend id</param>
952+
/// <param name="legend">The updated Legend object.</param>
953+
static member updateLegendById(id: StyleParam.SubPlotId, legend: Legend) =
961954
(fun (layout: Layout) ->
962-
layout.SetValue("legend", legend)
963-
layout)
955+
956+
match id with
957+
| StyleParam.SubPlotId.Legend _ ->
958+
959+
let legend' =
960+
match Layout.tryGetLegendById id layout with
961+
| Some l -> (DynObj.combine l legend) :?> Legend
962+
| None -> legend
963+
964+
legend' |> DynObj.setValue layout (StyleParam.SubPlotId.toString id)
965+
966+
layout
967+
| _ ->
968+
failwith
969+
$"{StyleParam.SubPlotId.toString id} is an invalid subplot id for setting a legend on layout")
964970

965971
/// <summary>
966-
/// Combines the given Legend object with the one already present on the layout.
972+
/// Returns the Legend object of the layout with the given id.
973+
///
974+
/// If there is no Legend set, returns an empty Legend object.
975+
/// </summary>
976+
/// <param name="id">The target Legend id</param>
977+
static member getLegendById(id: StyleParam.SubPlotId) =
978+
(fun (layout: Layout) -> layout |> Layout.tryGetLegendById id |> Option.defaultValue (Legend.init ()))
979+
980+
/// <summary>
981+
/// Sets a linear Legend object on the layout as a dynamic property with the given Legend id.
967982
/// </summary>
968-
/// <param name="legend">The updated Legend object</param>
969-
static member updateLegend(legend: Legend) =
983+
/// <param name="id">The Legend id of the new Legend</param>
984+
/// <param name="legend">The Legend to add to the layout.</param>
985+
static member setLegend(id: StyleParam.SubPlotId, legend: Legend) =
970986
(fun (layout: Layout) ->
971-
let combined =
972-
(DynObj.combine (layout |> Layout.getLegend) legend) :?> Legend
973987

974-
layout |> Layout.setLegend combined)
988+
match id with
989+
| StyleParam.SubPlotId.Legend _ ->
990+
legend |> DynObj.setValue layout (StyleParam.SubPlotId.toString id)
991+
layout
992+
993+
| _ ->
994+
failwith
995+
$"{StyleParam.SubPlotId.toString id} is an invalid subplot id for setting a Legend on layout")

0 commit comments

Comments
 (0)