srcrip / live_toast

A beautiful drop-in replacement for the Phoenix Flash system.
https://toast.src.rip
MIT License
192 stars 17 forks source link

Better custom component support #21

Open bamorim opened 4 months ago

bamorim commented 4 months ago

This is a WIP, but mainly I was trying to have a completely custom component for all flashes/toasts.

I realized that the DX was weird because I had to pass the component each time. Worse than that, the X icon was still being rendered even with a custom component.

So I'm trying to improve this by:

This is still missing a lot of important details. I also was failing to run the tests because some Jason issue that I'll look into after.

But with this setup I was able to customize the toast completely with the following toast component:

defmodule PursonalWeb.CoreComponents.Toast do
  use Phoenix.Component

  import Heroicons.LiveView
  import Turboprop.Variants

  @variant_config [
    slots: [
      base: [
        "group",
        "pointer-events-auto",
        "relative",
        "flex",
        "w-full",
        "items-center",
        "justify-between",
        "space-x-4",
        "overflow-hidden",
        "rounded-md",
        "p-6",
        "pr-8",
        "shadow-lg",
        "transition-all",
        "mt-4"
      ],
      cross: [
        "absolute",
        "right-2",
        "top-2",
        "rounded-md",
        "p-1",
        "opacity-0",
        "transition-opacity",
        "focus:opacity-100",
        "focus:outline-none",
        "focus:ring-2",
        "group-hover:opacity-100"
      ]
    ],
    variants: [
      variant: [
        default: [
          base: "bg-background text-foreground border",
          cross: "text-foreground/50 hover:text-foreground"
        ],
        destructive: [
          base: "bg-destructive text-destructive-foreground",
          cross: "text-destructive-foreground/50 hover:text-destructive-foreground"
        ]
      ]
    ]
  ]

  attr :id, :string, doc: "the optional id of flash container"
  attr :title, :string, default: nil
  attr :body, :string, default: ""
  attr :kind, :atom, values: [:info, :error], doc: "used for styling and flash lookup"
  attr :action, :any, doc: "Optinal action component"
  attr :close_args, :any, default: []

  def toast(assigns) do
    assigns =
      assigns
      |> assign(:base_variant_class, toast_variant(assigns, :base))
      |> assign(:cross_variant_class, toast_variant(assigns, :cross))

    ~H"""
    <div role="status" aria-live="off" aria-atomic="true" tabindex="0" class={@base_variant_class}>
      <div class="grid gap-1">
        <div :if={@title} class="text-sm font-semibold" data-part="title"><%= @title %></div>
        <div class="text-sm opacity-90"><%= @body %></div>
      </div>
      <button type="button" class={@cross_variant_class} {@close_args}>
        <.icon name="x-mark" type="solid" class="h-5 w-5" />
      </button>
    </div>
    """
  end

  defp toast_variant(assigns, slot) do
    variant(@variant_config, slot: slot, variant: variant_name(assigns))
  end

  defp variant_name(%{kind: :error}), do: :destructive
  defp variant_name(_), do: :default
end
peaceful-james commented 2 weeks ago

Hi @bamorim

I tried using your fork and I encountered a bug.

Note that I am using :top_right for my :corner prop of the toast group.

It looks OK with 1 toast:

image

But with 2 toasts the translate-y is messed up:

image

(Yes, that is 2 toasts superimposed)

Here is then 3 toasts:

image

As you can see, they are all being translated as though they are the "earliest" toast.

I reckon the problem lies in this bit of JS in assets/js/live_toast/live_toast.ts:

    // Calculate the translateY value with gap
    // now that they can be different heights, we need to actually caluclate the real heights and add them up.
    let val = 0

    for (let j = 0; j < toast.order; j++) {
      val += ts[j].offsetHeight + gap
    }

The screenshots are from a page I built just to test different toast behaviors. I had built my own similar toast system based on toastify-js but I want to get back in with the herd, for safety, so I switched to this lib. I will keep using this lib, even though I am dismayed at the impossible-to-customize buttons. Your PR is a good effort. I appreciate you.