lib/sanity/components/image.ex

defmodule Sanity.Components.Image do
  use Phoenix.Component

  @breakpoints [320, 768, 1024, 1600, 2048]

  def sanity_image(assigns) do
    {%{
       metadata: %{
         dimensions: %{height: height, width: width},
         palette: %{dominant: %{background: background}}
       },
       url: url
     }, assigns} = Map.pop!(assigns, :image)

    assigns =
      assigns
      |> Map.drop([:__changed__])
      |> Map.put_new(:height, height)
      |> Map.put_new(:width, width)
      |> Map.put_new(:style, "--sanity-image-bg: #{background}")
      |> Map.put_new(:sizes, "100vw")

    ~H"""
    <img {assigns} src={image_url(url, 1024)} srcset={srcset(url)} />
    """
  end

  defp image_url(url, size) when is_binary(url) and is_integer(size) do
    params = %{auto: "format", fit: "min", w: size}

    "#{url}?#{URI.encode_query(params)}"
  end

  defp srcset(url) when is_binary(url) do
    {breakpoints, [last_breakpoint]} = Enum.split(@breakpoints, -1)

    breakpoints
    |> Enum.map(fn w -> "#{image_url(url, w)} #{w}w" end)
    |> Enum.concat([image_url(url, last_breakpoint)])
    |> Enum.join(",")
  end
end