Implemented phase 1 for the POC prototype in the Website service.
This commit is contained in:
1 parent
6d7ab4bcba
commit
ea89ea892a
37 files changed
+4173
-43
No files matched your search
@@ -0,0 +1,108 @@
|
||||
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"
|
||||
)
|
||||
) {
|
||||
Cover(listing.cover)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user