Files
ccn/Services/Website/Sources/Library/Internal/Components/ListingCard.swift
T

109 lines
3.1 KiB
Swift

import Elementary
import Persistence
/// A listing in a card grid: its cover, title, price, grade chip, and — depending on the ``Context`` — the shop selling it.
struct ListingCard: HTML {
// MARK: Properties
/// Where the card is shown.
let context: Context
/// The listing the card shows.
let listing: Listing
// MARK: Initializers
/// Creates a listing card.
/// - Parameters:
/// - listing: the listing the card shows.
/// - context: where the card is shown; a browsing row by default.
init(
_ listing: Listing,
context: Context = .browse
) {
self.context = context
self.listing = listing
}
// MARK: Computed
var body: some HTML {
article(.class("listing-card")) {
// Saving is a gated action: signed out, it leads to the sign-in screen.
a(
.class("listing-card__save"),
.href(.Route.signIn(returningTo: listing.path)),
.custom(
name: "aria-label",
value: "Save \(listing.title) to watchlist"
)
) {
Icon(.bookmark, size: 18)
}
a(
.class("listing-card__cover"),
.href(listing.path),
.custom(
name: "tabindex",
value: "-1"
),
.custom(
name: "aria-hidden",
value: "true"
)
) {
ListingImage(listing)
}
div {
h3(.class("type-card-title")) {
a(
.class("listing-card__title"),
.href(listing.path)
) {
listing.title
}
}
p(.class("listing-card__meta")) {
listing.summary
}
}
div(.class("listing-card__row")) {
span(.class("type-price-card")) {
listing.priceCents.euros
}
Chip(
listing.chip.label,
tone: listing.chip.tone,
isCertified: listing.chip.isCertified
)
}
if context != .shop {
a(
.class("listing-card__shop"),
.href(listing.shop.path)
) {
context == .search
? "\(listing.shop.name) · \(listing.shop.country) · ship \(listing.shippingCents.euros)"
: listing.shop.name
}
}
}
}
}
// MARK: - Enumerations
extension ListingCard {
/// Where a listing card is shown.
enum Context: Sendable {
/// A browsing row: the shop is named.
case browse
/// Search results: the shop is named with its country and shipping cost.
case search
/// The shop's own screen: the shop goes unnamed.
case shop
}
}