# frozen_string_literal: true module Ariadne # The `TableNavComponent` is used to render a table navigation. class TableNavComponent < Ariadne::Component DEFAULT_CLASSES = "ariadne-min-w-full ariadne-divide-y ariadne-divide-gray-300" renders_one :header_row, "BaseRowItem::HeaderRowItem" renders_many :rows, "BaseRowItem::RowItem" renders_one :footer, "FooterItem" DEFAULT_TAG = :table # @example Default # # <%= render(Ariadne::TableNavComponent.new) do |table| %> # <%= table.header_row do |header_row| %> # <% header_row.selection_cell do %> # Status # <% end %> # <% header_row.main_cell do %> # State # <% end %> # <% header_row.action_cell do %> # Labels # <% end %> # <% end %> # <%= table.row do |row| %> # <% row.selection_cell do %> # G # <% end %> # <% row.main_cell do %> # "California" # <% end %> # <% row.metadata_cell do %> # Labels # <% end %> # <% end %> # <%= table.row do |row| %> # <% row.selection_cell do %> # V # <% end %> # <% row.main_cell do %> # "New York" # <% end %> # <% row.metadata_cell do %> # Labels # <% end %> # <% end %> # <%= table.row do |row| %> # <% row.cell do %> # D # <% end %> # <% row.selection_cell do %> # "Texas" # <% end %> # <% row.metadata_cell do %> # Labels # <% end %> # <% end %> # <% end %> # # @param classes [String] <%= link_to_classes_docs %> # @param attributes [Hash] <%= link_to_attributes_docs %> def initialize(classes: "", attributes: {}) @tag = DEFAULT_TAG @classes = class_names( DEFAULT_CLASSES, classes, ) @attributes = attributes end def has_header_row? header_row.present? end def has_footer? footer.present? end # This component is part of `TableNavComponent` and should not be # used as a standalone component. class BaseRowItem < Ariadne::TableNavComponent BASE_ROW_CLASSES = "" DEFAULT_TAG = :tr BASE_SELECTION_CLASSES = "" renders_one :selection_cell, lambda { |classes: "", attributes: {}| actual_classes = class_names(BASE_SELECTION_CLASSES, classes) if header? Ariadne::TableNavComponent::BaseCellItem::HeaderCellItem.new(classes: actual_classes, attributes: attributes) else Ariadne::TableNavComponent::BaseCellItem::CellItem.new(classes: actual_classes, attributes: attributes) end } BASE_MAIN_CLASSES = "ariadne-pr-5" renders_one :main_cell, lambda { |classes: "", attributes: {}| actual_classes = class_names(BASE_MAIN_CLASSES, classes) if header? Ariadne::TableNavComponent::BaseCellItem::HeaderCellItem.new(classes: actual_classes, attributes: attributes) else Ariadne::TableNavComponent::BaseCellItem::CellItem.new(classes: actual_classes, attributes: attributes) end } attr_reader :classes, :attributes def initialize(classes: "", attributes: {}) @tag = DEFAULT_TAG @classes = class_names(BASE_ROW_CLASSES, classes) @attributes = attributes end private def linked? @href.present? end private def header? @header.present? end def call Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) end # This component is part of `TableNavComponent` and should not be # used as a standalone component. class RowItem < Ariadne::TableNavComponent::BaseRowItem DEFAULT_ROW_CLASSES = "ariadne-bg-white" DEFAULT_METADATA_CLASSES = "" renders_many :metadata_cells, lambda { |classes: "", attributes: {}| actual_classes = class_names(DEFAULT_METADATA_CLASSES, classes) Ariadne::TableNavComponent::BaseCellItem::CellItem.new(classes: actual_classes, attributes: attributes) } attr_reader :href def initialize(classes: "", attributes: {}) @header = false actual_classes = class_names(DEFAULT_ROW_CLASSES, classes) super(classes: actual_classes, attributes: attributes) end end # This component is part of `TableNavComponent` and should not be # used as a standalone component. class HeaderRowItem < Ariadne::TableNavComponent::BaseRowItem DEFAULT_HEADER_ROW_CLASSES = "ariadne-bg-gray-50 ariadne-text-left" renders_many :action_cells, lambda { |classes: "", attributes: {}| Ariadne::TableNavComponent::BaseCellItem::HeaderCellItem.new(classes: classes, attributes: attributes) } def initialize(classes: "", attributes: {}) @header = true actual_classes = class_names( DEFAULT_HEADER_ROW_CLASSES, classes, ) super(classes: actual_classes, attributes: attributes) end end end # This component is part of `TableNavComponent` and should not be # used as a standalone component. class BaseCellItem < Ariadne::TableNavComponent DEFAULT_CELL_CLASSES = "ariadne-py-3.5 ariadne-pl-4 ariadne-pr-3 ariadne-text-left ariadne-text-sm ariadne-text-gray-900" attr_writer :first, :last def initialize(classes: "", attributes: {}) @classes = class_names( DEFAULT_CELL_CLASSES, classes, ) @attributes = attributes end def call render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do content end end # This component is part of `TableNavComponent` and should not be # used as a standalone component. class HeaderCellItem < Ariadne::TableNavComponent::BaseCellItem DEFAULT_HEADER_CELL_CLASSES = "ariadne-py-3.5 ariadne-pl-4 ariadne-pr-3 ariadne-text-left ariadne-text-sm ariadne-font-semibold ariadne-text-gray-900 sm:ariadne-pl-6 md:ariadne-pl-0" DEFAULT_TAG = :th WIDTH_CLASSES = { none: "ariadne-flex-none ariadne-w-14 ariadne-px-4", default: "ariadne-flex-initial", narrow: "ariadne-flex-initial ariadne-w-1/5 ariadne-text-right", wide: "ariadne-flex-grow ariadne-w-3/5 sm:ariadne-pl-6", } # TODO: add one_of check for width def initialize(classes: "", attributes: {}) @tag = DEFAULT_TAG actual_classes = class_names(DEFAULT_HEADER_CELL_CLASSES, classes) attributes["scope"] = "col" super(classes: actual_classes, attributes: attributes) end end # This component is part of `TableNavComponent` and should not be # used as a standalone component. class CellItem < Ariadne::TableNavComponent::BaseCellItem DEFAULT_CELL_CLASSES = "" DEFAULT_TAG = :td # TODO: add one_of check for width def initialize(classes: "", attributes: {}) @tag = DEFAULT_TAG actual_classes = class_names(DEFAULT_CELL_CLASSES, classes) super(classes: actual_classes, attributes: attributes) end end end # This component is part of `TableNavComponent` and should not be # used as a standalone component. class FooterItem < Ariadne::TableNavComponent DEFAULT_FOOTER_CLASSES = "ariadne-border-none ariadne-flex ariadne-items-center ariadne-justify-between ariadne-px-4 ariadne-py-3 sm:ariadne-px-6" DEFAULT_RESULT_CLASSES = "ariadne-text-sm ariadne-text-gray-700" renders_one :records_info, lambda { |classes: "", attributes: {}| actual_classes = class_names(DEFAULT_RESULT_CLASSES, classes) Ariadne::BaseComponent.new(tag: :p, classes: actual_classes, attributes: attributes) } renders_one :pagination_bar, "Ariadne::TableNavComponent::PaginationBarItem" attr_reader :classes, :attributes def initialize(classes: "", attributes: {}) @classes = class_names( DEFAULT_FOOTER_CLASSES, classes, ) @attributes = attributes end def call render(Ariadne::BaseComponent.new(tag: :div, classes: @classes, attributes: @attributes)) do records_info.to_s + pagination_bar.to_s end end end # This component is part of `TableNavComponent` and should not be # used as a standalone component. class PaginationBarItem < Ariadne::FooterComponent DEFAULT_PREV_PAGE_CLASSES = "ariadne-relative ariadne-inline-flex ariadne-items-center ariadne-rounded-l-md ariadne-border ariadne-border-gray-300 ariadne-bg-white ariadne-px-2 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-gray-500 hover:ariadne-bg-gray-50 focus:ariadne-z-20" renders_one :prev_page, lambda { |disabled: false, href:, classes: "", attributes: {}| if disabled actual_classes = class_names(DEFAULT_PREV_PAGE_CLASSES, "ariadne-bg-gray-50", classes) render(Ariadne::BaseComponent.new(tag: :span, classes: actual_classes, attributes: attributes)) do render(Ariadne::HeroiconComponent.new(icon: "chevron-left", size: :sm, variant: :mini, text_attributes: { "aria-hidden": true }, text_classes: "ariadne-sr-only")) end else actual_classes = class_names(DEFAULT_PREV_PAGE_CLASSES, "hover:ariadne-bg-gray-50", classes) attributes[:"aria-label"] = "previous" render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) do render(Ariadne::HeroiconComponent.new(icon: "chevron-left", size: :sm, variant: :mini, text_attributes: { "aria-hidden": true }, text_classes: "ariadne-sr-only")) end end } DEFAULT_PAGE_CLASSES = "ariadne-relative ariadne-inline-flex ariadne-items-center ariadne-border ariadne-border-gray-300 ariadne-bg-white ariadne-px-4 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-gray-500 hover:ariadne-bg-gray-50 focus:ariadne-z-20" DEFAULT_CURRENT_PAGE_CLASSES = "ariadne-relative ariadne-z-10 ariadne-inline-flex ariadne-items-center ariadne-border ariadne-border-slate-500 ariadne-bg-slate-50 ariadne-px-4 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-slate-600 focus:ariadne-z-20" DEFAULT_GAP_CLASSES = " ariadne-relative ariadne-inline-flex ariadne-items-center ariadne-border ariadne-border-gray-300 ariadne-bg-white ariadne-px-4 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-gray-700" renders_many :items, lambda { |link:, classes: "", attributes: {}| page, href = link if page.is_a?(Integer) actual_classes = class_names(DEFAULT_PAGE_CLASSES, classes) render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) { page.to_s } elsif page.is_a?(String) actual_classes = class_names(DEFAULT_CURRENT_PAGE_CLASSES, classes) render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) { page.to_s } elsif page == :gap actual_classes = class_names(DEFAULT_GAP_CLASSES, classes) render(Ariadne::BaseComponent.new(tag: :span, classes: actual_classes, attributes: attributes)) { h(href.to_s) } end } DEFAULT_NEXT_PAGE_CLASSES = "ariadne-relative ariadne-inline-flex ariadne-items-center ariadne-rounded-r-md ariadne-border ariadne-border-gray-300 ariadne-bg-white ariadne-px-2 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-gray-500 hover:ariadne-bg-gray-50 focus:ariadne-z-20" renders_one :next_page, lambda { |disabled: false, href:, classes: "", attributes: {}| if disabled actual_classes = class_names(DEFAULT_NEXT_PAGE_CLASSES, "ariadne-bg-gray-50", classes) render(Ariadne::BaseComponent.new(tag: :span, classes: actual_classes, attributes: attributes)) do render(Ariadne::HeroiconComponent.new(icon: "chevron-right", size: :sm, variant: :mini, text_attributes: { "aria-hidden": true }, text_classes: "ariadne-sr-only")) end else actual_classes = class_names(DEFAULT_NEXT_PAGE_CLASSES, "hover:ariadne-bg-gray-50", classes) attributes[:"aria-label"] = "next" render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) do render(Ariadne::HeroiconComponent.new(icon: "chevron-right", size: :sm, variant: :mini, text_attributes: { "aria-hidden": true }, text_classes: "ariadne-sr-only")) end end } attr_reader :classes, :attributes DEFAULT_PAGINATOR_CLASSES = "ariadne-flex ariadne-items-center ariadne-justify-between ariadne-m-10" def initialize(classes: "", attributes: {}) @classes = class_names( DEFAULT_PAGINATOR_CLASSES, classes, ) @attributes = attributes @attributes[:"aria-label"] ||= "paginator" end end end end