Skip to content

Commit ffe000b

Browse files
committed
#476: First try at no-deps interactive formatting
1 parent 5b1d95f commit ffe000b

File tree

4 files changed

+162
-0
lines changed

4 files changed

+162
-0
lines changed

Diff for: src/Plotly.NET/ChartAPI/GenericChart.fs

+23
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ open System
55
open Newtonsoft.Json
66
open System.Runtime.CompilerServices
77
open Giraffe.ViewEngine
8+
open System
9+
open System.IO
10+
open System.Collections.Generic
811

912
/// Figure is a domain transfer object that can be used to serialize a Chart to JSON. It is used internally and for most use cases should not be used directly.
1013
///
@@ -55,6 +58,7 @@ type ChartDTO =
5558
/// - `Config` is an object that configures high level properties of the chart like making all chart elements editable or the tool bar on top
5659
///
5760
/// - `DisplayOptions` is an object that contains meta information about how the html document that contains the chart.
61+
[<TypeFormatterSourceAttribute(typeof<GenericChartFormatterSource>)>]
5862
type GenericChart =
5963
| Chart of data: Trace * layout: Layout * config: Config * displayOpts: DisplayOptions
6064
| MultiChart of data: Trace list * layout: Layout * config: Config * displayOpts: DisplayOptions
@@ -584,3 +588,22 @@ type GenericChart =
584588
match gChart with
585589
| Chart(trace, _, _, _) -> [ TraceID.ofTrace trace ]
586590
| MultiChart(traces, _, _, _) -> traces |> List.map TraceID.ofTrace
591+
592+
and GenericChartFormatter() =
593+
let mutable mimeType = "text/html"
594+
member this.MimeType
595+
with get() = mimeType
596+
and set(v) = mimeType <- v
597+
member this.Format(instance:obj, writer:TextWriter) =
598+
match instance with
599+
| :? GenericChart as c ->
600+
writer.Write("LOL!")
601+
true
602+
| _ -> false
603+
604+
and GenericChartFormatterSource =
605+
member this.CreateTypeFormatters() : seq<obj> =
606+
seq {
607+
yield GenericChartFormatter()
608+
}
609+

Diff for: src/Plotly.NET/InternalUtils.fs

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ open DynamicObj
55
open System.Runtime.InteropServices
66
open System.IO
77
open System.Reflection
8+
open System
89

910
let combineOptSeqs (first: seq<'A> option) (second: seq<'A> option) =
1011
match first, second with
@@ -129,3 +130,13 @@ module internal ChartIO =
129130
System.Diagnostics.Process.Start("open", path) |> ignore
130131
else
131132
invalidOp "Not supported OS platform"
133+
134+
[<AttributeUsage(AttributeTargets.Class)>]
135+
type internal TypeFormatterSourceAttribute(formatterSourceType: Type) =
136+
inherit Attribute()
137+
let mutable preferredMimeTypes : string[] = [||]
138+
member this.TypeFormatterSourceType = formatterSourceType
139+
member this.PreferredMimeTypes
140+
with get() = preferredMimeTypes
141+
and set(v) = preferredMimeTypes <- v
142+

Diff for: tests/InteractiveTests/Repack.ps1

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Clean up the previously-cached NuGet packages.
2+
# Lower-case is intentional (that's how nuget stores those packages).
3+
Remove-Item -Recurse ~\.nuget\packages\plotly.net* -Force
4+
5+
# build and pack Plotly.NET.Interactive
6+
dotnet pack ../../src/Plotly.NET/Plotly.NET.fsproj -tl -c Release -p:PackageVersion=0.0.1-dev -o "./pkg"
7+

Diff for: tests/InteractiveTests/TestNotebook.ipynb

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {
6+
"dotnet_interactive": {
7+
"language": "csharp"
8+
},
9+
"polyglot_notebook": {
10+
"kernelName": "csharp"
11+
}
12+
},
13+
"source": [
14+
"To reproduce the package, run `./Repack.ps1` in powershell. It will clean your cache in `~/.nuget/packages` and pack the library to `Plotly.NET/pkg` folder, which you should specify below (absolute paths only) in `#i` line.\n",
15+
"\n",
16+
"The version of the package is always `0.0.0-dev`."
17+
]
18+
},
19+
{
20+
"cell_type": "code",
21+
"execution_count": 1,
22+
"metadata": {
23+
"dotnet_interactive": {
24+
"language": "fsharp"
25+
}
26+
},
27+
"outputs": [
28+
{
29+
"data": {
30+
"text/html": [
31+
"<div><div><strong>Restore sources</strong><ul><li><span> C:/Users/schne/source/repos/plotly/Plotly.NET/pkg</span></li></ul></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Plotly.NET, 2.0.0-alpha2</span></li></ul></div></div>"
32+
]
33+
},
34+
"metadata": {},
35+
"output_type": "display_data"
36+
}
37+
],
38+
"source": [
39+
"// be advised, that you always should set absolute paths for local nuget packages - change this to reflect your own setup\n",
40+
"#i \"nuget: C:/Users/schne/source/repos/plotly/Plotly.NET/tests/Interactivetests/pkg\"\n",
41+
"#r \"nuget: Plotly.NET, 0.0.1-dev\""
42+
]
43+
},
44+
{
45+
"cell_type": "code",
46+
"execution_count": 2,
47+
"metadata": {
48+
"dotnet_interactive": {
49+
"language": "fsharp"
50+
}
51+
},
52+
"outputs": [
53+
{
54+
"data": {
55+
"text/html": [
56+
"<div><div id=\"4fc61fc7-1f1e-48f0-9aca-5da1084da281\"><!-- Plotly chart will be drawn inside this DIV --></div><script type=\"text/javascript\">\n",
57+
"var renderPlotly_4fc61fc71f1e48f09aca5da1084da281 = function() {\n",
58+
" var fsharpPlotlyRequire = requirejs.config({context:'fsharp-plotly',paths:{plotly:'https://cdn.plot.ly/plotly-2.18.1.min'}}) || require;\n",
59+
" fsharpPlotlyRequire(['plotly'], function(Plotly) {\n",
60+
" var data = [{\"type\":\"scatter\",\"mode\":\"markers\",\"x\":[1,3],\"y\":[2,4],\"marker\":{},\"line\":{}}];\n",
61+
" var layout = {\"width\":600,\"height\":600,\"template\":{\"layout\":{\"title\":{\"x\":0.05},\"font\":{\"color\":\"rgba(42, 63, 95, 1.0)\"},\"paper_bgcolor\":\"rgba(255, 255, 255, 1.0)\",\"plot_bgcolor\":\"rgba(229, 236, 246, 1.0)\",\"autotypenumbers\":\"strict\",\"colorscale\":{\"diverging\":[[0.0,\"#8e0152\"],[0.1,\"#c51b7d\"],[0.2,\"#de77ae\"],[0.3,\"#f1b6da\"],[0.4,\"#fde0ef\"],[0.5,\"#f7f7f7\"],[0.6,\"#e6f5d0\"],[0.7,\"#b8e186\"],[0.8,\"#7fbc41\"],[0.9,\"#4d9221\"],[1.0,\"#276419\"]],\"sequential\":[[0.0,\"#0d0887\"],[0.1111111111111111,\"#46039f\"],[0.2222222222222222,\"#7201a8\"],[0.3333333333333333,\"#9c179e\"],[0.4444444444444444,\"#bd3786\"],[0.5555555555555556,\"#d8576b\"],[0.6666666666666666,\"#ed7953\"],[0.7777777777777778,\"#fb9f3a\"],[0.8888888888888888,\"#fdca26\"],[1.0,\"#f0f921\"]],\"sequentialminus\":[[0.0,\"#0d0887\"],[0.1111111111111111,\"#46039f\"],[0.2222222222222222,\"#7201a8\"],[0.3333333333333333,\"#9c179e\"],[0.4444444444444444,\"#bd3786\"],[0.5555555555555556,\"#d8576b\"],[0.6666666666666666,\"#ed7953\"],[0.7777777777777778,\"#fb9f3a\"],[0.8888888888888888,\"#fdca26\"],[1.0,\"#f0f921\"]]},\"hovermode\":\"closest\",\"hoverlabel\":{\"align\":\"left\"},\"coloraxis\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}},\"geo\":{\"showland\":true,\"landcolor\":\"rgba(229, 236, 246, 1.0)\",\"showlakes\":true,\"lakecolor\":\"rgba(255, 255, 255, 1.0)\",\"subunitcolor\":\"rgba(255, 255, 255, 1.0)\",\"bgcolor\":\"rgba(255, 255, 255, 1.0)\"},\"mapbox\":{\"style\":\"light\"},\"polar\":{\"bgcolor\":\"rgba(229, 236, 246, 1.0)\",\"radialaxis\":{\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\",\"ticks\":\"\"},\"angularaxis\":{\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\",\"ticks\":\"\"}},\"scene\":{\"xaxis\":{\"ticks\":\"\",\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\",\"gridwidth\":2.0,\"zerolinecolor\":\"rgba(255, 255, 255, 1.0)\",\"backgroundcolor\":\"rgba(229, 236, 246, 1.0)\",\"showbackground\":true},\"yaxis\":{\"ticks\":\"\",\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\",\"gridwidth\":2.0,\"zerolinecolor\":\"rgba(255, 255, 255, 1.0)\",\"backgroundcolor\":\"rgba(229, 236, 246, 1.0)\",\"showbackground\":true},\"zaxis\":{\"ticks\":\"\",\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\",\"gridwidth\":2.0,\"zerolinecolor\":\"rgba(255, 255, 255, 1.0)\",\"backgroundcolor\":\"rgba(229, 236, 246, 1.0)\",\"showbackground\":true}},\"ternary\":{\"aaxis\":{\"ticks\":\"\",\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\"},\"baxis\":{\"ticks\":\"\",\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\"},\"caxis\":{\"ticks\":\"\",\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\"},\"bgcolor\":\"rgba(229, 236, 246, 1.0)\"},\"xaxis\":{\"title\":{\"standoff\":15},\"ticks\":\"\",\"automargin\":\"height+width+left+right+top+bottom\",\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\",\"zerolinecolor\":\"rgba(255, 255, 255, 1.0)\",\"zerolinewidth\":2.0},\"yaxis\":{\"title\":{\"standoff\":15},\"ticks\":\"\",\"automargin\":\"height+width+left+right+top+bottom\",\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\",\"zerolinecolor\":\"rgba(255, 255, 255, 1.0)\",\"zerolinewidth\":2.0},\"annotationdefaults\":{\"arrowcolor\":\"#2a3f5f\",\"arrowhead\":0,\"arrowwidth\":1},\"shapedefaults\":{\"line\":{\"color\":\"rgba(42, 63, 95, 1.0)\"}},\"colorway\":[\"rgba(99, 110, 250, 1.0)\",\"rgba(239, 85, 59, 1.0)\",\"rgba(0, 204, 150, 1.0)\",\"rgba(171, 99, 250, 1.0)\",\"rgba(255, 161, 90, 1.0)\",\"rgba(25, 211, 243, 1.0)\",\"rgba(255, 102, 146, 1.0)\",\"rgba(182, 232, 128, 1.0)\",\"rgba(255, 151, 255, 1.0)\",\"rgba(254, 203, 82, 1.0)\"]},\"data\":{\"bar\":[{\"marker\":{\"line\":{\"color\":\"rgba(229, 236, 246, 1.0)\",\"width\":0.5},\"pattern\":{\"fillmode\":\"overlay\",\"size\":10,\"solidity\":0.2}},\"error_x\":{\"color\":\"rgba(42, 63, 95, 1.0)\"},\"error_y\":{\"color\":\"rgba(42, 63, 95, 1.0)\"}}],\"barpolar\":[{\"marker\":{\"line\":{\"color\":\"rgba(229, 236, 246, 1.0)\",\"width\":0.5},\"pattern\":{\"fillmode\":\"overlay\",\"size\":10,\"solidity\":0.2}}}],\"carpet\":[{\"aaxis\":{\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\",\"endlinecolor\":\"rgba(42, 63, 95, 1.0)\",\"minorgridcolor\":\"rgba(255, 255, 255, 1.0)\",\"startlinecolor\":\"rgba(42, 63, 95, 1.0)\"},\"baxis\":{\"linecolor\":\"rgba(255, 255, 255, 1.0)\",\"gridcolor\":\"rgba(255, 255, 255, 1.0)\",\"endlinecolor\":\"rgba(42, 63, 95, 1.0)\",\"minorgridcolor\":\"rgba(255, 255, 255, 1.0)\",\"startlinecolor\":\"rgba(42, 63, 95, 1.0)\"}}],\"choropleth\":[{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"},\"colorscale\":[[0.0,\"#0d0887\"],[0.1111111111111111,\"#46039f\"],[0.2222222222222222,\"#7201a8\"],[0.3333333333333333,\"#9c179e\"],[0.4444444444444444,\"#bd3786\"],[0.5555555555555556,\"#d8576b\"],[0.6666666666666666,\"#ed7953\"],[0.7777777777777778,\"#fb9f3a\"],[0.8888888888888888,\"#fdca26\"],[1.0,\"#f0f921\"]]}],\"contour\":[{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"},\"colorscale\":[[0.0,\"#0d0887\"],[0.1111111111111111,\"#46039f\"],[0.2222222222222222,\"#7201a8\"],[0.3333333333333333,\"#9c179e\"],[0.4444444444444444,\"#bd3786\"],[0.5555555555555556,\"#d8576b\"],[0.6666666666666666,\"#ed7953\"],[0.7777777777777778,\"#fb9f3a\"],[0.8888888888888888,\"#fdca26\"],[1.0,\"#f0f921\"]]}],\"contourcarpet\":[{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}],\"heatmap\":[{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"},\"colorscale\":[[0.0,\"#0d0887\"],[0.1111111111111111,\"#46039f\"],[0.2222222222222222,\"#7201a8\"],[0.3333333333333333,\"#9c179e\"],[0.4444444444444444,\"#bd3786\"],[0.5555555555555556,\"#d8576b\"],[0.6666666666666666,\"#ed7953\"],[0.7777777777777778,\"#fb9f3a\"],[0.8888888888888888,\"#fdca26\"],[1.0,\"#f0f921\"]]}],\"heatmapgl\":[{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"},\"colorscale\":[[0.0,\"#0d0887\"],[0.1111111111111111,\"#46039f\"],[0.2222222222222222,\"#7201a8\"],[0.3333333333333333,\"#9c179e\"],[0.4444444444444444,\"#bd3786\"],[0.5555555555555556,\"#d8576b\"],[0.6666666666666666,\"#ed7953\"],[0.7777777777777778,\"#fb9f3a\"],[0.8888888888888888,\"#fdca26\"],[1.0,\"#f0f921\"]]}],\"histogram\":[{\"marker\":{\"pattern\":{\"fillmode\":\"overlay\",\"size\":10,\"solidity\":0.2}}}],\"histogram2d\":[{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"},\"colorscale\":[[0.0,\"#0d0887\"],[0.1111111111111111,\"#46039f\"],[0.2222222222222222,\"#7201a8\"],[0.3333333333333333,\"#9c179e\"],[0.4444444444444444,\"#bd3786\"],[0.5555555555555556,\"#d8576b\"],[0.6666666666666666,\"#ed7953\"],[0.7777777777777778,\"#fb9f3a\"],[0.8888888888888888,\"#fdca26\"],[1.0,\"#f0f921\"]]}],\"histogram2dcontour\":[{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"},\"colorscale\":[[0.0,\"#0d0887\"],[0.1111111111111111,\"#46039f\"],[0.2222222222222222,\"#7201a8\"],[0.3333333333333333,\"#9c179e\"],[0.4444444444444444,\"#bd3786\"],[0.5555555555555556,\"#d8576b\"],[0.6666666666666666,\"#ed7953\"],[0.7777777777777778,\"#fb9f3a\"],[0.8888888888888888,\"#fdca26\"],[1.0,\"#f0f921\"]]}],\"mesh3d\":[{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}],\"parcoords\":[{\"line\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"pie\":[{\"automargin\":true}],\"scatter\":[{\"marker\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"scatter3d\":[{\"marker\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}},\"line\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"scattercarpet\":[{\"marker\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"scattergeo\":[{\"marker\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"scattergl\":[{\"marker\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"scattermapbox\":[{\"marker\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"scatterpolar\":[{\"marker\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"scatterpolargl\":[{\"marker\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"scatterternary\":[{\"marker\":{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"}}}],\"surface\":[{\"colorbar\":{\"outlinewidth\":0.0,\"ticks\":\"\"},\"colorscale\":[[0.0,\"#0d0887\"],[0.1111111111111111,\"#46039f\"],[0.2222222222222222,\"#7201a8\"],[0.3333333333333333,\"#9c179e\"],[0.4444444444444444,\"#bd3786\"],[0.5555555555555556,\"#d8576b\"],[0.6666666666666666,\"#ed7953\"],[0.7777777777777778,\"#fb9f3a\"],[0.8888888888888888,\"#fdca26\"],[1.0,\"#f0f921\"]]}],\"table\":[{\"cells\":{\"fill\":{\"color\":\"rgba(235, 240, 248, 1.0)\"},\"line\":{\"color\":\"rgba(255, 255, 255, 1.0)\"}},\"header\":{\"fill\":{\"color\":\"rgba(200, 212, 227, 1.0)\"},\"line\":{\"color\":\"rgba(255, 255, 255, 1.0)\"}}}]}}};\n",
62+
" var config = {\"responsive\":true};\n",
63+
" Plotly.newPlot('4fc61fc7-1f1e-48f0-9aca-5da1084da281', data, layout, config);\n",
64+
" });\n",
65+
"};\n",
66+
"if ((typeof(requirejs) !== typeof(Function)) || (typeof(requirejs.config) !== typeof(Function))) {\n",
67+
" var script = document.createElement(\"script\");\n",
68+
" script.setAttribute(\"src\", \"https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js\");\n",
69+
" script.onload = function(){\n",
70+
" renderPlotly_4fc61fc71f1e48f09aca5da1084da281();\n",
71+
" };\n",
72+
" document.getElementsByTagName(\"head\")[0].appendChild(script);\n",
73+
"}\n",
74+
"else {\n",
75+
" renderPlotly_4fc61fc71f1e48f09aca5da1084da281();\n",
76+
"}\n",
77+
"</script><h1>lel!</h1></div>"
78+
]
79+
},
80+
"metadata": {},
81+
"output_type": "display_data"
82+
}
83+
],
84+
"source": [
85+
"open Plotly.NET\n",
86+
"open Giraffe.ViewEngine\n",
87+
"\n",
88+
"Chart.Point([1,2; 3,4])\n",
89+
"|> Chart.withDescription [\n",
90+
" h1 [] [str \"lel!\"]\n",
91+
"]"
92+
]
93+
}
94+
],
95+
"metadata": {
96+
"kernelspec": {
97+
"display_name": ".NET (C#)",
98+
"language": "C#",
99+
"name": ".net-csharp"
100+
},
101+
"polyglot_notebook": {
102+
"kernelInfo": {
103+
"defaultKernelName": "csharp",
104+
"items": [
105+
{
106+
"aliases": [],
107+
"name": "csharp"
108+
},
109+
{
110+
"aliases": [
111+
"frontend"
112+
],
113+
"name": "vscode"
114+
}
115+
]
116+
}
117+
}
118+
},
119+
"nbformat": 4,
120+
"nbformat_minor": 2
121+
}

0 commit comments

Comments
 (0)