Infinite scroll — loads more content as you scroll. No pagination controls.
Filters
Drag to reorder, pin columns, and show/hide:
Table definition
app/infotable/posts_table.rb
class PostsTable < Infotable::Base
query do
Post.includes(:author, :comments)
end
column :id,
label: "ID",
type: :integer,
width: "60px",
sortable: true,
visible: true,
pinned: :left
column :title,
label: "Post Title",
type: :string,
width: "350px",
sortable: true,
searchable: true,
filterable: true,
filter_type: :text,
visible: true,
formatter: ->(value, record) {
truncated = value.to_s.length > 50 ? "#{value[0..50]}..." : value
"<strong class='text-gray-900'>#{truncated}</strong>".html_safe
}
column :author,
label: "Author",
type: :string,
width: "180px",
sortable: true,
searchable: true,
filterable: true,
filter_type: :select,
filter_options: -> { Author.order(:name).pluck(:name, :id).map { |name, id| [ name, id ] } },
visible: true,
formatter: ->(value, record) {
return "-" unless record.author
author_name = record.author.name
if record.author.verified
"#{author_name} <span class='text-green-600'>✓</span>".html_safe
else
author_name
end
}
column :category,
label: "Category",
type: :badge,
sortable: true,
filterable: true,
filter_type: :select,
filter_options: -> { Post.distinct.pluck(:category).compact.sort },
visible: true,
formatter: {
type: :badge,
options: {
colors: {
"Technology" => "blue",
"Design" => "purple",
"Business" => "green",
"Lifestyle" => "yellow",
"Travel" => "indigo",
"Food" => "pink",
"Health" => "red",
"Education" => "cyan"
}
}
}
column :status,
label: "Status",
type: :badge,
width: "110px",
sortable: true,
filterable: true,
filter_type: :select,
filter_options: [ "published", "draft", "archived" ],
visible: true,
formatter: {
type: :badge,
options: {
colors: {
"published" => "green",
"draft" => "yellow",
"archived" => "gray"
}
}
}
column :featured,
label: "Featured",
type: :boolean,
width: "90px",
sortable: true,
filterable: true,
filter_type: :select,
filter_options: [ [ "Yes", true ], [ "No", false ] ],
visible: true,
formatter: ->(value, record) {
if value
'<span class="text-yellow-500 text-lg">⭐</span>'.html_safe
else
'<span class="text-gray-300">☆</span>'.html_safe
end
}
column :views_count,
label: "Views",
type: :integer,
width: "100px",
sortable: true,
visible: true,
formatter: ->(value, record) {
if value.nil?
"0"
elsif value >= 1000000
"#{(value / 1000000.0).round(1)}M"
elsif value >= 1000
"#{(value / 1000.0).round(1)}K"
else
value.to_s
end
}
column :likes_count,
label: "Likes",
type: :integer,
width: "80px",
sortable: true,
visible: true,
formatter: ->(value, record) {
count = value || 0
"❤️ #{count}".html_safe
}
column :comments_count,
label: "Comments",
type: :integer,
width: "100px",
sortable: true,
visible: true,
formatter: ->(value, record) {
count = record.comments.count
"💬 #{count}"
}
column :reading_time,
label: "Read Time",
type: :integer,
width: "100px",
sortable: true,
visible: true,
formatter: ->(value, record) {
value ? "#{value} min" : "-"
}
column :published_at,
label: "Published",
type: :datetime,
sortable: true,
filterable: true,
filter_type: :date,
visible: true,
formatter: ->(value, record) {
if value
time_ago = ((Time.now - value) / 86400).round
if time_ago == 0
"Today"
elsif time_ago == 1
"Yesterday"
elsif time_ago < 7
"#{time_ago}d ago"
elsif time_ago < 30
"#{(time_ago / 7).round}w ago"
else
value.strftime("%b %d, %Y")
end
else
"Not published"
end
}
column :created_at,
label: "Created",
type: :datetime,
sortable: true,
visible: false,
formatter: :date,
formatter_options: { format: :short }
column :actions,
label: "Actions",
type: :action,
width: "150px",
visible: true,
pinned: :right,
formatter: :action,
formatter_options: {
actions: [
{
label: "View",
url: ->(record) { "/posts/#{record.id}" },
color: "primary"
},
{
label: "Edit",
url: ->(record) { "/posts/#{record.id}/edit" }
},
{
label: "Delete",
url: ->(record) { "/posts/#{record.id}" },
method: :delete,
color: "danger",
confirm: "Are you sure?"
}
]
}
per_page 50
per_page_options [ 25, 50, 100, 200 ]
default_sort :published_at, :desc
infinite_scroll true
table_min_height "500px"
table_max_height "700px"
end