defmodule BitstylesPhoenix.Component.Sidebar do use BitstylesPhoenix.Component @moduledoc """ The sidebar component without any JS. """ story( "Sidebar with items", """ iex> assigns = %{} ...> render ~H\""" ...> <.ui_sidebar_layout> ...> <:large_sidebar>Large header ...> <:small_sidebar>Small header ...> <:sidebar_content data-theme="dark"> ...> <.ui_sidebar_section border="bottom"> ...> Menu ...> ...> <.ui_sidebar_nav> ...> <.ui_sidebar_nav_item><.ui_button href="#" class="u-flex-grow-1" color="transparent">Menu item #1 ...> <.ui_sidebar_nav_item><.ui_button href="#" class="u-flex-grow-1" color="transparent">Menu item #2 ...> ...> <.ui_sidebar_section border="top"> ...> <.ui_dropdown variant={["top", "full-width"]}> ...> <:button color="transparent"> ...>
...> Jane Dobermann’s avatar ...>
...> Jane Dobermann ...> ...> <:menu> ...> <.ui_dropdown_option href="#">Logout ...> ...> ...> ...> ...> Main Content ...> ...> \""" """, """ \"""
Main Content
\""" """, width: "100%", height: "500px", transparent: false, module: true ) @doc """ Renders a sidebar component. *In order for the sidebar to be collapsible in small screens, you need to provide extra JS attributes.* ## Attributes - `class` - Extra classes to pass to the outer `div` See `BitstylesPhoenix.Helper.classnames/1` for usage. - All other attributes are passed on to the outer `div` The sidebar comes with 4 slots: - `large_sidebar` - Content that is only shown on large screens in the sidebar - `small_sidebar` - Content that is only shown on small screens in the sidebar - `sidebar_content` - Content that is shown on all screens in the sidebar - `main` - The main content of the page (next to the sidebar) Instead of the `main` slot you can also just use the inner content of the sidebar, but the slot is valuable if you want to provide extra attributes on the `main` tag. The `large_siebar` and `small_sidebar` slots are displayed before the `sidebar_content` in the layout, since typically they host the logo/header/brand name, while the main navigation is hosted in the `sidebar_content` slot and shown on all screens. The reason for this separation is that the sidebar in the small screen is meant to start out hidden and only be shown when needed and therefore needs control buttons to close it again (usually at the top of the screen). If you have different requirements you can simply omit the `sidebar_content` block and render the shared content twice yourself. # Attributes - `small_sidebar` slot - `class` - Extra classes to pass to the div containing the small sidebar See `BitstylesPhoenix.Helper.classnames/1` for usage. - `fg` - The forground color class for the text. Defaults to `gray-30` resulting in `fg-gray-30`. - `bg` - The background color class for the sidebar. Defaults to `gray-80` resulting in `fg-gray-80`. - All other attributes are passed on to the small sidebar `div` # Attributes - `large_sidebar` slot - `class` - Extra classes to pass to the div containing the large sidebar See `BitstylesPhoenix.Helper.classnames/1` for usage. - `fg` - The forground color class for the text. Defaults to `gray-30` resulting in `fg-gray-30`. - `bg` - The background color class for the sidebar. Defaults to `gray-80` resulting in `fg-gray-80`. - All other attributes are passed on to the large sidebar `div` # Attributes - `sidebar_content` slot - `class` - Extra classes to pass to the div containing the large and small sidebar See `BitstylesPhoenix.Helper.classnames/1` for usage. - All other attributes are passed on to the large and small sidebar `div`s # Attributes - `main` slot - `class` - Extra classes to pass to the `main` tag. See `BitstylesPhoenix.Helper.classnames/1` for usage. - All other attributes are passed on to the main tag. See the [bitstyles sidebar docs](https://bitcrowd.github.io/bitstyles/?path=/docs/ui-navigation-sidebar--sidebar) for more examples. """ story( "Bare sidebar", """ iex> assigns = %{} ...> render ~H\""" ...> <.ui_sidebar_layout> ...> <:large_sidebar>Large header ...> <:small_sidebar>Small header ...> <:sidebar_content>
Sidebar
...> <:main>Main Content ...> ...> \""" """, """ \"""
Main Content
\""" """, width: "100%", height: "300px", transparent: false ) story( "All slots are optional", """ iex> assigns = %{} ...> render ~H\""" ...> <.ui_sidebar_layout> ...> Main Content ...> ...> \""" """, """ \"""
Main Content
\""" """, width: "100%", height: "300px", transparent: false ) @default_bg "grayscale-dark-2" @default_fg "grayscale-light-2" @large_classes "u-hidden o-sidebar--large u-flex-shrink-0 u-padding-m-top u-flex@l u-flex-col" @small_classes "o-sidebar--small u-flex u-flex-col u-hidden@l" def ui_sidebar_layout(assigns) do {sidebar, sidebar_extra} = assigns_from_single_slot(assigns, :sidebar_content, exclude: [:class, :bg, :fg], optional: true ) {large_sidebar, large_extra} = assigns_from_single_slot(assigns, :large_sidebar, exclude: [:class, :bg, :fg], optional: true ) {small_sidebar, small_extra} = assigns_from_single_slot(assigns, :small_sidebar, exclude: [:class, :bg, :fg], optional: true ) {main, main_extra} = assigns_from_single_slot(assigns, :main, exclude: [:class], optional: true) extra = assigns_to_attributes(assigns, [ :class, :sidebar_content, :large_sidebar, :small_sidebar, :main ]) assigns = assigns |> assign( class: classnames(["u-flex u-height-100vh", assigns[:class]]), extra: extra, sidebar_extra: sidebar_extra, large_extra: large_extra, small_extra: small_extra, main_extra: main_extra, main_class: classnames(["u-flex-grow-1 u-overflow-y-auto", main[:class]]), large_class: sidebar_classnames(large_sidebar, sidebar, @large_classes), small_class: sidebar_classnames(small_sidebar, sidebar, @small_classes) ) ~H"""
<%= render_slot(assigns[:main] || @inner_block) %>
""" end defp sidebar_classnames(sidebar_layout, sidebar, default_classes) do sidebar_layout = sidebar_layout || %{} bg = Map.get(sidebar_layout, :bg, @default_bg) fg = Map.get(sidebar_layout, :fg, @default_fg) classnames([ default_classes, {"u-bg-#{bg}", !!bg}, {"u-fg-#{fg}", !!fg}, sidebar[:class], sidebar_layout[:class] ]) end @doc """ A navigation menu for usage in the sidebar. ## Attributes - `class` - Extra classes to pass to the outer `ul` See `BitstylesPhoenix.Helper.classnames/1` for usage. - All other attributes are passed on to the outer `ul` You can add items `ui_sidebar_nav_item/1` to add items to the navigation. """ @sidebar_nav_classes "u-flex-grow-1 u-flex-shrink-1 u-overflow-y-auto u-list-none u-flex " <> "u-flex-col u-items-stretch u-padding-s4-right u-padding-s4-left" def ui_sidebar_nav(assigns) do extra = assigns_to_attributes(assigns, [:class]) class = classnames([@sidebar_nav_classes, assigns[:class]]) assigns = assign(assigns, extra: extra, class: class) ~H""" """ end @doc """ A navigation menu item for usage in `ui_sidebar_nav/1`. ## Attributes - `class` - Extra classes to pass to the `li` See `BitstylesPhoenix.Helper.classnames/1` for usage. The component is meant to be used with `ui_button` to add links here like showcased in the top level module documentation. """ def ui_sidebar_nav_item(assigns) do extra = assigns_to_attributes(assigns, [:class]) class = classnames(["u-margin-s4-bottom u-flex", assigns[:class]]) assigns = assign(assigns, extra: extra, class: class) ~H"""
  • <%= render_slot(@inner_block) %>
  • """ end @doc """ A navigation section with a bottom or top border. See top level module doc for an example. ## Attributes - `class` - Extra classes to pass to the outer `div` See `BitstylesPhoenix.Helper.classnames/1` for usage. - `border` - Either `:top` or `:bottom` (optional) - `border_color` - The border color, defaults to `gray-dark` resulting in `u-border-gray-dark-top` for a `:top` border. - All other attributes are passed on to the outer `div` """ @sidebar_section_classes "u-flex-shrink-0 u-padding-s4-y u-margin-s4-left u-margin-s4-right u-flex u-flex-col" @sidebar_section_default_border_color "gray-dark" def ui_sidebar_section(assigns) do border_color = Map.get(assigns, :border_color, @sidebar_section_default_border_color) border = Map.get(assigns, :border) class = classnames([ @sidebar_section_classes, assigns[:class], {"u-border-#{border_color}-#{border} u-margin-s4-#{border}", !!border} ]) extra = assigns_to_attributes(assigns, [:class, :border, :border_color]) assigns = assign(assigns, extra: extra, class: class) ~H"""
    <%= render_slot(@inner_block) %>
    """ end end