Packages
phoenix_kit
2.64.1
2.64.1
2.64.0
2.63.0
2.62.2
2.62.1
2.62.0
2.61.0
2.60.3
2.60.2
2.60.1
2.60.0
2.59.0
2.58.0
2.57.1
2.57.0
2.56.1
2.56.0
2.55.1
2.55.0
2.54.2
2.54.1
2.54.0
2.53.0
2.52.2
2.52.1
2.52.0
2.51.0
2.50.0
2.49.1
2.49.0
2.48.0
2.47.0
2.46.0
2.45.0
2.44.0
2.43.1
2.43.0
2.42.1
2.42.0
2.41.6
2.41.4
2.41.3
2.41.2
2.41.1
2.41.0
2.40.1
2.40.0
2.39.0
2.38.1
2.38.0
2.37.5
2.37.4
2.37.3
2.37.2
2.37.1
2.37.0
2.36.1
2.36.0
2.35.0
2.34.0
2.33.0
2.32.1
2.32.0
2.31.1
2.31.0
2.30.0
2.29.1
2.29.0
2.28.2
2.28.1
2.28.0
2.27.2
2.27.1
2.27.0
2.26.1
2.26.0
2.25.0
2.24.0
2.23.3
2.23.2
2.23.1
2.23.0
2.22.24
2.22.23
2.22.22
2.22.21
2.22.20
2.22.19
2.22.18
2.22.17
2.22.16
2.22.15
2.22.14
2.22.13
2.22.12
2.22.11
2.22.10
2.22.9
2.22.8
2.22.7
2.22.6
2.22.5
2.22.4
2.22.3
2.22.2
2.22.1
2.22.0
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.0
2.19.0
2.18.1
2.18.0
2.17.0
2.16.0
2.15.1
2.15.0
2.14.2
2.14.1
2.14.0
2.13.19
2.13.18
2.13.17
2.13.16
2.13.15
2.13.13
2.13.12
2.13.11
2.13.10
2.13.9
2.13.8
2.13.7
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.0
2.10.0
2.9.0
2.8.1
2.8.0
2.7.0
2.6.0
2.5.0
2.4.0
2.3.0
2.2.0
2.1.0
2.0.1
2.0.0
1.7.236
1.7.235
1.7.234
1.7.233
1.7.232
1.7.231
1.7.230
1.7.229
1.7.228
1.7.227
1.7.226
1.7.225
1.7.224
1.7.223
1.7.222
1.7.221
1.7.220
1.7.219
1.7.218
1.7.217
1.7.216
1.7.215
1.7.214
1.7.213
1.7.212
1.7.211
1.7.210
1.7.209
1.7.208
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
Files
Jump to
Current section
Files
lib/phoenix_kit_web/components/admin_dock.ex
defmodule PhoenixKitWeb.Components.AdminDock do
@moduledoc """
The optional right-hand dock column of the admin layout, and the commands that open it.
A module's header widget (`c:PhoenixKit.Module.header_widgets/0`) that needs room beside
the page, a chat panel for instance, declares `dock: true`. While at least one rendered
widget does, `#admin-drawer` gets one more grid column, `#pk-dock`. Without such a widget
the layout has no dock markup at all.
## Contract
* **Container**: `#pk-dock` (`container_id/0`), the last child of `#admin-drawer`. It is
an empty `<aside>` that is `display: none` until opened. What goes into it is the
widget's business: the widget can position its own panel over the column (the column
holds the width free, the panel sits at `right-0` with `w-[var(--pk-dock-width,25rem)]`),
or move its panel element into the container with a hook.
* **State**: the class `pk-dock-open` (`open_class/0`) on `<body>` (`target/0`) or on
**any element inside it**, the widget's own markup included; either opens the dock.
The page is laid out as `<body> > header` (where the widgets live) next to
`<body> > ... > #admin-drawer > #pk-dock`, so `<body>` is the one element above both
and the CSS looks there. Which form to use depends on how long the state has to live:
* The JS commands below put the class on `<body>`. LiveView never patches `<body>`, so
the class survives `push_patch`, a live redirect to another LiveView (the usual
admin navigation, e.g. a sidebar link), and the sticky widget is not mounted again.
A full page load resets it.
* A class on an element of the widget itself (in the header) travels with the sticky
widget as well. A widget that keeps "open" in its own assigns renders
`class={[@open? && "pk-dock-open"]}` on an element of its own, and the dock is shown
for as long as that element has the class. It needs no JavaScript and no replay on
mount.
* **Width**: the CSS variable `--pk-dock-width` (`width_var/0`), `25rem` when unset. The
dock reads it inside `#admin-drawer`, a widget panel in the header reads it from the
header: set it on `:root` or `<body>` (above both), from CSS or a style attribute, and
write `var(--pk-dock-width,25rem)` wherever you read it.
* **Layout**: from `lg` up the dock is a third column of the drawer grid, so the work
area gets narrower by the dock's width; the dock is as tall as the viewport below the
header and scrolls itself. Below `lg` it covers the whole screen under the header.
## Commands
<button phx-click={AdminDock.toggle()}>Chat</button>
<button phx-click={AdminDock.close() |> JS.push("forget_panel")}>Close</button>
The commands target `<body>` by selector, so they work from the widget's nested LiveView,
which sits in the header. A widget that also keeps its own class on an element of its own
must not mix the two: when it closes, it removes the class from its element and runs
`close/1`.
## Not covered by tests
`Phoenix.LiveViewTest` has no notion of sticky LiveViews moving to the new page on a live
redirect, so the ExUnit tests pin the markup (the container, the class variants), the
JS ops, and that the element the commands address, and the one the CSS looks at, is an
ancestor of both the widget and the dock; not the survival across a navigation. That is
LiveView's own behaviour.
"""
alias Phoenix.LiveView.JS
@target "body"
@container_id "pk-dock"
@open_class "pk-dock-open"
@width_var "--pk-dock-width"
@doc "The selector of the element the commands put the open class on."
@spec target() :: String.t()
def target, do: @target
@doc "The id of the dock container."
@spec container_id() :: String.t()
def container_id, do: @container_id
@doc "The class that opens the dock."
@spec open_class() :: String.t()
def open_class, do: @open_class
@doc "The CSS variable that sets the dock's width."
@spec width_var() :: String.t()
def width_var, do: @width_var
@doc "Open the dock."
@spec open(JS.t()) :: JS.t()
def open(js \\ %JS{}), do: JS.add_class(js, @open_class, to: @target)
@doc "Close the dock."
@spec close(JS.t()) :: JS.t()
def close(js \\ %JS{}), do: JS.remove_class(js, @open_class, to: @target)
@doc "Open the dock if it is closed, close it if it is open."
@spec toggle(JS.t()) :: JS.t()
def toggle(js \\ %JS{}), do: JS.toggle_class(js, @open_class, to: @target)
end