Packages
phoenix_kit
1.7.174
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
221 Versions
Jump to
Current section
221 Versions
Compare versions
5
files changed
+69
additions
-15
deletions
unknownCHANGELOG.md
File is too large to be displayed (100 KB limit).
| @@ -1,6 +1,6 @@ | |
| 1 1 | {<<"links">>,[{<<"GitHub">>,<<"https://github.com/BeamLabEU/phoenix_kit">>}]}. |
| 2 2 | {<<"name">>,<<"phoenix_kit">>}. |
| 3 | - {<<"version">>,<<"1.7.173">>}. |
| 3 | + {<<"version">>,<<"1.7.174">>}. |
| 4 4 | {<<"description">>, |
| 5 5 | <<65,32,102,111,117,110,100,97,116,105,111,110,32,102,111,114,32,98,117,105, |
| 6 6 | 108,100,105,110,103,32,69,108,105,120,105,114,32,80,104,111,101,110,105, |
| @@ -47,8 +47,14 @@ defmodule PhoenixKitWeb do | |
| 47 47 | |
| 48 48 | def live_view do |
| 49 49 | quote do |
| 50 | + # The native `:layout` is a pure passthrough (`PhoenixKitWeb.Layouts.app` |
| 51 | + # renders only `{@inner_content}`). PhoenixKit LiveViews apply the host's |
| 52 | + # configured `config :phoenix_kit, layout:` themselves, once, via |
| 53 | + # `LayoutWrapper.app_layout` in their render — so routing `:layout` at the |
| 54 | + # host layout too would render the host chrome twice. Keeping `:layout` a |
| 55 | + # passthrough makes `app_layout` the single owner of the host layout. |
| 50 56 | use Phoenix.LiveView, |
| 51 | - layout: PhoenixKit.LayoutConfig.get_layout() |
| 57 | + layout: {PhoenixKitWeb.Layouts, :app} |
| 52 58 | |
| 53 59 | use Gettext, backend: PhoenixKitWeb.Gettext |
| @@ -177,22 +177,40 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do | |
| 177 177 | end |
| 178 178 | |
| 179 179 | defp has_inner_content?(assigns), do: assigns[:inner_content] != nil |
| 180 | - defp has_inner_block?(assigns), do: assigns[:inner_block] && assigns[:inner_block] != [] |
| 180 | + |
| 181 | + # Must return a strict boolean: `needs_inner_block_conversion?/1` calls |
| 182 | + # `not has_inner_block?(...)`. When `app_layout` is reached with only an |
| 183 | + # `inner_content` (the legacy Phoenix 1.7 flow) and no `inner_block` key at |
| 184 | + # all, the old `assigns[:inner_block] && ...` short-circuited to `nil`, and |
| 185 | + # `not nil` raised ArgumentError. `not in [nil, []]` normalizes both "absent" |
| 186 | + # (nil) and "declared-but-empty slot" ([]) to `false`. |
| 187 | + defp has_inner_block?(assigns), do: assigns[:inner_block] not in [nil, []] |
| 181 188 | |
| 182 189 | defp convert_inner_content_to_block(assigns) do |
| 183 190 | inner_content = assigns[:inner_content] |
| 184 191 | inner_block = build_synthetic_inner_block(inner_content) |
| 185 | - Map.put(assigns, :inner_block, inner_block) |
| 192 | + # Use assign/3 (not Map.put) so `__changed__[:inner_block]` is force-marked. |
| 193 | + # Phoenix only force-marks `:inner_content` on a diff (renderer.ex), so a |
| 194 | + # host layout's `render_slot(@inner_block)` dynamic is guarded by |
| 195 | + # `changed_assign?(__changed__, :inner_block)` — which is false for a bare |
| 196 | + # Map.put — and would emit nil, freezing the page body after first paint on |
| 197 | + # connected updates. Mirrors the admin-nav path's `assign(..., :inner_block)`. |
| 198 | + assign(assigns, :inner_block, inner_block) |
| 186 199 | end |
| 187 200 | |
| 201 | + # Synthesize a one-entry slot whose body yields `inner_content`. `inner_content` |
| 202 | + # may be a `%Phoenix.LiveView.Rendered{}` (return it verbatim — it is already |
| 203 | + # renderable; `Phoenix.HTML.raw/1` has no struct clause and would raise |
| 204 | + # FunctionClauseError on it) or a binary from the legacy Phoenix 1.7- flow |
| 205 | + # (mark it safe via `raw/1`). |
| 188 206 | defp build_synthetic_inner_block(inner_content) do |
| 189 | - [ |
| 190 | - %{ |
| 191 | - inner_block: fn _slot_assigns, _index -> |
| 192 | - Phoenix.HTML.raw(inner_content) |
| 193 | - end |
| 194 | - } |
| 195 | - ] |
| 207 | + body = |
| 208 | + case inner_content do |
| 209 | + %Phoenix.LiveView.Rendered{} = rendered -> rendered |
| 210 | + other -> Phoenix.HTML.raw(other) |
| 211 | + end |
| 212 | + |
| 213 | + [%{inner_block: fn _slot_assigns, _index -> body end}] |
| 196 214 | end |
| 197 215 | |
| 198 216 | # Check if current page is an admin page that needs navigation. |
| @@ -696,14 +714,44 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do | |
| 696 714 | # Wrap inner content with admin navigation if needed |
| 697 715 | assigns = wrap_inner_block_with_admin_nav_if_needed(assigns) |
| 698 716 | |
| 699 | - # Use apply/3 to dynamically call the parent layout function |
| 700 | - apply(module, function, [assigns]) |
| 717 | + # `app_layout` is the single owner of the host layout (the native `:layout` |
| 718 | + # is a passthrough — see `PhoenixKitWeb.__using__(:live_view)`), so this is |
| 719 | + # the one place the host layout is applied. |
| 720 | + apply_host_layout(assigns, module, function) |
| 721 | + end |
| 722 | + |
| 723 | + # Apply the configured host layout, giving it BOTH inner conventions so it |
| 724 | + # works whether it renders `{@inner_content}` (the documented contract) or |
| 725 | + # `render_slot(@inner_block)` (the Phoenix 1.8 idiom). `app_layout` is invoked |
| 726 | + # with an `inner_block` slot; `ensure_inner_content_from_block/1` derives a |
| 727 | + # lazy `@inner_content` from it when absent. Rescues a bad |
| 728 | + # `config :phoenix_kit, layout:` (renamed/removed function) to PhoenixKit's own |
| 729 | + # layout instead of 500-ing every page. Public for regression testing. |
| 730 | + @doc false |
| 731 | + def apply_host_layout(assigns, module, function) do |
| 732 | + assigns |
| 733 | + |> ensure_inner_content_from_block() |
| 734 | + |> then(&apply(module, function, [&1])) |
| 701 735 | rescue |
| 702 736 | UndefinedFunctionError -> |
| 703 | - # Fallback to PhoenixKit layout if parent function doesn't exist |
| 704 737 | render_with_phoenix_kit_layout(assigns) |
| 705 738 | end |
| 706 739 | |
| 740 | + # Derive `@inner_content` from the `inner_block` slot when the caller only |
| 741 | + # supplied a slot. Kept lazy (a `%Rendered{}`) so a host layout using |
| 742 | + # `{@inner_content}` still change-tracks correctly on connected updates. |
| 743 | + defp ensure_inner_content_from_block(assigns) do |
| 744 | + if has_inner_content?(assigns) do |
| 745 | + assigns |
| 746 | + else |
| 747 | + assign(assigns, :inner_content, render_inner_block(assigns)) |
| 748 | + end |
| 749 | + end |
| 750 | + |
| 751 | + defp render_inner_block(assigns) do |
| 752 | + ~H"{render_slot(@inner_block)}" |
| 753 | + end |
| 754 | + |
| 707 755 | # Phoenix v1.7- approach - templates (legacy support) |
| 708 756 | defp render_legacy_parent_layout(assigns, _module, _function) do |
| 709 757 | # For legacy Phoenix, layouts are handled at router level |
| @@ -1,7 +1,7 @@ | |
| 1 1 | defmodule PhoenixKit.MixProject do |
| 2 2 | use Mix.Project |
| 3 3 | |
| 4 | - @version "1.7.173" |
| 4 | + @version "1.7.174" |
| 5 5 | @description "A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more" |
| 6 6 | @source_url "https://github.com/BeamLabEU/phoenix_kit" |