Extract the card-list row into ListItem
Assisted-by: GLM-5.3 via pi
diff --git a/Steepfile b/Steepfile
index 6be4ae5..e6911e1 100644
--- a/Steepfile
+++ b/Steepfile
@@ -22,7 +22,7 @@ target :lib do
# the files stay checked.
ignore(*%w[
avatar contact_dialog contacts_edit contacts_index contacts_show dashboard device_setup format
- group_dialog group_label groups_edit groups_index groups_show icons layout phlex upcoming_birthdays
+ group_dialog group_label groups_edit groups_index groups_show icons layout list_item phlex upcoming_birthdays
].map { "lib/pro_tacts/admin/#{it}.rb" }, inline: true)
signature "sig"
diff --git a/lib/pro_tacts/admin/contacts_index.rb b/lib/pro_tacts/admin/contacts_index.rb
index 58b29da..3ec5f00 100644
--- a/lib/pro_tacts/admin/contacts_index.rb
+++ b/lib/pro_tacts/admin/contacts_index.rb
@@ -3,6 +3,7 @@ require "pro_tacts/admin/phlex"
require "pro_tacts/admin/avatar"
require "pro_tacts/admin/format"
require "pro_tacts/admin/layout"
+require "pro_tacts/admin/list_item"
module ProTacts
module Admin
@@ -51,12 +52,10 @@ module ProTacts
#: (Contact contact) -> void
def render_row(contact)
- li do
- a(href: "/contacts/#{contact.id}") do
- render Avatar.new(contact:, size: "lg")
- div(style: "flex: 1; min-width: 0; font-weight: 550;") { Format.name_label(contact) }
- end
- end
+ render ListItem.new(
+ href: "/contacts/#{contact.id}",
+ avatar: Avatar.new(contact:, size: "lg"),
+ ) { Format.name_label(contact) }
end
end
end
diff --git a/lib/pro_tacts/admin/dashboard.rb b/lib/pro_tacts/admin/dashboard.rb
index 8d0701f..048c18b 100644
--- a/lib/pro_tacts/admin/dashboard.rb
+++ b/lib/pro_tacts/admin/dashboard.rb
@@ -4,6 +4,7 @@ require "pro_tacts/admin/avatar"
require "pro_tacts/admin/contact_dialog"
require "pro_tacts/admin/format"
require "pro_tacts/admin/group_label"
+require "pro_tacts/admin/list_item"
require "pro_tacts/admin/layout"
require "pro_tacts/admin/upcoming_birthdays"
@@ -101,24 +102,18 @@ module ProTacts
end
ul(class: "card") do
@groups.each do |group|
- li do
- a(href: "/groups/#{group.id}") do
- div(style: "flex: 1; min-width: 0; font-weight: 550;") { render GroupLabel.new(group:) }
- end
- end
+ render ListItem.new(href: "/groups/#{group.id}") { render GroupLabel.new(group:) }
end
end
end
#: (Store::RecentContact row) -> void
def render_row(row)
- li do
- a(href: "/contacts/#{row.contact.id}") do
- render Avatar.new(contact: row.contact, size: "lg")
- div(style: "flex: 1; min-width: 0; font-weight: 550;") { Format.name_label(row.contact) }
- span(class: "type-label") { Format.time_ago(row.updated_at) }
- end
- end
+ render ListItem.new(
+ href: "/contacts/#{row.contact.id}",
+ avatar: Avatar.new(contact: row.contact, size: "lg"),
+ trailing: Format.time_ago(row.updated_at),
+ ) { Format.name_label(row.contact) }
end
end
end
diff --git a/lib/pro_tacts/admin/groups_index.rb b/lib/pro_tacts/admin/groups_index.rb
index 7039b2a..fe080d6 100644
--- a/lib/pro_tacts/admin/groups_index.rb
+++ b/lib/pro_tacts/admin/groups_index.rb
@@ -2,6 +2,7 @@ require "pro_tacts/admin/phlex"
require "pro_tacts/admin/group_label"
require "pro_tacts/admin/layout"
+require "pro_tacts/admin/list_item"
module ProTacts
module Admin
@@ -34,12 +35,10 @@ module ProTacts
else
ul(class: "card") do
@groups.each do |group|
- li do
- a(href: "/groups/#{group.id}") do
- div(style: "flex: 1; min-width: 0; font-weight: 550;") { render GroupLabel.new(group:) }
- span(class: "type-label") { members_label(group) }
- end
- end
+ render ListItem.new(
+ href: "/groups/#{group.id}",
+ trailing: members_label(group),
+ ) { render GroupLabel.new(group:) }
end
end
end
diff --git a/lib/pro_tacts/admin/list_item.rb b/lib/pro_tacts/admin/list_item.rb
new file mode 100644
index 0000000..7bacce2
--- /dev/null
+++ b/lib/pro_tacts/admin/list_item.rb
@@ -0,0 +1,35 @@
+require "pro_tacts/admin/phlex"
+
+module ProTacts
+ module Admin
+ # One row of a card list: an li holding a link, an optional avatar
+ # in front, the block's label in a bold column that flexes, and an
+ # optional muted label behind it — the row shape every browse
+ # screen shares (the dashboard's contacts and group results,
+ # /contacts, /groups). The middle column is the one piece of
+ # layout this owns: min-width 0 lets a long name shrink instead
+ # of pushing what trails it off the card.
+ class ListItem < Phlex::HTML
+ # @rbs @href: String
+ # @rbs @avatar: Phlex::HTML?
+ # @rbs @trailing: String?
+
+ #: (href: String, ?avatar: Phlex::HTML?, ?trailing: String?) -> void
+ def initialize(href:, avatar: nil, trailing: nil)
+ @href = href
+ @avatar = avatar
+ @trailing = trailing
+ end
+
+ def view_template
+ li do
+ a(href: @href) do
+ render @avatar if @avatar
+ div(style: "flex: 1; min-width: 0; font-weight: 550;") { yield }
+ span(class: "type-label") { @trailing } if @trailing
+ end
+ end
+ end
+ end
+ end
+end