diff --git a/assets/vega_lite/main.js b/assets/vega_lite/main.js index 8576047..6f3449a 100644 --- a/assets/vega_lite/main.js +++ b/assets/vega_lite/main.js @@ -120,15 +120,17 @@ export function init(ctx, data) { "https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap" ); - const { spec, datasets } = data; + const { spec, datasets, config } = data; if (!spec.data) { spec.data = { values: [] }; } + let theme = config.theme === "livebook" ? livebookTheme : {}; + const options = { actions: { export: true, source: false, compiled: false, editor: false }, - config: livebookTheme, + config: theme, }; vegaEmbed(ctx.root, spec, options) diff --git a/lib/kino/vega_lite.ex b/lib/kino/vega_lite.ex index 7d049bf..28ab569 100644 --- a/lib/kino/vega_lite.ex +++ b/lib/kino/vega_lite.ex @@ -40,7 +40,8 @@ defmodule Kino.VegaLite do def static(vl) when is_struct(vl, VegaLite) do data = %{ spec: VegaLite.to_spec(vl), - datasets: [] + datasets: [], + config: config() } Kino.JS.new(__MODULE__, data, @@ -51,6 +52,27 @@ defmodule Kino.VegaLite do ) end + @doc """ + Applies global configuration options for the VegaLite kinos. + + ## Options + + * `:theme` - the theme to be applied on the rendered VegaLite + charts. Currently the only supported theme is `:livebook`. If + set to `nil`, no theme is applied. Defaults to `:livebook`. + """ + @spec configure(keyword()) :: :ok + def configure(opts) do + opts = Keyword.validate!(opts, theme: :livebook) + + unless opts[:theme] in [nil, :livebook] do + raise ArgumentError, + "expected :theme to be either :livebook or nil, got: #{inspect(opts[:theme])}" + end + + Application.put_all_env(kino_vega_lite: opts) + end + @doc """ Renders and returns a new kino with the given VegaLite definition. @@ -164,7 +186,7 @@ defmodule Kino.VegaLite do @impl true def init(vl, ctx) do - {:ok, assign(ctx, vl: vl, datasets: %{})} + {:ok, assign(ctx, vl: vl, datasets: %{}, config: config())} end @compile {:no_warn_undefined, {VegaLite, :to_spec, 1}} @@ -173,7 +195,8 @@ defmodule Kino.VegaLite do def handle_connect(ctx) do data = %{ spec: VegaLite.to_spec(ctx.assigns.vl), - datasets: for({dataset, data} <- ctx.assigns.datasets, do: [dataset, data]) + datasets: for({dataset, data} <- ctx.assigns.datasets, do: [dataset, data]), + config: ctx.assigns.config } {:ok, data, ctx} @@ -231,4 +254,12 @@ defmodule Kino.VegaLite do :ok end end + + defp config do + default_config = [theme: :livebook] + + default_config + |> Keyword.merge(Application.get_all_env(:kino_vega_lite)) + |> Map.new() + end end diff --git a/test/kino/vega_lite_test.exs b/test/kino/vega_lite_test.exs index 6719c0c..62da2bf 100644 --- a/test/kino/vega_lite_test.exs +++ b/test/kino/vega_lite_test.exs @@ -93,6 +93,27 @@ defmodule Kino.VegaLiteTest do assert_broadcast_event(kino, "push", %{data: [], dataset: nil, window: 0}) end + test "configure/2" do + # with invalid theme + assert_raise ArgumentError, + "expected :theme to be either :livebook or nil, got: :invalid", + fn -> Kino.VegaLite.configure(theme: :invalid) end + + # with default theme + kino = start_kino() + + data = connect(kino) + assert %{config: %{theme: :livebook}} = data + + # with empty theme + Kino.VegaLite.configure(theme: nil) + + kino = start_kino() + + data = connect(kino) + assert %{config: %{theme: nil}} = data + end + defp start_kino() do Vl.new() |> Vl.mark(:point)