@@ -96,7 +96,7 @@ defmodule Phoenix.LiveDashboard.PageBuilder do
9696
9797 We currently support `card/1`, `fields_card/1`, `row/1`,
9898 `shared_usage_card/1`, and `usage_card/1`;
99- and the live components `live_layered_graph/1`, `live_nav_bar/1`,
99+ and the live components `live_layered_graph/1`, `live_nav_bar/1`,
100100 and `live_table/1`.
101101
102102 ## Helpers
@@ -105,6 +105,74 @@ defmodule Phoenix.LiveDashboard.PageBuilder do
105105 helpers are: `live_dashboard_path/2`, `live_dashboard_path/3`,
106106 `encode_app/1`, `encode_ets/1`, `encode_pid/1`, `encode_port/1`,
107107 and `encode_socket/1`.
108+
109+ ## Custom Hooks
110+
111+ If your page needs to register custom hooks, you can use the `register_after_opening_head_tag/2`
112+ function. Because the hooks need to be available on the dead render in the layout, before the
113+ LiveView's LiveSocket is configured, your need to do this inside an `on_mount` hook:
114+
115+ ```elixir
116+ defmodule MyAppWeb.MyLiveDashboardHooks do
117+ import Phoenix.LiveView
118+ import Phoenix.Component
119+
120+ alias Phoenix.LiveDashboard.PageBuilder
121+
122+ def on_mount(:default, _params, _session, socket) do
123+ {:cont, PageBuilder.register_after_opening_head_tag(socket, &after_opening_head_tag/1)}
124+ end
125+
126+ defp after_opening_head_tag(assigns) do
127+ ~H\" \" \"
128+ <script>
129+ window.customHooks = {
130+ ...(window.customHooks || {}),
131+ MyHook: {
132+ mounted() {
133+ // do something
134+ }
135+ }
136+ }
137+ </script>
138+ \" \" \"
139+ end
140+ end
141+
142+ defmodule MyAppWeb.MyCustomPage do
143+ ...
144+ end
145+ ```
146+
147+ And then add it to the list of `on_mount` hooks in the `live_dashboard` router configuration:
148+
149+ ```elixir
150+ live_dashboard "/dashboard",
151+ additional_pages: [
152+ route_name: MyAppWeb.MyCustomPage
153+ ],
154+ on_mount: [
155+ MyAppWeb.MyLiveDashboardHooks
156+ ]
157+ ```
158+
159+ The LiveDashboard will merge the `window.customHooks` object into the hooks that are
160+ configured on the LiveSocket.
161+
162+ > #### Warning {: .warning}
163+ >
164+ > If you are building a library that will be used by others, ensure that you are
165+ > not overwriting the `window.customHooks` object instead of extending it.
166+ >
167+ > Instead of `window.customHooks = {...}`,
168+ > use `window.customHooks = {...(window.customHooks || {}), ...}`.
169+
170+ Note that in order to use external libraries, you will either need to include them from
171+ a CDN, or bundle them yourself and include them from your apps static paths.
172+
173+ Also, you are responsible for ensuring that your Content Security Policy (CSP) allows
174+ the hooks to be executed. If you are building a library that will be used by others,
175+ consider including a valid nonce on your script tags.
108176 """
109177
110178 use Phoenix.Component
@@ -971,6 +1039,30 @@ defmodule Phoenix.LiveDashboard.PageBuilder do
9711039 live_dashboard_path ( socket , route , node , old_params , new_params )
9721040 end
9731041
1042+ @ doc """
1043+ Registers a component to be rendered after the opening head tag in the layout.
1044+ """
1045+ def register_after_opening_head_tag ( socket , component ) do
1046+ register_head ( socket , component , :after_opening_head_tag )
1047+ end
1048+
1049+ @ doc """
1050+ Registers a component to be rendered before the closing head tag in the layout.
1051+ """
1052+ def register_before_closing_head_tag ( socket , component ) do
1053+ register_head ( socket , component , :before_closing_head_tag )
1054+ end
1055+
1056+ defp register_head ( socket , component , assign ) do
1057+ case socket do
1058+ % { assigns: % { ^ assign => [ _ | _ ] } } ->
1059+ update ( socket , assign , fn existing -> [ component | existing ] end )
1060+
1061+ _ ->
1062+ assign ( socket , assign , [ component ] )
1063+ end
1064+ end
1065+
9741066 # TODO: Remove this and the conditional on Phoenix v1.7+
9751067 @ compile { :no_warn_undefined , Phoenix.VerifiedRoutes }
9761068
0 commit comments