diff --git a/.formatter.exs b/.formatter.exs index 1b2af96..b8d13c5 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,6 +1,6 @@ [ import_deps: [:phoenix], - plugins: [Phoenix.LiveView.HTMLFormatter, Styler], + plugins: [Phoenix.LiveView.HTMLFormatter, Quokka], inputs: ["{mix,.formatter}.exs", "*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}"], subdirectories: ["demo"] ] diff --git a/README.md b/README.md index 0549aac..835123d 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,13 @@ msgstr "Aguanta mientras volvemos a la normalidad" component-specific presentation or behavior without adding library-level options. LiveToast does not interpret metadata. +### Custom rendering and classes + +The `component` option and `toast_component_fn` render the content inside a LiveToast-managed toast shell. The shell +continues to provide the hook, timing attributes, enter and exit animations, stacking, and dismiss button. Use +[`toast_class_fn/1`](https://hexdocs.pm/live_toast/LiveToast.html#toast_class_fn/1) to change that outer shell's +classes; use a custom component function for the icon, title, body, action, and other toast content. + ### Default custom toast component Pass `toast_component_fn` to `LiveToast.toast_group` when all programmatic toasts in that host should use the same diff --git a/config/config.exs b/config/config.exs index 2103d99..2abdeee 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,8 +1,9 @@ import Config -config :phoenix, :json_library, Jason -config :logger, :level, :debug config :logger, :backends, [] +config :logger, :level, :debug + +config :phoenix, :json_library, Jason if Mix.env() == :dev do esbuild = fn args -> diff --git a/demo/.formatter.exs b/demo/.formatter.exs index 2e6e73e..da90261 100644 --- a/demo/.formatter.exs +++ b/demo/.formatter.exs @@ -1,5 +1,5 @@ [ import_deps: [:phoenix], - plugins: [Phoenix.LiveView.HTMLFormatter, Styler], + plugins: [Phoenix.LiveView.HTMLFormatter, Quokka], inputs: ["{mix,.formatter}.exs", "*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}"] ] diff --git a/demo/config/config.exs b/demo/config/config.exs index 92ae11b..54e6200 100644 --- a/demo/config/config.exs +++ b/demo/config/config.exs @@ -1,27 +1,10 @@ import Config -config :esbuild, - version: "0.17.11", - demo: [ - args: ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*), - cd: Path.expand("../assets", __DIR__), - env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} - ] - -config :tailwind, - version: "3.4.0", - demo: [ - args: ~w( - --config=tailwind.config.cjs - --input=css/app.css - --output=../priv/static/assets/app.css - ), - cd: Path.expand("../assets", __DIR__) - ] +alias Phoenix.Endpoint.Cowboy2Adapter config :demo, DemoWeb.Endpoint, url: [host: "localhost"], - adapter: Phoenix.Endpoint.Cowboy2Adapter, + adapter: Cowboy2Adapter, render_errors: [ formats: [html: DemoWeb.ErrorHTML, json: DemoWeb.ErrorJSON], layout: false @@ -29,6 +12,15 @@ config :demo, DemoWeb.Endpoint, pubsub_server: Demo.PubSub, live_view: [signing_salt: "mca0adK+"] +config :esbuild, + version: "0.17.11", + demo: [ + args: + ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*), + cd: Path.expand("../assets", __DIR__), + env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} + ] + config :live_toast, gettext_backend: Demo.Gettext @@ -38,4 +30,15 @@ config :logger, :console, config :phoenix, :json_library, Jason +config :tailwind, + version: "3.4.0", + demo: [ + args: ~w( + --config=tailwind.config.cjs + --input=css/app.css + --output=../priv/static/assets/app.css + ), + cd: Path.expand("../assets", __DIR__) + ] + import_config "#{config_env()}.exs" diff --git a/demo/config/dev.exs b/demo/config/dev.exs index 2eab3ae..40ada74 100644 --- a/demo/config/dev.exs +++ b/demo/config/dev.exs @@ -1,5 +1,14 @@ import Config +config :demo, DemoWeb.Endpoint, + live_reload: [ + patterns: [ + ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$", + ~r"priv/gettext/.*(po)$", + ~r"lib/demo_web/(controllers|live|components)/.*(ex|heex)$" + ] + ] + config :demo, DemoWeb.Endpoint, reloadable_apps: [:live_toast, :demo], http: [ip: {127, 0, 0, 1}, port: 4004], @@ -12,15 +21,5 @@ config :demo, DemoWeb.Endpoint, tailwind: {Tailwind, :install_and_run, [:demo, ~w(--watch)]} ] -config :demo, DemoWeb.Endpoint, - live_reload: [ - patterns: [ - ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$", - ~r"priv/gettext/.*(po)$", - ~r"lib/demo_web/(controllers|live|components)/.*(ex|heex)$" - ] - ] - -config :phoenix, :stacktrace_depth, 20 - config :phoenix, :plug_init_mode, :runtime +config :phoenix, :stacktrace_depth, 20 diff --git a/demo/config/runtime.exs b/demo/config/runtime.exs index 47f0e90..12828e6 100644 --- a/demo/config/runtime.exs +++ b/demo/config/runtime.exs @@ -15,8 +15,6 @@ if config_env() == :prod do host = System.get_env("PHX_HOST") || "example.com" port = String.to_integer(System.get_env("PORT") || "4004") - config :demo, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY") - config :demo, DemoWeb.Endpoint, url: [host: host, port: 443, scheme: "https"], http: [ @@ -24,4 +22,6 @@ if config_env() == :prod do port: port ], secret_key_base: secret_key_base + + config :demo, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY") end diff --git a/demo/config/test.exs b/demo/config/test.exs index 8905546..eb49431 100644 --- a/demo/config/test.exs +++ b/demo/config/test.exs @@ -1,9 +1,10 @@ import Config -config :logger, level: :warning -config :phoenix, :plug_init_mode, :runtime - config :demo, DemoWeb.Endpoint, http: [ip: {127, 0, 0, 1}, port: 4002], secret_key_base: "C+Yn8zjDt2z+Sbl3NhLDra8U+J1QlqLoTSLx7w9BkToiWOnPk58H9nqTq+Y9OR3A", server: false + +config :logger, level: :warning + +config :phoenix, :plug_init_mode, :runtime diff --git a/demo/lib/demo_web/components/layouts.ex b/demo/lib/demo_web/components/layouts.ex index 6176546..27a66b5 100644 --- a/demo/lib/demo_web/components/layouts.ex +++ b/demo/lib/demo_web/components/layouts.ex @@ -29,7 +29,8 @@ defmodule DemoWeb.Layouts do "items-end bottom-0 left-1/2 transform -translate-x-1/2 flex-col-reverse sm:top-auto", assigns[:corner] == :bottom_right && "items-end bottom-0 right-0 flex-col-reverse sm:top-auto", assigns[:corner] == :top_left && "items-start top-0 left-0 flex-col sm:bottom-auto", - assigns[:corner] == :top_center && "items-start top-0 left-1/2 transform -translate-x-1/2 flex-col sm:bottom-auto", + assigns[:corner] == :top_center && + "items-start top-0 left-1/2 transform -translate-x-1/2 flex-col sm:bottom-auto", assigns[:corner] == :top_right && "items-start top-0 right-0 flex-col sm:bottom-auto" ] end diff --git a/demo/lib/demo_web/endpoint.ex b/demo/lib/demo_web/endpoint.ex index 54ce653..f790599 100644 --- a/demo/lib/demo_web/endpoint.ex +++ b/demo/lib/demo_web/endpoint.ex @@ -1,6 +1,8 @@ defmodule DemoWeb.Endpoint do use Phoenix.Endpoint, otp_app: :demo + alias Phoenix.LiveView.Socket + @session_options [ store: :cookie, key: "_demo_key", @@ -8,7 +10,7 @@ defmodule DemoWeb.Endpoint do same_site: "Lax" ] - socket("/live", Phoenix.LiveView.Socket, + socket("/live", Socket, websocket: [connect_info: [session: @session_options]], longpoll: [connect_info: [session: @session_options]] ) diff --git a/demo/lib/demo_web/live/home_live.ex b/demo/lib/demo_web/live/home_live.ex index 8046fa2..95b4f74 100644 --- a/demo/lib/demo_web/live/home_live.ex +++ b/demo/lib/demo_web/live/home_live.ex @@ -203,7 +203,7 @@ defmodule DemoWeb.HomeLive do end duration = - case Map.get(payload, "duration", nil) do + case Map.get(payload, "duration") do d when is_integer(d) -> d d when is_binary(d) -> String.to_integer(d) _ -> nil diff --git a/demo/lib/demo_web/live/tabs/customization.html.heex b/demo/lib/demo_web/live/tabs/customization.html.heex index 5f02c95..926ccfd 100644 --- a/demo/lib/demo_web/live/tabs/customization.html.heex +++ b/demo/lib/demo_web/live/tabs/customization.html.heex @@ -31,7 +31,7 @@

Custom Class Function

- If you need more control over the styles, you can also pass a custom class function (that generates the class string for the main element). Note that there are a lot of required things in the default implementation of this class. It is best to take the example from the docs and extend it rather than start from zero. + A custom class function changes the outer toast shell, which owns the LiveToast hook, timing, animation, and dismiss button. Note that there are a lot of required things in the default implementation of this class. It is best to take the example from the docs and extend it rather than start from zero.

Custom JavaScript

@@ -41,6 +41,9 @@

Custom Components

- If you need total control, you can pass a custom component to the toast functions. This hijacks the normal rendering and then you can pretty much do whatever you want in terms of generating the DOM elements. + A custom component renders the toast content inside the LiveToast shell. Use it to control the icon, title, body, actions, and application-specific markup while LiveToast continues to manage timing, animation, and dismissal. Pass it to an individual toast, or define + toast_component_fn + on toast_group + to use it for every programmatic toast in that host.

diff --git a/demo/mix.exs b/demo/mix.exs index 731d47b..9e35b79 100644 --- a/demo/mix.exs +++ b/demo/mix.exs @@ -34,13 +34,13 @@ defmodule Demo.MixProject do {:ecto, ">= 0.0.0"}, {:esbuild, "~> 0.2"}, {:ex_check, "~> 0.14.0", only: [:dev], runtime: false}, - {:credo, "~> 1.7.12", only: [:dev], runtime: false}, + {:credo, "~> 1.7.12", only: [:dev, :test], runtime: false}, {:dialyxir, ">= 0.0.0", only: [:dev], runtime: false}, {:doctor, ">= 0.0.0", only: [:dev], runtime: false}, {:ex_doc, ">= 0.0.0", only: [:dev], runtime: false}, {:gettext, ">= 0.26.2"}, {:mix_audit, ">= 0.0.0", only: [:dev], runtime: false}, - {:styler, "~> 0.11.9", only: [:dev, :test], runtime: false}, + {:quokka, github: "s3cur3/quokka", branch: "jump", only: [:dev, :test], runtime: false}, {:phoenix_html, "~> 4.0"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, {:floki, ">= 0.30.0", only: :test}, diff --git a/demo/mix.lock b/demo/mix.lock index 2dcb62a..13ae0d6 100644 --- a/demo/mix.lock +++ b/demo/mix.lock @@ -37,8 +37,8 @@ "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, "plug_cowboy": {:hex, :plug_cowboy, "2.7.2", "fdadb973799ae691bf9ecad99125b16625b1c6039999da5fe544d99218e662e4", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "245d8a11ee2306094840c000e8816f0cbed69a23fc0ac2bcf8d7835ae019bb2f"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, + "quokka": {:git, "https://github.com/s3cur3/quokka.git", "4280a5bbe34d612c012ed5bc08c22c933d497bf9", [branch: "jump"]}, "ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"}, - "styler": {:hex, :styler, "0.11.9", "2595393b94e660cd6e8b582876337cc50ff047d184ccbed42fdad2bfd5d78af5", [:mix], [], "hexpm", "8b7806ba1fdc94d0a75127c56875f91db89b75117fcc67572661010c13e1f259"}, "tailwind": {:hex, :tailwind, "0.2.2", "9e27288b568ede1d88517e8c61259bc214a12d7eed271e102db4c93fcca9b2cd", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "ccfb5025179ea307f7f899d1bb3905cd0ac9f687ed77feebc8f67bdca78565c4"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "thousand_island": {:hex, :thousand_island, "1.3.10", "a9971ebab1dfb36e2710a86b37c3f54973fbc9470d892035334415521fb53328", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17ab1f1b13aadb1f4b4c8e5b59c06874d701119fed082884c9c6d38addad254f"}, diff --git a/docs.exs b/docs.exs index de7f48a..b1f9b46 100644 --- a/docs.exs +++ b/docs.exs @@ -28,6 +28,6 @@ bandit = {Bandit, plug: Router, scheme: :http, port: Router.port()} {:ok, _} = Supervisor.start_link([bandit], strategy: :one_for_one) # unless running from IEx, sleep idenfinitely so we can serve requests -unless IEx.started?() do +if !IEx.started?() do Process.sleep(:infinity) end diff --git a/lib/live_toast.ex b/lib/live_toast.ex index abd28f9..f1ccfed 100644 --- a/lib/live_toast.ex +++ b/lib/live_toast.ex @@ -8,6 +8,7 @@ defmodule LiveToast do alias LiveToast.Components alias Phoenix.LiveView alias Phoenix.LiveView.JS + alias Phoenix.LiveView.Rendered @enforce_keys [:kind, :msg] defstruct [ @@ -40,7 +41,7 @@ defmodule LiveToast do } @typedoc "`Phoenix.Component` that renders a part of the toast message." - @type component_fn() :: (map() -> Phoenix.LiveView.Rendered.t()) + @type component_fn() :: (map() -> Rendered.t()) @typedoc "Milliseconds before expiry, or `:infinity` for a persistent toast." @type duration() :: non_neg_integer() | :infinity diff --git a/lib/live_toast/live_component.ex b/lib/live_toast/live_component.ex index 5d1ef8a..5bc41ed 100644 --- a/lib/live_toast/live_component.ex +++ b/lib/live_toast/live_component.ex @@ -40,7 +40,7 @@ defmodule LiveToast.LiveComponent do flash_map = assigns[:f] - sync_toast_kind = Map.get(sync_toast, :kind, nil) + sync_toast_kind = Map.get(sync_toast, :kind) sync_toast_kind = if is_atom(sync_toast_kind) do diff --git a/lib/mix/tasks/docs/run.ex b/lib/mix/tasks/docs/run.ex index 603fe7f..2be8fad 100644 --- a/lib/mix/tasks/docs/run.ex +++ b/lib/mix/tasks/docs/run.ex @@ -34,7 +34,7 @@ defmodule Mix.Tasks.Docs.Run do {:ok, _} = Supervisor.start_link([bandit], strategy: :one_for_one) # unless running from IEx, sleep idenfinitely so we can serve requests - unless IEx.started?() do + if !IEx.started?() do Process.sleep(:infinity) end end diff --git a/mix.exs b/mix.exs index dcbead3..088b363 100644 --- a/mix.exs +++ b/mix.exs @@ -40,12 +40,12 @@ defmodule LiveToast.MixProject do {:esbuild, "~> 0.2", only: :dev}, {:bandit, "~> 1.1", only: :dev}, {:ex_check, "~> 0.14.0", only: [:dev], runtime: false}, - {:credo, "~> 1.7.12", only: [:dev], runtime: false}, + {:credo, "~> 1.7.12", only: [:dev, :test], runtime: false}, {:dialyxir, ">= 0.0.0", only: [:dev], runtime: false}, {:doctor, ">= 0.0.0", only: [:dev], runtime: false}, {:ex_doc, "~> 0.32.2", only: [:dev], runtime: false}, {:mix_audit, ">= 0.0.0", only: [:dev], runtime: false}, - {:styler, "~> 0.11.9", only: [:dev, :test], runtime: false}, + {:quokka, github: "s3cur3/quokka", branch: "jump", only: [:dev, :test], runtime: false}, {:makeup, "1.1.2", only: [:dev], runtime: false}, {:makeup_elixir, "0.16.2", only: [:dev], runtime: false}, {:makeup_js, "~> 0.1.0", only: [:dev], runtime: false}, diff --git a/mix.lock b/mix.lock index 63eaa76..ad475b6 100644 --- a/mix.lock +++ b/mix.lock @@ -33,7 +33,7 @@ "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, - "styler": {:hex, :styler, "0.11.9", "2595393b94e660cd6e8b582876337cc50ff047d184ccbed42fdad2bfd5d78af5", [:mix], [], "hexpm", "8b7806ba1fdc94d0a75127c56875f91db89b75117fcc67572661010c13e1f259"}, + "quokka": {:git, "https://github.com/s3cur3/quokka.git", "4280a5bbe34d612c012ed5bc08c22c933d497bf9", [branch: "jump"]}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "thousand_island": {:hex, :thousand_island, "1.3.9", "095db3e2650819443e33237891271943fad3b7f9ba341073947581362582ab5a", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "25ab4c07badadf7f87adb4ab414e0ed374e5f19e72503aa85132caa25776e54f"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},