LineaUI.Components.Drawer (linea_ui v0.3.0)

Copy Markdown View Source

LineOne's drawer: a full-height panel that slides in from the left or the right over a dark backdrop. Bare, it is a sliding frame for a menu; with a title (or a :header) and a :footer it is a sheet, the panel a form or a detail opens in: a header with the title, its description and the close ×, a body that scrolls, and a footer with the buttons.

<.button phx-click={LineaUI.toggle("menu")}>Menu</.button>

<.drawer id="menu" side="right">
  <nav class="p-5">…</nav>
</.drawer>

<.drawer id="customer" placement="right" size="md" title="New customer"
  description="Who buys from you." on_close="cancel">
  <.form for={@form} id="customer-form" phx-submit="save">…</.form>
  <:footer>
    <.button variant="outline" phx-click={LineaUI.close("customer")}>Cancel</.button>
    <.button type="submit" form="customer-form" color="primary">Save</.button>
  </:footer>
</.drawer>

size sets the sheet's width (sm 400px, md 560px, lg 760px, xl 1080px), never wider than the screen, so it is full width on a phone. Buttons in the header go in :actions, a back arrow (a sheet opened from another) in :back.

Like the modal it opens from the browser (LineaUI.open/close/toggle("id")), from the server (LineaUI.open(socket, "id")) or from open; on_close runs however it closed (Esc, the backdrop, the ×, a command), so the server can follow. By default a drawer does not lock the page's scroll, as in LineOne (lock_scroll locks it).

Drawers and modals stack in the order they open: a drawer opened from another (a detail, then its edit form), or a modal opened from a drawer, is drawn above it; Esc and the backdrop close only the one on top. The second overlay may sit next to the first or inside its markup (a confirm rendered in the sheet's body or footer): the open drawer carries no CSS transform (the slide is an animation that only runs while it enters and leaves, with no fill), so it is not a containing block for position: fixed and a modal inside it still covers the whole viewport, above the drawer. Keep panel_class and class free of transform, filter, backdrop-filter, perspective and contain for the same reason. Nothing is moved out of the LiveView's DOM, so patches reach the nested modal as usual. The layers come from the CSS custom properties --linea-z-overlay (drawers) and --linea-z-modal (modals), see the README.

Summary

Functions

Renders a drawer, closed unless open.

Functions

drawer(assigns)

Renders a drawer, closed unless open.

Events: on_open and on_close push %{"id" => id} when they are event names.

Attributes

  • id (:string) (required)
  • open (:boolean) - Defaults to false.
  • side (:string) - Defaults to "left". Must be one of "left", or "right".
  • placement (:string) - The same as side, which it overrides when given. Defaults to nil. Must be one of nil, "left", or "right".
  • size (:string) - The sheet's width: sm 400px, md 560px, lg 760px, xl 1080px, full width on a phone. nil is LineOne's w-72 frame. Defaults to nil. Must be one of nil, "sm", "md", "lg", or "xl".
  • title (:string) - The sheet's title; draws the header. Defaults to nil.
  • description (:string) - A line under the title. Defaults to nil.
  • close_button (:boolean) - The × at the end of the header (only drawn when there is a header). Defaults to true.
  • close_label (:string) - Defaults to "Close".
  • overlay (:boolean) - The dark backdrop, which closes the drawer when clicked. Defaults to true.
  • lock_scroll (:boolean) - Locks the page's scroll while it is open, as a modal does. Defaults to false.
  • prevent_close (:boolean) - Esc and the backdrop do not close it. Defaults to false.
  • on_open (:any) - An event name or a %JS{}. Defaults to nil.
  • on_close (:any) - An event name or a %JS{}. Defaults to nil.
  • target (:any) - The phx-target of on_open / on_close events. Defaults to nil.
  • class (:any) - Merged into the sliding frame: its width (size, or w-72; e.g. class="w-96") and shadow. Defaults to nil.
  • panel_class (:any) - Merged into the white panel inside the frame. Defaults to nil.
  • body_class (:any) - Merged into the scrolling body of a sheet (px-6 py-5, a column with gap-4). Defaults to nil.
  • Global attributes are accepted.

Slots

  • header - The header's content instead of title and description; the back arrow, the actions and the × still frame it. Accepts attributes:
    • class (:any)
  • back - Before the title: the button back to the sheet this one was opened from (phx-click={LineaUI.close("this")}).
  • actions - Buttons at the end of the header, before the ×.
  • footer - The buttons under the body, aligned to the end, on a tinted bar. Accepts attributes:
    • class (:any)
  • inner_block (required)