Components

Lato provides a set of reusable functional components.
By using Lato components, you can save development time and simplify maintenance tasks.
All components are available via the Lato::ComponentsHelper helper.

Source code: Lato::ComponentsHelper

Navbar

Navbar nav item

Component to render a menu item in the navbar.
Parameters:
- key: the menu item's key. Used to activate the item via the active_navbar function from the Lato::Layoutable concern.
- path: a path or absolute URL to redirect the user.
- &block: block of HTML to render inside the menu item.

<%= lato_navbar_nav_item :account, lato.account_path do %>
  Account
<% end %>

Sidebar

Sidebar nav item

Component to render a menu item in the sidebar.
Parameters:
- key: the menu item's key. Used to activate the item via the active_sidebar function from the Lato::Layoutable concern.
- path: a path or absolute URL to redirect the user.
- &block: block of HTML to render inside the menu item.

<%= lato_sidebar_nav_item :account, lato.account_path do %>
  Account
<% end %>

Page

Page head

<%= lato_page_head 'Page title' %>

Page title

<%= lato_page_head 'Page title', [{ label: 'Title 1', path: main_app.page_path }, { label: 'Title 2' }] %>

Page title

<%= lato_page_head 'Page title' do %>
  <p class="lead">Welcome to this page</p>
<% end %>

Page title

Welcome to this page

Form

Forms can be generated using the lato_form components.
lato_form components are a set of utilities designed to work with Rails' form_with helper and the Stimulus controller lato_form_controller.
Below is a list of available components with usage examples:

  • lato_form_notices displays success feedback (notices set by the controller action).
  • lato_form_errors displays error feedback (based on model instance errors).
  • lato_form_item_label renders the field label.
  • lato_form_item_input_text renders a text input.
  • lato_form_item_input_number renders a number input.
  • lato_form_item_input_email renders an email input.
  • lato_form_item_input_password renders a password input.
  • lato_form_item_input_check renders a checkbox input.
  • lato_form_item_input_select renders a select input.
  • lato_form_item_input_file renders a file input.
  • lato_form_item_input_textarea renders a textarea input.
  • lato_form_item_input_date renders a date input.
  • lato_form_item_input_datetime renders a datetime input.
  • lato_form_item_input_time renders a time input.
  • lato_form_item_input_color renders a color input.
  • lato_form_submit renders the submit button.
<%

user ||= @session.user

%>

<%= turbo_frame_tag 'tutorial_form-example' do %>
  <%= form_with model: user, url: main_app.components_update_user_action_path, data: {
    turbo_frame: '_self',
    controller: 'lato-form'
  } do |form| %>
    <%= lato_form_notices class: %w[mb-3] %>
    <%= lato_form_errors user, class: %w[mb-3] %>

    <div class="row">
      <div class="col col-12 col-lg-6 mb-3">
        <%= lato_form_item_label form, :first_name, 'Name' %>
        <%= lato_form_item_input_text form, :first_name, required: true %>
      </div>

      <div class="col col-12 col-lg-6 mb-3">
        <%= lato_form_item_label form, :last_name, 'Surname' %>
        <%= lato_form_item_input_text form, :last_name, required: true %>
      </div>
    </div>

    <div class="row">
      <div class="col col-12 col-lg-6 mb-3">
        <%= lato_form_item_label form, :select, 'Select' %>
        <%= lato_form_item_input_select form, :select, [['Type 1', '1'], ['Type 2', '2']], required: true %>
      </div>

      <div class="col col-12 col-lg-6 mb-3">
        <%= lato_form_item_label form, :file, 'File' %>
        <%= lato_form_item_input_file form, :file, required: true %>
      </div>
    </div>

    <div class="row">
      <div class="col col-12 mb-3">
        <%= lato_form_item_label form, :textarea, 'Textarea' %>
        <%= lato_form_item_input_textarea form, :textarea, required: true %>
      </div>
    </div>

    <div class="row">
      <div class="col col-12 col-lg-4 mb-3">
        <%= lato_form_item_label form, :date, 'Date' %>
        <%= lato_form_item_input_date form, :date, required: true %>
      </div>

      <div class="col col-12 col-lg-4 mb-3">
        <%= lato_form_item_label form, :datetime, 'Datetime' %>
        <%= lato_form_item_input_datetime form, :datetime, required: true %>
      </div>

      <div class="col col-12 col-lg-4 mb-3">
        <%= lato_form_item_label form, :time, 'Time' %>
        <%= lato_form_item_input_time form, :time, required: true %>
      </div>
    </div>

    <div class="row">
      <div class="col col-12 mb-3">
        <%= lato_form_item_label form, :color, 'Color' %>
        <%= lato_form_item_input_color form, :color, required: true %>
      </div>
    </div>

    <div class="row">
      <div class="col col-12 mb-3">
        <%= lato_form_item_label form, :first_name, 'Name with auto-complete' %>
        <%= lato_form_item_input_text form, :first_name, required: true, data: {
          controller: 'lato-input-autocomplete',
          lato_input_autocomplete_path_value: products_autocomplete_path
        } %>
      </div>
    </div>

    <div class="row">
      <div class="col col-12 mb-3">
        <%= lato_form_item_label form, :first_name, 'Name with auto-complete V2' %>
        <%= lato_form_item_input_text form, :first_name, required: true, data: {
          controller: 'lato-input-autocomplete2',
          lato_input_autocomplete2_path_value: products_autocomplete_path
        } %>
      </div>
    </div>

    <div class="d-flex justify-content-end">
      <%= lato_form_submit form, 'Update', class: %w[btn-success] %>
    </div>
  <% end %>
<% end %>

Index

The lato_index component allows easy visualization of database records.

Index (front-end)

The simplest way to use the component is to pass it an ActiveRecord collection:

<%= lato_index Product.all.limit(5) %>
Id
Code
Status
Lato user
Created at
Updated at
Product parent
1 T0196172H Completed
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
2 S8631447I Cancelled
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
3 S8045197J Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
4 S7634215F Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
5 S7964049B In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
5 total results

Customizing columns

Columns can be customized using the :columns parameter:

<%= lato_index Product.all.limit(5), columns: %i[id code status] %>
Id
Code
Status
1 T0196172H Completed
2 S8631447I Cancelled
3 S8045197J Created
4 S7634215F Created
5 S7964049B In progress
5 total results

Notes

Column titles can be customized using Rails translation files.
Example:

en:
  activerecord:
    attributes:
      product:
        code: Code
        status: Status

Customizing column content

You can customize column content by creating helper methods with the format model_column(record).
For example, to customize the output of the :lato_user_id column, define the following helper in ProductsHelper:

class ProductsHelper
  def product_lato_user_id(product)
    product.lato_user.full_name
  end
end

Adding custom columns

To add custom columns, declare a method in the model that provides the value.
For example, after adding a lifetime method to the Product model, you can use the :lifetime column:

class Product < ApplicationRecord
  def lifetime
    Time.now - created_at
  end
end
class ProductsHelper
  def product_lifetime(product)
    Time.at(product.lifetime).utc.strftime('%H h %M m')
  end
end
<%= lato_index Product.all.limit(5), columns: %i[id code status lifetime] %>
Id
Code
Status
Lifetime
1 T0196172H Completed 18 h 05 m
2 S8631447I Cancelled 18 h 05 m
3 S8045197J Created 18 h 05 m
4 S7634215F Created 18 h 05 m
5 S7964049B In progress 18 h 05 m
5 total results

Notes

Custom columns can also be translated using Rails translation files, just like database-backed columns.

To add columns that don't map to real attributes, use attr_accessor :column_name instead of defining a method in the model:

class Product < ApplicationRecord
  attr_accessor :lifetime
end

Adding actions

<%= lato_index Product.all.limit(5), custom_actions: {
    create: {
      path: products_create_path,
      icon: 'bi bi-plus',
      aria_label: 'Create product',
      title: 'Create product'
    }
 } %>
Id
Code
Status
Lato user
Created at
Updated at
Product parent
1 T0196172H Completed
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
2 S8631447I Cancelled
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
3 S8045197J Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
4 S7634215F Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
5 S7964049B In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
5 total results

Adding in-page actions

<%= lato_index Product.all.limit(5), custom_actions: {
    create: {
      path: products_create_path,
      icon: 'bi bi-plus',
      data: { lato_action_target: 'trigger', turbo_frame: dom_id(Product.new, 'form') },
      aria_label: 'Create product',
      title: 'Create product'
    }
 } %>
Id
Code
Status
Lato user
Created at
Updated at
Product parent
1 T0196172H Completed
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
2 S8631447I Cancelled
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
3 S8045197J Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
4 S7634215F Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
5 S7964049B In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
5 total results

Notes

The :create_turbo_frame option specifies the turbo-frame ID in the products_create_path page that should be inserted into the overlay.

Adding row-specific actions

Row-specific actions can be added through custom columns (as shown above).
These actions can also be opened in-page via overlays using data-lato-index-target="action" and data-turbo-frame="turbo-frame-ID".

class ProductsHelper
  def product_actions(product)
    content_tag(:div, class: 'btn-group btn-group-sm') do
      concat link_to(
        'Edit',
        products_update_path(product),
        class: 'btn btn-primary',
        data: { lato_action_target: 'trigger', turbo_frame: dom_id(product, 'form') }
      )
    end
  end
end
<%= lato_index Product.all.limit(5), columns: %i[id code status lifetime actions] %>
Id
Code
Status
Lifetime
Actions
1 T0196172H Completed 18 h 05 m
2 S8631447I Cancelled 18 h 05 m
3 S8045197J Created 18 h 05 m
4 S7634215F Created 18 h 05 m
5 S7964049B In progress 18 h 05 m
5 total results

Index (back-end)

To make the index fully interactive, use the lato_index_collection function from the Lato::Componentable concern in the controller.

def index
  @products = lato_index_collection(Product.all, pagination: true)
end
<%= lato_index @products %>
Id
Code
Status
Lato user
Created at
Updated at
Product parent
1 T0196172H Completed
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
2 S8631447I Cancelled
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
3 S8045197J Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
4 S7634215F Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
5 S7964049B In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
6 T0177097C Completed
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
7 S9119897E Completed
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
8 S9310289D Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
9 S8879757D In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
10 T0227585B Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
11 S6481543A In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
12 S7837484E Completed
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
13 S6311281Z In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
14 S8148809F Completed
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
15 S8186570A Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
16 T0265341E Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
17 S7223859A Created
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
18 S6069124Z Cancelled
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
19 S9888358D Completed
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
20 S7789196Z In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
21 T0525625E In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
22 S8143709B In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
23 S7371530Z Cancelled
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
24 S7675078E In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
25 S8712790G In progress
Admin Admin
28/05/2025 2025-05-28 12:05:14 UTC -
100 total results

Customizing columns

def index
  @products_columns = lato_index_collection(
    Product.all,
    pagination: true,
    columns: %i[code status id]
  )
end
<%= lato_index @products_columns %>
Code
Status
Id
T0196172H Completed 1
S8631447I Cancelled 2
S8045197J Created 3
S7634215F Created 4
S7964049B In progress 5
T0177097C Completed 6
S9119897E Completed 7
S9310289D Created 8
S8879757D In progress 9
T0227585B Created 10
S6481543A In progress 11
S7837484E Completed 12
S6311281Z In progress 13
S8148809F Completed 14
S8186570A Created 15
T0265341E Created 16
S7223859A Created 17
S6069124Z Cancelled 18
S9888358D Completed 19
S7789196Z In progress 20
T0525625E In progress 21
S8143709B In progress 22
S7371530Z Cancelled 23
S7675078E In progress 24
S8712790G In progress 25
100 total results

Customizing pagination size

Users can choose how many items to display per page using a selector enabled via the :pagination_options parameter in lato_index_collection.

def index
  @products_pagination_options = lato_index_collection(
    Product.all,
    pagination: 10,
    columns: %i[code status id],
    key: 'products_pagination_options'
  )
end
<%= lato_index @products_pagination_options, pagination_options: [10,20,50,100] %>
Code
Status
Id
T0196172H Completed 1
S8631447I Cancelled 2
S8045197J Created 3
S7634215F Created 4
S7964049B In progress 5
T0177097C Completed 6
S9119897E Completed 7
S9310289D Created 8
S8879757D In progress 9
T0227585B Created 10
100 total results

Making columns sortable

def index
  @products_sortable_columns = lato_index_collection(
    Product.all,
    pagination: true,
    columns: %i[code status lato_user_id],
    sortable_columns: %i[code status lato_user_id]
  )
end
<%= lato_index @products_sortable_columns %>
Code
Status
Lato user
T0196172H Completed
Admin Admin
S8631447I Cancelled
Admin Admin
S8045197J Created
Admin Admin
S7634215F Created
Admin Admin
S7964049B In progress
Admin Admin
T0177097C Completed
Admin Admin
S9119897E Completed
Admin Admin
S9310289D Created
Admin Admin
S8879757D In progress
Admin Admin
T0227585B Created
Admin Admin
S6481543A In progress
Admin Admin
S7837484E Completed
Admin Admin
S6311281Z In progress
Admin Admin
S8148809F Completed
Admin Admin
S8186570A Created
Admin Admin
T0265341E Created
Admin Admin
S7223859A Created
Admin Admin
S6069124Z Cancelled
Admin Admin
S9888358D Completed
Admin Admin
S7789196Z In progress
Admin Admin
T0525625E In progress
Admin Admin
S8143709B In progress
Admin Admin
S7371530Z Cancelled
Admin Admin
S7675078E In progress
Admin Admin
S8712790G In progress
Admin Admin
100 total results

Notes

To customize sorting logic, define the lato_index_order scope inside the model.
Example:

class Product < ApplicationRecord
  belongs_to :lato_user, class_name: 'Lato::User'

  scope :lato_index_order, ->(column, order) do
    return joins(:lato_user).order("lato_users.last_name #{order}, lato_users.first_name #{order}") if column == :lato_user_id

    order("#{column} #{order}")
  end
end

Notes

To set a default sorting column, use the default_sort_by option in the lato_index_collection method.
Example:

def index
  @products_default_sort = lato_index_collection(
    Product.all,
    pagination: true,
    columns: %i[code status lato_user_id],
    sortable_columns: %i[code status lato_user_id],
    default_sort_by: 'code|asc' # 'column|direction'
  )
end

Making columns searchable

def index
  @products_searchable_columns = lato_index_collection(
    Product.all,
    pagination: true,
    columns: %i[code status lato_user_id],
    sortable_columns: %i[code status lato_user_id],
    searchable_columns: %i[code lato_user_id]
  )
end
<%= lato_index @products_searchable_columns %>
Code
Status
Lato user
T0196172H Completed
Admin Admin
S8631447I Cancelled
Admin Admin
S8045197J Created
Admin Admin
S7634215F Created
Admin Admin
S7964049B In progress
Admin Admin
T0177097C Completed
Admin Admin
S9119897E Completed
Admin Admin
S9310289D Created
Admin Admin
S8879757D In progress
Admin Admin
T0227585B Created
Admin Admin
S6481543A In progress
Admin Admin
S7837484E Completed
Admin Admin
S6311281Z In progress
Admin Admin
S8148809F Completed
Admin Admin
S8186570A Created
Admin Admin
T0265341E Created
Admin Admin
S7223859A Created
Admin Admin
S6069124Z Cancelled
Admin Admin
S9888358D Completed
Admin Admin
S7789196Z In progress
Admin Admin
T0525625E In progress
Admin Admin
S8143709B In progress
Admin Admin
S7371530Z Cancelled
Admin Admin
S7675078E In progress
Admin Admin
S8712790G In progress
Admin Admin
100 total results

Notes

To customize search logic, define the lato_index_search scope inside the model.
Example:

class Product < ApplicationRecord
  belongs_to :lato_user, class_name: 'Lato::User'

  scope :lato_index_search, ->(search) do
    joins(:lato_user).where("
      lower(code) LIKE :search OR
      lower(lato_users.first_name) LIKE :search OR
      lower(lato_users.last_name) LIKE :search
    ", search: "%#{search.downcase.strip}%")
  end
end
You are offline You are online