# `LineaUI.Components.AppShell`
[🔗](https://github.com/devaction-labs/linea_ui/blob/v0.1.1/lib/linea_ui/components/app_shell.ex#L1)

LineOne's application shell: the page frame, the main sidebar (the icon rail), the sidebar panel with its
collapsible groups, the minimized panel, the sideblock and the horizontal navigation, the header, the right
sidebar, the page header and the dark mode and monochrome toggles.

    <.app_shell id="shell" sidebar_open={@sidebar_open} on_toggle="sidebar">
      <:sidebar>
        <.main_sidebar current={@current_path}>
          <:logo navigate={~p"/"} src={~p"/images/logo.svg"} />
          <:item navigate={~p"/dashboards"} label="Dashboards"><.icon name="home" /></:item>
          <:bottom>
            <.sidebar_profile id="profile" src={@user.avatar} name={@user.name} subtitle="Designer">
              <:item navigate={~p"/profile"} color="warning" title="Profile" description="Your profile setting">
                <.icon name="user" />
              </:item>
            </.sidebar_profile>
          </:bottom>
        </.main_sidebar>
        <.sidebar_panel title="Layouts">
          <.sidebar_nav>
            <.sidebar_nav_group label="Starter" current={@current_path}>
              <:item navigate={~p"/starter/blurred"}>Blurred Header</:item>
            </.sidebar_nav_group>
          </.sidebar_nav>
        </.sidebar_panel>
      </:sidebar>
      <:header>
        <.header right_sidebar="right-sidebar">
          <:notifications>…</:notifications>
        </.header>
      </:header>
      <:aside>
        <.right_sidebar id="right-sidebar">…</.right_sidebar>
      </:aside>
      <.page_header title="Avatar">
        <:crumb navigate={~p"/elements"}>Elements</:crumb>
        <:crumb>Avatar</:crumb>
      </.page_header>
      …
    </.app_shell>

The state LineOne keeps in `<body>` classes lives on the shell's root element: `is-sidebar-open`,
`has-min-sidebar` (`layout="minimal"`), `navigation:sideblock` and `navigation:horizontal`,
`is-header-blur` and `is-header-not-sticky`.

## LiveView

`sidebar_open` is the server's state; the browser toggles without a round trip and tells the server through
`on_toggle`. A choice made in the browser survives patches and live navigation (`navigate` between LiveViews
rendering the same shell id) unless the server changes `sidebar_open`; at `xl` and up it is also kept in
`localStorage["linea-sidebar"]` (`persist`). Below `xl` the sidebar closes, as in LineOne.
`LineaUI.open/close/toggle(socket_or_js, "shell")` drive it from the server or a JS pipeline.

Active items come from `active` on each item or from `current` (the current path, compared with the item's
`navigate`, `patch` or `href`), so they follow live navigation.

Dark mode (`<html class="dark">`, `localStorage["dark-mode"]`) and monochrome mode (`<body class="is-monochrome">`,
`localStorage["monochrome-mode"]`) are client state; put `<.theme_script />` in the root layout's `<head>`
so the first paint already has them.

## Setup

The hooks (`LineaUI.AppShell`, `LineaUI.SidebarGroup`, `LineaUI.ShellPopper`, `LineaUI.ShellTooltips`,
`LineaUI.RightSidebar`, `LineaUI.ThemeToggle`, `LineaUI.MonochromeToggle`) come with LineaUI's `Hooks`.
Render the shell in the app layout of a `live_session` and give it the current path from an `on_mount`
hook, so the active items follow `navigate`:

    def on_mount(:current_path, _params, _session, socket) do
      {:cont,
       Phoenix.LiveView.attach_hook(socket, :current_path, :handle_params, fn _params, url, socket ->
         {:cont, assign(socket, :current_path, URI.parse(url).path)}
       end)}
    end

## Layouts

`layout="sidebar"` (the default) takes a `main_sidebar/1` and a `sidebar_panel/1`; `"minimal"` adds a
`sidebar_panel_min/1`; `"sideblock"` takes a `sideblock/1`; `"horizontal"` keeps the main sidebar and panel
for phones and puts `horizontal_nav_menu/1`s in the `:nav` slot of `header/1`.

# `app_shell`

The page frame: the sidebar, the header, the drawers and the `<main>` content, with LineOne's body classes.

`on_toggle` runs when the browser opens or closes the sidebar (a toggle button, a close button, a breakpoint
change, or a remembered choice applied on mount). An event name is pushed with
`%{"id" => id, "open" => boolean}`; a `%JS{}` is executed.

    <.app_shell id="shell" layout="minimal" sidebar_open={@sidebar_open} on_toggle="sidebar">
      <:sidebar>…</:sidebar>
      <:header><.header /></:header>
      <.page_header title="Orders" />
    </.app_shell>

    def handle_event("sidebar", %{"open" => open}, socket),
      do: {:noreply, assign(socket, :sidebar_open, open)}

## Attributes

* `id` (`:string`) - Defaults to `"app-shell"`.
* `layout` (`:string`) - `sidebar` is the main sidebar with its panel, `minimal` shows the minimized panel while the panel is closed, `sideblock` is one wide sidebar, `horizontal` puts the navigation in the header. Defaults to `"sidebar"`. Must be one of `"sidebar"`, `"minimal"`, `"sideblock"`, or `"horizontal"`.
* `sidebar_open` (`:boolean`) - Whether the sidebar is open; defaults to open for `sidebar` and `sideblock`. Defaults to `nil`.
* `header_blur` (`:boolean`) - LineOne's `is-header-blur`. Defaults to `true`.
* `header_sticky` (`:boolean`) - `false` is LineOne's `is-header-not-sticky`. Defaults to `true`.
* `persist` (`:boolean`) - Keep the sidebar choice in `localStorage`. Defaults to `true`.
* `preloader` (`:boolean`) - LineOne's app preloader, faded out on mount. Defaults to `false`.
* `on_toggle` (`:any`) - An event name or a `%JS{}`. Defaults to `nil`.
* `target` (`:any`) - The `phx-target` of `on_toggle`. Defaults to `nil`.
* `main_class` (`:any`) - Merged into the classes of `<main class="main-content">`. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `sidebar` - The main sidebar and its panel, or the sideblock.
* `header` - The `header/1`.
* `aside` - Fixed parts: the right sidebar, drawers.
* `inner_block`

# `header`

The header: LineOne's animated sidebar toggle, the search bar (and the full screen search of phones), the
dark mode and monochrome toggles, the notifications, the right sidebar toggle and a profile slot.

With a `:nav` slot it is LineOne's horizontal navigation header: the toggle only on phones, the `:brand`
on larger screens and the navigation row (`horizontal_nav_menu/1`) below.

    <.header right_sidebar="right-sidebar">
      <:start><.page_header title="Orders" class="py-0 lg:py-0" /></:start>
      <:search_results>…</:search_results>
      <:notifications>…</:notifications>
    </.header>

The search input takes `search_input` (a map of attributes: `name`, `value`, `phx-change`, `phx-debounce`…).

## Attributes

* `id` (`:string`) - Defaults to `"app-header"`.
* `toggle` (`:boolean`) - The sidebar toggle. Defaults to `true`.
* `search` (`:boolean`) - Defaults to `true`.
* `search_placeholder` (`:string`) - Defaults to `"Search here..."`.
* `search_input` (`:map`) - Defaults to `%{}`.
* `theme_toggle` (`:boolean`) - Defaults to `true`.
* `monochrome_toggle` (`:boolean`) - Defaults to `true`.
* `right_sidebar` (`:string`) - The id of the `right_sidebar/1` its button toggles. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `brand` - The logo and name of a horizontal header. Accepts attributes:

  * `href` (`:string`)
  * `navigate` (`:string`)
  * `src` (`:string`)
  * `label` (`:string`)
* `start` - Content after the toggle: a title, a breadcrumb.
* `search_results` - The box under the search bar, shown while it has the focus.
* `mobile_search` - What the full screen search of phones shows under its input.
* `actions` - Buttons before the theme toggle.
* `notifications` - The notification box; the bell opens it. Accepts attributes:

  * `dot` (`:boolean`) - LineOne's pinging dot; true by default.
  * `label` (`:string`)
* `profile` - Content at the end, such as an avatar menu.
* `nav` - The navigation row of a horizontal header.

# `horizontal_nav_divider`

The line between the links of a horizontal navigation box.

    <.horizontal_nav_divider />

## Attributes

* `class` (`:any`) - Defaults to `nil`.

# `horizontal_nav_link`

A link of a horizontal navigation box (inside `horizontal_nav_menu/1`). A click closes the box.

    <.horizontal_nav_link navigate={~p"/reports"} current={@current_path}>Reports</.horizontal_nav_link>

## Attributes

* `active` (`:boolean`) - Defaults to `false`.
* `current` (`:string`) - The current path; the link is active when it matches. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted. Supports all globals plus: `["href", "navigate", "patch", "method", "replace"]`.
## Slots

* `inner_block` (required)

# `horizontal_nav_menu`

A dropdown of the horizontal navigation (the `:nav` slot of `header/1`): an icon and a label, and a box of
links (`:item` slots, or `horizontal_nav_link/1` and `horizontal_nav_divider/1` as content). It is active
with `active`, or when one of its items is active or matches `current`.

    <.horizontal_nav_menu id="nav-pages" label="Pages" current={@current_path}>
      <:icon><svg>…</svg></:icon>
      <:item navigate={~p"/pages/blank"}>Blank</:item>
    </.horizontal_nav_menu>

## Attributes

* `id` (`:string`) (required)
* `label` (`:string`) (required)
* `active` (`:boolean`) - Defaults to `false`.
* `current` (`:string`) - Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `icon`
* `item` - Accepts attributes:

  * `href` (`:string`)
  * `navigate` (`:string`)
  * `patch` (`:string`)
  * `active` (`:boolean`)
* `inner_block`

# `main_sidebar`

The main sidebar: LineOne's icon rail with the logo, the section links (with a tooltip on the right and an
active state) and the bottom links.

    <.main_sidebar current={@current_path}>
      <:logo navigate={~p"/"} src={~p"/images/app-logo.svg"} />
      <:item navigate={~p"/forms"} label="Forms"><svg>…</svg></:item>
      <:bottom><.main_sidebar_link navigate={~p"/settings"}><svg>…</svg></.main_sidebar_link></:bottom>
    </.main_sidebar>

## Attributes

* `id` (`:string`) - Defaults to `"main-sidebar"`.
* `current` (`:string`) - The current path; an item whose link matches it is active. Defaults to `nil`.
* `centered` (`:boolean`) - Centers the links between the logo and the bottom. Defaults to `false`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `logo` - Accepts attributes:

  * `href` (`:string`)
  * `navigate` (`:string`)
  * `patch` (`:string`)
  * `src` (`:string`)
  * `alt` (`:string`)
* `item` - Accepts attributes:

  * `href` (`:string`)
  * `navigate` (`:string`)
  * `patch` (`:string`)
  * `label` (`:string`) - The tooltip and the accessible name.
  * `active` (`:boolean`)
* `bottom`

# `main_sidebar_link`

One icon link of the main sidebar. With `label` it gets LineOne's tooltip on its right (inside a
`main_sidebar/1`, which positions it) and that accessible name.

    <.main_sidebar_link navigate={~p"/settings"} label="Settings" current={@current_path}>
      <svg class="size-7">…</svg>
    </.main_sidebar_link>

## Attributes

* `label` (`:string`) - The tooltip and the accessible name. Defaults to `nil`.
* `active` (`:boolean`) - Defaults to `false`.
* `current` (`:string`) - The current path; the link is active when it matches. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted. Supports all globals plus: `["href", "navigate", "patch", "method", "replace"]`.
## Slots

* `inner_block` (required) - The icon.

# `monochrome_toggle`

LineOne's monochrome mode: the palette button of the header (`variant="button"`) or the switch of its
monochrome page (`variant="switch"`, the label as content). It toggles the `is-monochrome` class of `<body>`
and keeps it in `localStorage["monochrome-mode"]`; every toggle on the page follows. `on_change` gets
`%{"monochrome" => boolean}` or runs.

    <.monochrome_toggle id="mono" />
    <.monochrome_toggle id="mono-switch" variant="switch">Toggle Monochrome Mode</.monochrome_toggle>

## Attributes

* `id` (`:string`) - Defaults to `"monochrome-toggle"`.
* `variant` (`:string`) - Defaults to `"button"`. Must be one of `"button"`, or `"switch"`.
* `on_change` (`:any`) - Defaults to `nil`.
* `target` (`:any`) - Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block`

# `page_header`

The title of a page with LineOne's breadcrumb next to it: the crumbs with a link are followed by a
chevron, the last one is plain text.

    <.page_header title="Avatar">
      <:crumb navigate={~p"/elements"}>Elements</:crumb>
      <:crumb>Avatar</:crumb>
      <:actions><.button color="primary">New</.button></:actions>
    </.page_header>

## Attributes

* `title` (`:string`) (required)
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `crumb` - Accepts attributes:

  * `href` (`:string`)
  * `navigate` (`:string`)
  * `patch` (`:string`)
* `actions` - Buttons or filters at the right end of the row.
* `inner_block` - Content after the breadcrumb.

# `right_sidebar`

LineOne's right sidebar: a drawer from the right with an overlay, a top row (`:title` and the close button),
the content and a `:footer` (LineOne's floating tab bar).

Opened with `open`, `right_sidebar_toggle/1`, or `LineaUI.open/close/toggle(id)` (also from the server with
a socket). Esc, the overlay and the close button close it. `on_open` and `on_close` take an event name,
pushed with `%{"id" => id}`, or a `%JS{}`.

    <.right_sidebar id="right-sidebar" open={@panel_open} on_close="panel-closed">
      <:title><span class="text-xs">25 May, 2022</span></:title>
      <div class="px-3">…</div>
    </.right_sidebar>

## Attributes

* `id` (`:string`) (required)
* `open` (`:boolean`) - Defaults to `false`.
* `label` (`:string`) - Defaults to `"Sidebar"`.
* `on_open` (`:any`) - Defaults to `nil`.
* `on_close` (`:any`) - Defaults to `nil`.
* `target` (`:any`) - Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `title`
* `inner_block`
* `footer`

# `right_sidebar_toggle`

The header button (LineOne's grid icon) that toggles a `right_sidebar/1`; `header/1` renders one with
`right_sidebar="id"`.

    <.right_sidebar_toggle target="right-sidebar" />

## Attributes

* `target` (`:string`) (required)
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.

# `sidebar_min_divider`

The line between the lists of the minimized panel.

    <.sidebar_min_divider />

## Attributes

* `class` (`:any`) - Defaults to `nil`.

# `sidebar_min_group`

A list of icon links of the minimized panel; the first one keeps LineOne's top margin.

    <.sidebar_min_group>
      <.sidebar_min_link href="#" label="Home"><svg>…</svg></.sidebar_min_link>
    </.sidebar_min_group>

## Attributes

* `class` (`:any`) - Defaults to `nil`.
## Slots

* `inner_block` (required)

# `sidebar_min_link`

An icon link of the minimized panel. Active links are primary soft, the others take `color`.

    <.sidebar_min_link navigate={~p"/starred"} label="Starred" color="warning"><svg>…</svg></.sidebar_min_link>

## Attributes

* `label` (`:string`) - The accessible name. Defaults to `nil`.
* `color` (`:string`) - Defaults to `"neutral"`. Must be one of `"neutral"`, `"primary"`, `"secondary"`, `"info"`, `"success"`, `"warning"`, or `"error"`.
* `active` (`:boolean`) - Defaults to `false`.
* `current` (`:string`) - The current path; the link is active when it matches. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted. Supports all globals plus: `["href", "navigate", "patch", "method", "replace"]`.
## Slots

* `inner_block` (required)

# `sidebar_nav`

A list of the sidebar panel (or the sideblock): `sidebar_nav_link/1` and `sidebar_nav_group/1`.

    <.sidebar_nav>
      <.sidebar_nav_link navigate={~p"/orders"} current={@current_path}>Orders</.sidebar_nav_link>
    </.sidebar_nav>

## Attributes

* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `sidebar_nav_divider`

The line between the lists of the sidebar panel.

    <.sidebar_nav_divider />

## Attributes

* `class` (`:any`) - Defaults to `nil`.

# `sidebar_nav_group`

A collapsible group of the sidebar panel. It opens when one of its items is active (or with `open`); the
groups that share `name` open one at a time, like LineOne's accordion. A group opened or closed in the browser
stays so across patches unless the server changes `open`.

    <.sidebar_nav_group label="Starter" current={@current_path}>
      <:item navigate={~p"/starter/1"}>Blurred Header</:item>
      <:item navigate={~p"/starter/2"}>Unblurred Header</:item>
    </.sidebar_nav_group>

## Attributes

* `id` (`:string`) - Defaults to `sidebar-group-<label>`. Defaults to `nil`.
* `label` (`:string`) (required)
* `open` (`:boolean`) - Defaults to whether an item is active. Defaults to `nil`.
* `name` (`:string`) - Groups with the same name open one at a time. Defaults to `"sidebar-nav"`.
* `current` (`:string`) - The current path; the matching item is active. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `item` (required) - Accepts attributes:

  * `href` (`:string`)
  * `navigate` (`:string`)
  * `patch` (`:string`)
  * `active` (`:boolean`)

# `sidebar_nav_link`

A plain link of the sidebar panel, active with `active` or when its path is `current`.

    <.sidebar_nav_link navigate={~p"/orders"} current={@current_path}>Orders</.sidebar_nav_link>

## Attributes

* `active` (`:boolean`) - Defaults to `false`.
* `current` (`:string`) - The current path; the link is active when it matches. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted. Supports all globals plus: `["href", "navigate", "patch", "method", "replace"]`.
## Slots

* `inner_block` (required)

# `sidebar_panel`

The sidebar panel next to the main sidebar: its title, the close button LineOne shows below `xl`, and the
scrolling body (`sidebar_nav/1`, `sidebar_nav_divider/1`).

    <.sidebar_panel title="Elements">
      <.sidebar_nav>
        <.sidebar_nav_link navigate={~p"/avatar"} current={@current_path}>Avatar</.sidebar_nav_link>
      </.sidebar_nav>
      <.sidebar_nav_divider />
      <.sidebar_nav>…</.sidebar_nav>
    </.sidebar_panel>

## Attributes

* `title` (`:string`) (required)
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `sidebar_panel_min`

LineOne's minimized sidebar panel (`layout="minimal"`): shown next to the main sidebar while the panel is
closed. A header (an avatar), the icon links (`sidebar_min_group/1`, `sidebar_min_link/1`,
`sidebar_min_divider/1`) and a footer.

    <.sidebar_panel_min>
      <:header><div class="avatar size-10">…</div></:header>
      <.sidebar_min_group>
        <.sidebar_min_link navigate={~p"/inbox"} label="Inbox" current={@current_path}><svg>…</svg></.sidebar_min_link>
        <.sidebar_min_link navigate={~p"/trash"} label="Trash" color="error"><svg>…</svg></.sidebar_min_link>
      </.sidebar_min_group>
      <.sidebar_min_divider />
      <.sidebar_min_group>…</.sidebar_min_group>
      <:footer><.button variant="outline" shape="pill" icon aria-label="More">…</.button></:footer>
    </.sidebar_panel_min>

## Attributes

* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `header`
* `inner_block` (required)
* `footer`

# `sidebar_profile`

The avatar at the bottom of the main sidebar and the profile box it opens on its right: the person, a list
of links with a colored icon, and a footer (LineOne's logout button).

    <.sidebar_profile id="profile" src={@user.avatar} name="Travis Fuller" subtitle="Product Designer">
      <:item navigate={~p"/profile"} color="warning" title="Profile" description="Your profile setting">
        <svg>…</svg>
      </:item>
      <:footer><.button color="primary" class="h-9 w-full">Logout</.button></:footer>
    </.sidebar_profile>

A link of the box closes it.

## Attributes

* `id` (`:string`) (required)
* `src` (`:string`) (required)
* `alt` (`:string`) - Defaults to `"avatar"`.
* `name` (`:string`) (required)
* `subtitle` (`:string`) - Defaults to `nil`.
* `status` (`:string`) - Defaults to `"success"`. Must be one of `nil`, `"success"`, `"warning"`, `"error"`, `"info"`, or `"neutral"`.
* `href` (`:string`) - Defaults to `nil`.
* `navigate` (`:string`) - Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `item` - Accepts attributes:

  * `href` (`:string`)
  * `navigate` (`:string`)
  * `patch` (`:string`)
  * `method` (`:string`)
  * `color` (`:string`) - Must be one of `"primary"`, `"secondary"`, `"info"`, `"success"`, `"warning"`, or `"error"`.
  * `title` (`:string`) (required)
  * `description` (`:string`)
* `footer`

# `sidebar_toggle`

LineOne's animated hamburger: three bars that turn into an arrow while the sidebar is open. Inside an
`app_shell/1` it toggles the sidebar; `header/1` already renders one.

    <.sidebar_toggle />

## Attributes

* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.

# `sideblock`

LineOne's sideblock (`layout="sideblock"`): one wide sidebar with the logo, the close button and the
navigation (`sidebar_nav/1`). It goes in the `:sidebar` slot of `<.app_shell layout="sideblock">`.

    <.sideblock>
      <:logo navigate={~p"/"} src={~p"/images/app-logo.svg"} />
      <.sidebar_nav>
        <.sidebar_nav_group label="Orders" current={@current_path}>
          <:item navigate={~p"/orders"}>All orders</:item>
        </.sidebar_nav_group>
      </.sidebar_nav>
    </.sideblock>

## Attributes

* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `logo` - Accepts attributes:

  * `href` (`:string`)
  * `navigate` (`:string`)
  * `patch` (`:string`)
  * `src` (`:string`)
  * `alt` (`:string`)
* `inner_block` (required)

# `theme_script`

The script for the root layout's `<head>` that applies the stored dark mode before the first paint (LineOne's
"prevent flickering" script) and the stored monochrome mode as soon as `<body>` exists.

    <head>
      <.theme_script />
    </head>

## Attributes

* Global attributes are accepted.

# `theme_toggle`

LineOne's dark mode button: a moon in dark mode, a sun in light mode. It toggles the `dark` class of `<html>`
and keeps it in `localStorage["dark-mode"]`. `on_change` gets `%{"dark" => boolean}` (an event name) or runs
(a `%JS{}`).

    <.theme_toggle id="theme" on_change="theme-changed" />

## Attributes

* `id` (`:string`) - Defaults to `"theme-toggle"`.
* `on_change` (`:any`) - Defaults to `nil`.
* `target` (`:any`) - Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
