module ExpressAdmin class SmartTable < ExpressTemplates::Components::Base include ExpressTemplates::Components::Capabilities::Configurable include ExpressTemplates::Components::Capabilities::Resourceful MAX_COLS_TO_SHOW_IDX = 5 attr :columns emits -> { table(my[:id]).table.striped { thead { tr { display_columns.each do |column| th.send(column.name) { column.title } end actions_header if should_show_actions? hidden_columns_header_indicator if columns_hidden? } } tbody { for_each(collection, as: collection_member_name) { tr(row_args) { display_columns.each do |column| td.send(column.name) { cell_value(column.accessor) } end actions_column if should_show_actions? hidden_column_cell if columns_hidden? } } } } scroll_table if !!@config[:scroll_table] } def scroll_table script { %Q($('\##{my[:id]}').scrollTableBody()) } end def actions_header th.actions { 'Actions' } end def should_show_actions? @config[:actions].nil? || !!@config[:actions] end def actions_column td { link_to 'Delete', "{{#{resource_path}}}", method: :delete, data: {confirm: 'Are you sure?'}, class: 'button small secondary' } end def row_class @config[:row_class].try(:respond_to?, :call) ? "{{#{@config[:row_class].source}.call(#{collection_member_name})}}" : "{{#{collection_member_name}.eql?(@#{resource_name}) ? 'current' : ''}}" end def is_permanent? @config[:permanent].nil? || (@config[:permanent].present? && @config[:permanent]) end def row_id "#{collection_member_name}:{{#{collection_member_name}.id}}" end def row_args {id: row_id, class: row_class} end def cell_value(accessor) if accessor.respond_to?(:call) value = "(begin #{accessor.source}.call(#{collection_member_name}).to_s rescue 'Error: '+$!.to_s ; end)" elsif attrib = accessor.to_s.match(/(\w+)_link$/).try(:[], 1) value = "(link_to #{collection_member_name}.#{attrib}, #{collection_member_name}_path(#{collection_member_name}.id))" elsif attrib = accessor.to_s.match(/(\w+)_in_words/).try(:[], 1) value = "(#{collection_member_name}.#{attrib} ? time_ago_in_words(#{collection_member_name}.#{attrib})+' ago' : 'never')" else if relation_name = accessor.to_s.match(/(.*)_id$/).try(:[], 1) reflection = resource_class.reflect_on_association(relation_name.to_sym) end value = if reflection relation = "#{collection_member_name}.#{relation_name}" "#{relation}.try(:name) || #{relation}.to_s" else "#{collection_member_name}.#{accessor}" end end span { "{{#{value}}}" } end def display_columns specified_columns? ? columns : columns.slice(1..MAX_COLS_TO_SHOW_IDX) end def columns_hidden? !specified_columns? && columns.size > MAX_COLS_TO_SHOW_IDX+1 end class Column attr :name, :title, :accessor def initialize(accessor, title = nil) @name = accessor.kind_of?(Symbol) ? accessor.to_s.underscore : title.titleize.gsub(/\s+/,'').underscore @accessor = accessor @title = title || @name.titleize end end def columns @columns ||= _initialize_columns end def specified_columns? !!specified_columns end def specified_columns @config[:columns] end def hidden_columns_header_indicator th._more_columns_indicator { "..." } end def hidden_column_cell td._more_columns_indicator end private def _initialize_columns @columns = if specified_columns.kind_of?(Array) specified_columns.map { |name| Column.new(name) } elsif specified_columns.kind_of?(Hash) specified_columns.map { |title, accessor| Column.new(accessor, title) } else attributes.map { |column| Column.new(column.name.to_sym) } end end end end