@@ -32,6 +32,77 @@ defmodule AresWeb.CoreComponents do
3232 alias Phoenix.HTML.Form
3333 alias Phoenix.LiveView.JS
3434
35+ @ doc """
36+ Renders a modal.
37+
38+ ## Examples
39+
40+ <.modal id="confirm-modal">
41+ This is a modal.
42+ </.modal>
43+
44+ JS commands may be passed to the `:on_cancel` to configure
45+ the closing/cancel event, for example:
46+
47+ <.modal id="confirm" on_cancel={JS.navigate(~p"/posts")}>
48+ This is another modal.
49+ </.modal>
50+
51+ """
52+ attr :id , :string , required: true
53+ attr :show , :boolean , default: false
54+ attr :wrapper_class , :string , default: ""
55+ attr :on_cancel , JS , default: % JS { }
56+ slot :inner_block , required: true
57+
58+ def modal ( assigns ) do
59+ ~H"""
60+ < div
61+ id = { @ id }
62+ phx-mounted = { @ show && show_modal ( @ id ) }
63+ phx-remove = { hide_modal ( @ id ) }
64+ data-cancel = { JS . exec ( @ on_cancel , "phx-remove" ) }
65+ class = "relative z-50 hidden "
66+ >
67+ < div id = { "#{ @ id } -bg" } class = "bg-black/90 fixed inset-0 transition-opacity " aria-hidden = "true " />
68+ < div
69+ class = { "fixed inset-0 overflow-y-auto #{ @ wrapper_class } " }
70+ aria-labelledby = { "#{ @ id } -title" }
71+ aria-describedby = { "#{ @ id } -description" }
72+ role = "dialog "
73+ aria-modal = "true "
74+ tabindex = "0 "
75+ >
76+ < div class = "flex min-h-full items-center justify-center m-4 " >
77+ < div class = "w-full max-w-xl " >
78+ < . focus_wrap
79+ id = { "#{ @ id } -container" }
80+ phx-window-keydown = { JS . exec ( "data-cancel" , to: "##{ @ id } " ) }
81+ phx-key = "escape "
82+ phx-click-away = { JS . exec ( "data-cancel" , to: "##{ @ id } " ) }
83+ class = "p-6 bg-[#0A0A0A] border border-zinc-800 text-white shadow-2xl shadow-primary/10 relative rounded-2xl transition "
84+ >
85+ < div class = "absolute top-6 right-5 " >
86+ < button
87+ phx-click = { JS . exec ( "data-cancel" , to: "##{ @ id } " ) }
88+ type = "button "
89+ class = "-m-3 flex-none p-3 opacity-20 text-white black:text-black cursor-pointer hover:opacity-40 "
90+ aria-label = { gettext ( "close" ) }
91+ >
92+ < . icon name = "hero-x-mark-solid " class = "h-5 w-5 " />
93+ </ button >
94+ </ div >
95+ < div id = { "#{ @ id } -content" } >
96+ { render_slot ( @ inner_block ) }
97+ </ div >
98+ </ . focus_wrap >
99+ </ div >
100+ </ div >
101+ </ div >
102+ </ div >
103+ """
104+ end
105+
35106 @ doc """
36107 Renders flash notices.
37108
@@ -443,6 +514,30 @@ defmodule AresWeb.CoreComponents do
443514 )
444515 end
445516
517+ def show_modal ( js \\ % JS { } , id ) when is_binary ( id ) do
518+ js
519+ |> JS . show ( to: "##{ id } " )
520+ |> JS . show (
521+ to: "##{ id } -bg" ,
522+ transition: { "transition-all transform ease-out duration-300" , "opacity-0" , "opacity-100" }
523+ )
524+ |> show ( "##{ id } -container" )
525+ |> JS . add_class ( "overflow-hidden" , to: "body" )
526+ |> JS . focus_first ( to: "##{ id } -content" )
527+ end
528+
529+ def hide_modal ( js \\ % JS { } , id ) do
530+ js
531+ |> JS . hide (
532+ to: "##{ id } -bg" ,
533+ transition: { "transition-all transform ease-in duration-200" , "opacity-100" , "opacity-0" }
534+ )
535+ |> hide ( "##{ id } -container" )
536+ |> JS . hide ( to: "##{ id } " , transition: { "block" , "block" , "hidden" } )
537+ |> JS . remove_class ( "overflow-hidden" , to: "body" )
538+ |> JS . pop_focus ( )
539+ end
540+
446541 @ doc """
447542 Translates an error message using gettext.
448543 """
0 commit comments