@stapel/listings-react 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/default/FeedGrid.d.ts +42 -0
  3. package/dist/default/FeedGrid.d.ts.map +1 -0
  4. package/dist/default/FeedGrid.js +23 -0
  5. package/dist/default/FeedGrid.js.map +1 -0
  6. package/dist/default/ListingCard.d.ts +31 -0
  7. package/dist/default/ListingCard.d.ts.map +1 -1
  8. package/dist/default/ListingCard.js +12 -4
  9. package/dist/default/ListingCard.js.map +1 -1
  10. package/dist/default/ListingFeedCard.d.ts +77 -0
  11. package/dist/default/ListingFeedCard.d.ts.map +1 -0
  12. package/dist/default/ListingFeedCard.js +75 -0
  13. package/dist/default/ListingFeedCard.js.map +1 -0
  14. package/dist/default/ListingSerpCard.d.ts +114 -0
  15. package/dist/default/ListingSerpCard.d.ts.map +1 -0
  16. package/dist/default/ListingSerpCard.js +72 -0
  17. package/dist/default/ListingSerpCard.js.map +1 -0
  18. package/dist/default/favorite.d.ts +49 -0
  19. package/dist/default/favorite.d.ts.map +1 -0
  20. package/dist/default/favorite.js +25 -0
  21. package/dist/default/favorite.js.map +1 -0
  22. package/dist/default/icons.d.ts +17 -3
  23. package/dist/default/icons.d.ts.map +1 -1
  24. package/dist/default/icons.js +13 -1
  25. package/dist/default/icons.js.map +1 -1
  26. package/dist/default/index.d.ts +17 -1
  27. package/dist/default/index.d.ts.map +1 -1
  28. package/dist/default/index.js +14 -1
  29. package/dist/default/index.js.map +1 -1
  30. package/dist/i18n/es.d.ts.map +1 -1
  31. package/dist/i18n/es.js +4 -0
  32. package/dist/i18n/es.js.map +1 -1
  33. package/dist/i18n/keys.d.ts +11 -0
  34. package/dist/i18n/keys.d.ts.map +1 -1
  35. package/dist/i18n/keys.js +15 -0
  36. package/dist/i18n/keys.js.map +1 -1
  37. package/dist/i18n/ru.d.ts.map +1 -1
  38. package/dist/i18n/ru.js +4 -0
  39. package/dist/i18n/ru.js.map +1 -1
  40. package/llms.txt +3 -1
  41. package/manifest.json +45 -1
  42. package/nav-manifest.json +1 -1
  43. package/package.json +7 -7
  44. package/src/analytics/generated/events.json +1 -1
  45. package/src/default/FeedGrid.tsx +70 -0
  46. package/src/default/ListingCard.tsx +21 -5
  47. package/src/default/ListingFeedCard.tsx +232 -0
  48. package/src/default/ListingSerpCard.tsx +374 -0
  49. package/src/default/favorite.tsx +91 -0
  50. package/src/default/icons.tsx +37 -3
  51. package/src/default/index.ts +24 -1
  52. package/src/i18n/es.ts +4 -0
  53. package/src/i18n/keys.ts +15 -0
  54. package/src/i18n/ru.ts +4 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @stapel/listings-react
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c887a5a: **The pair gets its two phone card surfaces: `<ListingSerpCard>` for a one-column result page and `<ListingFeedCard>` + `<FeedGrid>` for a home feed.** Both on `/default`. `<ListingCard>` is untouched — the desktop grid card stays exactly what it was.
8
+
9
+ **Three cards, not one with a `variant`.** They differ in READING ORDER and in what may live inside the card's anchor, not merely in size, and a mode switch would have produced one component nobody could photograph either arm of. What they DO share is shared for real: one `CardTarget` (the three-armed link/button/neither union, so the double-navigation defect has one place to come back in), one `<FavoriteHeart>` (one hook, one gate, one `aria-pressed`, one refusal), one `<ListingPhoto>`, one `<ListingPrice>`.
10
+
11
+ **`<ListingSerpCard>` — the price is first, and the photo strip is outside the anchor.** On a one-column result the photo is already full-bleed above the text, so the first LINE is where the eye lands, and on a classified that line is the price: large and bold, title at regular weight under it, the host's muted spec line under that, then the stored badge projection. The photos are a `<SkinCarousel>` (peek and dots when there is more than one, neither when there is not) and it sits as a SIBLING of the card's anchor, never inside it — a `<SkinCarousel>` is a scroll container with its own tab stop, and a horizontal swipe that ends inside an `<a>` is a swipe the browser may deliver as a click. Putting the strip in the anchor would mean every attempt to look at photo two navigated to the listing.
12
+
13
+ Two slots at the trailing edge and under the text: `actionsRail` (the container's call and chat controls, with the favourite heart added at its end) and `sellerSlot`. **The caveat `sellerSlot` ships with, stated rather than discovered:** `<RatingBadge>` FETCHES — one request per card, twenty requests for a decoration on a page of twenty results. A container drawing a list should render the seller's name plus a bare `<Rate>` from an aggregate the row already carries, and nothing at all when the row carries none. It is outside the anchor because a seller's name is usually a link, and a link inside a link is neither valid nor operable.
14
+
15
+ **`priceTrend` is a seam over data that does not exist yet, and says so.** `{ oldPrice, direction }` renders the ref's struck previous price and a NAMED arrow (`role="img"`, not `aria-hidden` — the arrow is the whole message). The search projection carries no price history, so nothing on a live deployment fills it today: **wave gap G-2**, and the follow-up is on the stapel-listings/stapel-search projection. It ships anyway because the alternative is re-laying-out the card the day the field lands; the demos show it against fixture data and label it as fixture data.
16
+
17
+ **`<ListingFeedCard>` — borderless, and the one surface where the heart floats.** No antd `Card`: the photo IS the card and the rhythm of the grid separates one from the next, because twenty bordered boxes on a 390px screen is twenty frames around the only thing anyone is looking at. Two lines of title then a clamp (as a real CSS rule — `-webkit-line-clamp` does not survive an inline style object, which is how a clamp goes missing unnoticed), bold price, muted place, and an optional `badgeOverlay` on the photo's leading corner.
18
+
19
+ `<ListingCard>` argues at length that the heart belongs in a row UNDER the card and not on the photograph, because a blocked favourite states its reason as text. That argument is right, and this card does not repeat its conclusion for a reason it writes down instead of hiding: a 2-column feed tile has no line to spare, and a full row of "Sign in to save this" under each of twenty tiles is not twenty pieces of help — it is the feed. The fleet already has the mechanism, and it is not "hide the reason": **a container should wrap `<FeedGrid>` in a `<PaneGate>`**, whose `GateReasonScope` pools identical reasons and renders each once with every control's `aria-describedby` still pointing at that single copy. Unscoped, the reason still renders, visibly, over the photo.
20
+
21
+ **`<FeedGrid>` is two columns and no masonry.** `repeat(columns, minmax(0, 1fr))` — not the results pane's `auto-fill, minmax(280px, 1fr)`, which collapses to ONE column on a 390px phone, exactly where the ref calls for two. No masonry polyfill and no CSS `columns`: every photo is drawn in the same 4:3 well so the tiles line up on their own, and multi-column would have walked a keyboard and a screen reader down the whole left column before the top of the right one. `columns` defaults to 2; a wider surface passes its own number, so desktop is not this wave's consumer and is not broken either.
22
+
23
+ The `@stapel/tokens-antd` peer floor rises to `>=0.9.0` — the release that introduces `SkinCarousel`.
24
+
25
+ New keys in en/ru/es: `listings.card.photos`, `listings.card.price_was`, `listings.card.price_dropped`, `listings.card.price_raised`.
26
+
3
27
  ## 0.9.1
4
28
 
5
29
  ### Patch Changes
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `<FeedGrid>` — the two-column wall a phone home feed is laid out on.
3
+ *
4
+ * ── Why this is not `<SearchResultsPane>`'s grid ──────────────────────────
5
+ *
6
+ * The results grid is `repeat(auto-fill, minmax(280px, 1fr))`: as many columns
7
+ * as FIT, each at least a readable card. That is right for a catalogue and
8
+ * wrong for a feed — 280px is wider than half of a 390px phone, so a feed laid
9
+ * out that way collapses to one column exactly where the ref calls for two.
10
+ * A feed's column count is a DESIGN decision ("two, side by side, small"), not
11
+ * a consequence of a minimum card width, and the two rules cannot be spelled
12
+ * with one declaration.
13
+ *
14
+ * ── No masonry ────────────────────────────────────────────────────────────
15
+ *
16
+ * The refs' feed looks staggered because the photos have different heights.
17
+ * They do not here: `<ListingFeedCard>` draws every photo in the same 4:3 well
18
+ * (`LISTING_PHOTO_ASPECT`), so the tiles line up in rows on their own and the
19
+ * grid stays a grid. Nothing in this package will pull in a masonry polyfill
20
+ * to reproduce a raggedness that is a property of unconstrained images —
21
+ * `columns: 2` (the CSS multi-column route) was the other candidate and was
22
+ * rejected because it breaks reading order: a screen reader and a keyboard
23
+ * would walk the whole left column before reaching the top of the right one.
24
+ *
25
+ * ── Desktop is not this wave's consumer, and is not broken either ─────────
26
+ *
27
+ * `columns` defaults to 2 because a phone feed is the surface this exists for.
28
+ * A wider surface passes its own number; the declaration is the same one, so
29
+ * there is no second layout to keep in step and no breakpoint in this file.
30
+ */
31
+ import type { CSSProperties, ReactElement, ReactNode } from "react";
32
+ /** The ref's phone feed: two tiles across. */
33
+ export declare const FEED_GRID_COLUMNS = 2;
34
+ export interface FeedGridProps {
35
+ /** How many tiles across. Default {@link FEED_GRID_COLUMNS}. */
36
+ readonly columns?: number;
37
+ /** The tiles — `<ListingFeedCard>`s, conventionally. */
38
+ readonly children: ReactNode;
39
+ readonly style?: CSSProperties;
40
+ }
41
+ export declare function FeedGrid(props: FeedGridProps): ReactElement;
42
+ //# sourceMappingURL=FeedGrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeedGrid.d.ts","sourceRoot":"","sources":["../../src/default/FeedGrid.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGpE,8CAA8C;AAC9C,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAyB3D"}
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { spacing } from "@stapel/tokens";
3
+ /** The ref's phone feed: two tiles across. */
4
+ export const FEED_GRID_COLUMNS = 2;
5
+ export function FeedGrid(props) {
6
+ const columns = props.columns ?? FEED_GRID_COLUMNS;
7
+ return (_jsx("div", { "data-testid": "listings-feed-grid", "data-columns": String(columns), style: {
8
+ display: "grid",
9
+ // `minmax(0, 1fr)` rather than `1fr`: a bare `1fr` is `minmax(auto,
10
+ // 1fr)`, and `auto` refuses to shrink below its content — one long
11
+ // unbroken word in a title then widens the whole column and pushes the
12
+ // grid past the screen.
13
+ gridTemplateColumns: `repeat(${String(columns)}, minmax(0, 1fr))`,
14
+ columnGap: spacing[3],
15
+ // Rows breathe more than columns do: the tiles carry no border, so the
16
+ // gap between two rows is the only thing saying where one card's
17
+ // location line ends and the next card's photo begins.
18
+ rowGap: spacing[5],
19
+ alignItems: "start",
20
+ ...props.style,
21
+ }, children: props.children }));
22
+ }
23
+ //# sourceMappingURL=FeedGrid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeedGrid.js","sourceRoot":"","sources":["../../src/default/FeedGrid.tsx"],"names":[],"mappings":";AA+BA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,8CAA8C;AAC9C,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAUnC,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,iBAAiB,CAAC;IACnD,OAAO,CACL,6BACc,oBAAoB,kBAClB,MAAM,CAAC,OAAO,CAAC,EAC7B,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,oEAAoE;YACpE,mEAAmE;YACnE,uEAAuE;YACvE,wBAAwB;YACxB,mBAAmB,EAAE,UAAU,MAAM,CAAC,OAAO,CAAC,mBAAmB;YACjE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;YACrB,uEAAuE;YACvE,iEAAiE;YACjE,uDAAuD;YACvD,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;YAClB,UAAU,EAAE,OAAO;YACnB,GAAG,KAAK,CAAC,KAAK;SACf,YAEA,KAAK,CAAC,QAAQ,GACX,CACP,CAAC;AACJ,CAAC"}
@@ -158,5 +158,36 @@ export interface ListingCardBaseProps extends ThemeModeProp, SignInCtaProp {
158
158
  readonly showFavorite?: boolean;
159
159
  }
160
160
  export type ListingCardProps = ListingCardBaseProps & ListingCardOpenProps;
161
+ /**
162
+ * The card's own body, wrapped in whatever makes it openable: an anchor, a
163
+ * button, or nothing at all.
164
+ *
165
+ * Exactly one of the three renders, so exactly one navigation happens per
166
+ * click — the branch below has no arm in which both a handler and an `href`
167
+ * reach the DOM.
168
+ *
169
+ * The BUTTON arm exists for a container that routes by callback. It wraps the
170
+ * same content in a `<button>` reset to look like nothing, rather than drawing
171
+ * a separate captioned control: a card that is a target on one deployment and
172
+ * a card-plus-a-button on another would be two different products.
173
+ *
174
+ * Exported for this pair's OTHER card surfaces (`<ListingSerpCard>`,
175
+ * `<ListingFeedCard>`) and for no one else — it is deliberately absent from
176
+ * `src/default/index.ts`. Three cards each re-deriving "which of the three
177
+ * arms is this" is three places for the double-navigation defect to come back;
178
+ * one function is one place. The card surfaces differ in what they PUT inside
179
+ * the target, never in how the target is made.
180
+ */
181
+ export declare function CardTarget(props: ListingCardOpenProps & {
182
+ readonly listingId: number;
183
+ readonly label: string;
184
+ /** The target's own test id. Each card surface names its own, so a screen
185
+ * holding two kinds of card does not hand a test two elements under one
186
+ * name. Default: the original card's. */
187
+ readonly testId?: string;
188
+ /** The body's test id on the arm where nothing opens. */
189
+ readonly bodyTestId?: string;
190
+ readonly children: ReactNode;
191
+ }): ReactElement;
161
192
  export declare function ListingCard(props: ListingCardProps): ReactElement;
162
193
  //# sourceMappingURL=ListingCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ListingCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIpE,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGjE,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAStE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IACE;iEAC6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;8CAE0C;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B,GACD;IACE;wEACoE;IACpE,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;CACpC,GACD;IACE;kBACc;IACd,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;CACpC,CAAC;AAEN;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvD,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,+BAA+B,CAAC;AAE9D,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,gCAAgC,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAwBD,MAAM,WAAW,oBAAqB,SAAQ,aAAa,EAAE,aAAa;IACxE,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,CAAC,EAAE,wBAAwB,CAAC;IAClD;oEACgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B;gFAC4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;AA+E3E,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,YAAY,CA6KjE"}
1
+ {"version":3,"file":"ListingCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIpE,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGjE,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAStE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IACE;iEAC6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;8CAE0C;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B,GACD;IACE;wEACoE;IACpE,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;CACpC,GACD;IACE;kBACc;IACd,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;CACpC,CAAC;AAEN;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvD,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,+BAA+B,CAAC;AAE9D,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,gCAAgC,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAwBD,MAAM,WAAW,oBAAqB,SAAQ,aAAa,EAAE,aAAa;IACxE,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,CAAC,EAAE,wBAAwB,CAAC;IAClD;oEACgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B;gFAC4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,oBAAoB,GAAG;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;6CAEyC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B,GACA,YAAY,CA2Dd;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,YAAY,CA6KjE"}
@@ -66,24 +66,32 @@ const BUTTON_TARGET_STYLE = {
66
66
  * same content in a `<button>` reset to look like nothing, rather than drawing
67
67
  * a separate captioned control: a card that is a target on one deployment and
68
68
  * a card-plus-a-button on another would be two different products.
69
+ *
70
+ * Exported for this pair's OTHER card surfaces (`<ListingSerpCard>`,
71
+ * `<ListingFeedCard>`) and for no one else — it is deliberately absent from
72
+ * `src/default/index.ts`. Three cards each re-deriving "which of the three
73
+ * arms is this" is three places for the double-navigation defect to come back;
74
+ * one function is one place. The card surfaces differ in what they PUT inside
75
+ * the target, never in how the target is made.
69
76
  */
70
- function CardTarget(props) {
77
+ export function CardTarget(props) {
71
78
  const { label, children } = props;
79
+ const testId = props.testId ?? "listings-card-open";
72
80
  if (props.href !== undefined) {
73
81
  const Link = props.linkComponent;
74
82
  // The host's component is rendered as it comes: this pair has no CSS and
75
83
  // no way to hand its own styling to a foreign element. A wrapper element
76
84
  // around a link would be a click target that is not the link.
77
- return Link !== undefined ? (_jsx(Link, { href: props.href, "aria-label": label, className: CARD_TARGET_CLASS, "data-testid": "listings-card-open", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: children })) : (_jsx("a", { href: props.href, "aria-label": label, className: CARD_TARGET_CLASS, style: TARGET_STYLE, "data-testid": "listings-card-open", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: children }));
85
+ return Link !== undefined ? (_jsx(Link, { href: props.href, "aria-label": label, className: CARD_TARGET_CLASS, "data-testid": testId, "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: children })) : (_jsx("a", { href: props.href, "aria-label": label, className: CARD_TARGET_CLASS, style: TARGET_STYLE, "data-testid": testId, "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: children }));
78
86
  }
79
87
  if (props.onOpen !== undefined) {
80
88
  const onOpen = props.onOpen;
81
- return (_jsx("button", { type: "button", "aria-label": label, className: CARD_TARGET_CLASS, style: BUTTON_TARGET_STYLE, "data-testid": "listings-card-open", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", onClick: () => {
89
+ return (_jsx("button", { type: "button", "aria-label": label, className: CARD_TARGET_CLASS, style: BUTTON_TARGET_STYLE, "data-testid": testId, "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", onClick: () => {
82
90
  onOpen(props.listingId);
83
91
  }, children: children }));
84
92
  }
85
93
  // No open control at all — a card inside a screen that IS the listing.
86
- return _jsx("div", { "data-testid": "listings-card-body", children: children });
94
+ return (_jsx("div", { "data-testid": props.bodyTestId ?? "listings-card-body", children: children }));
87
95
  }
88
96
  export function ListingCard(props) {
89
97
  const t = useT();
@@ -1 +1 @@
1
- {"version":3,"file":"ListingCard.js","sourceRoot":"","sources":["../../src/default/ListingCard.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ListingCard.js","sourceRoot":"","sources":["../../src/default/ListingCard.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * `<ListingFeedCard>` — the borderless card of a phone home feed (refs §1).
3
+ *
4
+ * ── What "borderless" is actually doing ───────────────────────────────────
5
+ *
6
+ * `<ListingCard>` and `<ListingSerpCard>` are antd `Card`s: a painted surface
7
+ * with a border, which is what makes one result read as one object next to
8
+ * another. A two-column feed is a different picture. Twenty bordered boxes on
9
+ * a 390px screen is twenty frames and forty vertical lines, and the photos —
10
+ * the only thing anyone is actually looking at — end up as small pictures
11
+ * inside chrome. So this card has no surface of its own: the photo is the
12
+ * card, the three lines under it sit on the page's own ground, and the RHYTHM
13
+ * of the grid is what separates one from the next. That is the ref, and it is
14
+ * also why this is a third component rather than a `bordered={false}` on the
15
+ * first: nothing else about the layout survives the change either.
16
+ *
17
+ * ── The heart is over the photo here, and only here ───────────────────────
18
+ *
19
+ * `<ListingCard>` argues at length that the heart belongs in a row UNDER the
20
+ * card rather than floating on the photograph, because a blocked favourite
21
+ * states its reason as text and there is nowhere to put a sentence on top of a
22
+ * picture. That argument is correct and this card does not repeat its
23
+ * conclusion, for a reason it states rather than hides: a 2-column feed tile
24
+ * has no line to spare. A full row of "Sign in to save this" under every one
25
+ * of twenty tiles is not twenty pieces of help — it is the feed.
26
+ *
27
+ * The fleet already has the mechanism for exactly this and it is not "hide the
28
+ * reason": `GateReasonScopeContext` / `<PaneGate>` pool identical reasons and
29
+ * render each ONCE for everything inside the scope, with every control's
30
+ * `aria-describedby` still pointing at that single copy. **A container drawing
31
+ * a feed should wrap `<FeedGrid>` in a `<PaneGate>`.** Unscoped, the reason
32
+ * still renders — over the photo, visible, never behind a hover — because a
33
+ * reason a person cannot read is the one outcome the doctrine forbids.
34
+ *
35
+ * ── Two lines of title, and then it stops ─────────────────────────────────
36
+ *
37
+ * A feed tile is roughly 170px wide. A three-line title pushes the price below
38
+ * the fold of the row and makes the grid ragged; a one-line ellipsis throws
39
+ * away the half of a listing's name that distinguishes it from the one beside
40
+ * it. Two lines, clamped, is the ref's answer and the only one that keeps the
41
+ * price on the same y as its neighbour's.
42
+ */
43
+ import type { ReactElement, ReactNode } from "react";
44
+ import type { ListingCard as ListingCardData } from "../api/types.js";
45
+ import type { ListingCardOpenProps } from "./ListingCard.js";
46
+ import type { ThemeModeProp } from "./types.js";
47
+ /** The class the clamped title carries, for {@link feedCardCss}. */
48
+ export declare const FEED_TITLE_CLASS = "stapel-listing-feed-title";
49
+ /** The `href` the hoisted feed stylesheet is deduplicated by. */
50
+ export declare const FEED_CARD_STYLE_HREF = "stapel-listings-feed-card";
51
+ /**
52
+ * The clamp, as a real CSS rule rather than an inline style.
53
+ *
54
+ * `-webkit-line-clamp` and `-webkit-box-orient` are the two declarations that
55
+ * do not survive the trip through an inline style object: React's serializer
56
+ * and every DOM implementation that is not a browser drop them silently, so a
57
+ * tile written that way clamps in Chrome, does not clamp in a test, and
58
+ * nothing anywhere says which. A hoisted sheet keeps one copy for the document
59
+ * and makes the rule something a test can read.
60
+ */
61
+ export declare function feedCardCss(): string;
62
+ export interface ListingFeedCardBaseProps extends ThemeModeProp {
63
+ readonly listing: ListingCardData;
64
+ /**
65
+ * Drawn over the photo's leading corner — "New", "In stock", a promotion
66
+ * marking. A SLOT rather than a string: what earns an overlay on a feed is a
67
+ * deployment's decision, and DSA Art. 26 marking belongs to whichever pair
68
+ * received the fact.
69
+ */
70
+ readonly badgeOverlay?: ReactNode;
71
+ /** Hide the favourite entirely — for a surface where it makes no sense.
72
+ * NOT a way to hide it from visitors. */
73
+ readonly showFavorite?: boolean;
74
+ }
75
+ export type ListingFeedCardProps = ListingFeedCardBaseProps & ListingCardOpenProps;
76
+ export declare function ListingFeedCard(props: ListingFeedCardProps): ReactElement;
77
+ //# sourceMappingURL=ListingFeedCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ListingFeedCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingFeedCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKpE,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAStE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD,oEAAoE;AACpE,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAE5D,iEAAiE;AACjE,eAAO,MAAM,oBAAoB,8BAA8B,CAAC;AAEhE;;;;;;;;;GASG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAKpC;AAuBD,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC7D,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC;IAClC;6CACyC;IACzC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,GAAG,oBAAoB,CAAC;AAEnF,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,CA+FzE"}
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Flex, Typography } from "antd";
3
+ import { SkinTheme } from "@stapel/tokens-antd/skin";
4
+ import { useT } from "@stapel/core";
5
+ import { radii, spacing } from "@stapel/tokens";
6
+ import { lifecycleCaption } from "../model/status.js";
7
+ import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
8
+ import { FavoriteHeart } from "./favorite.js";
9
+ import { CARD_TARGET_STYLE_HREF, CardTarget, cardTargetCss, } from "./ListingCard.js";
10
+ import { ListingPhoto } from "./ListingPhoto.js";
11
+ import { ListingPrice } from "./ListingPrice.js";
12
+ /** Lines of title a tile draws before it clips. See the file header. */
13
+ const TITLE_LINES = 2;
14
+ /** The class the clamped title carries, for {@link feedCardCss}. */
15
+ export const FEED_TITLE_CLASS = "stapel-listing-feed-title";
16
+ /** The `href` the hoisted feed stylesheet is deduplicated by. */
17
+ export const FEED_CARD_STYLE_HREF = "stapel-listings-feed-card";
18
+ /**
19
+ * The clamp, as a real CSS rule rather than an inline style.
20
+ *
21
+ * `-webkit-line-clamp` and `-webkit-box-orient` are the two declarations that
22
+ * do not survive the trip through an inline style object: React's serializer
23
+ * and every DOM implementation that is not a browser drop them silently, so a
24
+ * tile written that way clamps in Chrome, does not clamp in a test, and
25
+ * nothing anywhere says which. A hoisted sheet keeps one copy for the document
26
+ * and makes the rule something a test can read.
27
+ */
28
+ export function feedCardCss() {
29
+ return (`.${FEED_TITLE_CLASS}{display:-webkit-box;-webkit-box-orient:vertical;` +
30
+ `-webkit-line-clamp:${String(TITLE_LINES)};overflow:hidden}`);
31
+ }
32
+ /** The tile. `position: relative` is what the heart and the badge overlay are
33
+ * pinned to; `minWidth: 0` keeps a long word inside its grid track. */
34
+ const TILE = { position: "relative", minWidth: 0 };
35
+ /** Pinned to the photo's leading corner: the container's own marking. */
36
+ const BADGE = {
37
+ position: "absolute",
38
+ insetBlockStart: spacing[2],
39
+ insetInlineStart: spacing[2],
40
+ };
41
+ /** Pinned to the photo's trailing corner. `alignItems: flex-end` so the
42
+ * pooled-or-not reason, when there is one, stacks under the heart against the
43
+ * same edge rather than pushing it inwards. */
44
+ const HEART = {
45
+ position: "absolute",
46
+ insetBlockStart: spacing[2],
47
+ insetInlineEnd: spacing[2],
48
+ alignItems: "flex-end",
49
+ };
50
+ export function ListingFeedCard(props) {
51
+ const t = useT();
52
+ const { listing } = props;
53
+ const status = listing.status === undefined ? undefined : lifecycleCaption(listing.status);
54
+ const title = listing.title ?? "";
55
+ const targetLabel = title.length > 0 ? title : t(LISTINGS_I18N_KEYS.cardUntitled);
56
+ return (_jsxs(SkinTheme, { surface: "bare", ...(props.mode !== undefined ? { mode: props.mode } : {}), children: [_jsx("style", { href: CARD_TARGET_STYLE_HREF, precedence: "default", children: cardTargetCss() }), _jsx("style", { href: FEED_CARD_STYLE_HREF, precedence: "default", children: feedCardCss() }), _jsxs("div", { style: TILE, "data-testid": "listings-feed-card", "data-listing-id": listing.id, ...(status !== undefined
57
+ ? { "data-listing-status": status.status }
58
+ : {}), children: [_jsx(CardTarget, { ...openProps(props), listingId: listing.id, label: targetLabel, testId: "listings-feed-open", bodyTestId: "listings-feed-body", children: _jsxs(Flex, { vertical: true, gap: spacing[1], children: [_jsx(ListingPhoto, { imageRef: listing.images?.[0], alt: title.length > 0 ? title : String(listing.id), style: { borderRadius: radii.lg } }), _jsx(Typography.Text, { className: FEED_TITLE_CLASS, "data-testid": "listings-feed-title", children: title }), _jsx(Typography.Text, { strong: true, "data-testid": "listings-feed-price", children: _jsx(ListingPrice, { amount: listing.price, ...(listing.currency !== undefined
59
+ ? { currency: listing.currency }
60
+ : {}) }) }), listing.location_label !== undefined &&
61
+ listing.location_label.length > 0 ? (_jsx(Typography.Text, { type: "secondary", ellipsis: true, "data-testid": "listings-feed-location", children: listing.location_label })) : null] }) }), props.badgeOverlay !== undefined && (_jsx("div", { style: BADGE, "data-testid": "listings-feed-badge", children: props.badgeOverlay })), props.showFavorite === false ? null : (_jsx(FavoriteHeart, { listingId: listing.id, favorited: listing.is_favorited, testId: "listings-feed-favorite", style: HEART }))] })] }));
62
+ }
63
+ /** The three-armed open union, narrowed out of this card's own props — see
64
+ * `<ListingSerpCard>`'s copy for why it is picked rather than spread. */
65
+ function openProps(props) {
66
+ if (props.href !== undefined) {
67
+ return props.linkComponent !== undefined
68
+ ? { href: props.href, linkComponent: props.linkComponent }
69
+ : { href: props.href };
70
+ }
71
+ if (props.onOpen !== undefined)
72
+ return { onOpen: props.onOpen };
73
+ return {};
74
+ }
75
+ //# sourceMappingURL=ListingFeedCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ListingFeedCard.js","sourceRoot":"","sources":["../../src/default/ListingFeedCard.tsx"],"names":[],"mappings":";AA2CA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EACL,sBAAsB,EACtB,UAAU,EACV,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,wEAAwE;AACxE,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB,oEAAoE;AACpE,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAE5D,iEAAiE;AACjE,MAAM,CAAC,MAAM,oBAAoB,GAAG,2BAA2B,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,CACL,IAAI,gBAAgB,mDAAmD;QACvE,sBAAsB,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAC7D,CAAC;AACJ,CAAC;AAED;uEACuE;AACvE,MAAM,IAAI,GAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAElE,yEAAyE;AACzE,MAAM,KAAK,GAAkB;IAC3B,QAAQ,EAAE,UAAU;IACpB,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC;CAC7B,CAAC;AAEF;;+CAE+C;AAC/C,MAAM,KAAK,GAAkB;IAC3B,QAAQ,EAAE,UAAU;IACpB,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3B,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1B,UAAU,EAAE,UAAU;CACvB,CAAC;AAkBF,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1B,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IAClC,MAAM,WAAW,GACf,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAEhE,OAAO,CACL,MAAC,SAAS,IACR,OAAO,EAAC,MAAM,KACV,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAE1D,gBAAO,IAAI,EAAE,sBAAsB,EAAE,UAAU,EAAC,SAAS,YACtD,aAAa,EAAE,GACV,EACR,gBAAO,IAAI,EAAE,oBAAoB,EAAE,UAAU,EAAC,SAAS,YACpD,WAAW,EAAE,GACR,EACR,eACE,KAAK,EAAE,IAAI,iBACC,oBAAoB,qBACf,OAAO,CAAC,EAAE,KACvB,CAAC,MAAM,KAAK,SAAS;oBACvB,CAAC,CAAC,EAAE,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE;oBAC1C,CAAC,CAAC,EAAE,CAAC,aAKP,KAAC,UAAU,OACL,SAAS,CAAC,KAAK,CAAC,EACpB,SAAS,EAAE,OAAO,CAAC,EAAE,EACrB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAC,oBAAoB,EAC3B,UAAU,EAAC,oBAAoB,YAE/B,MAAC,IAAI,IAAC,QAAQ,QAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,aAC5B,KAAC,YAAY,IACX,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAC7B,GAAG,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAClD,KAAK,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,EAAE,GACjC,EAIF,KAAC,UAAU,CAAC,IAAI,IACd,SAAS,EAAE,gBAAgB,iBACf,qBAAqB,YAEhC,KAAK,GACU,EAElB,KAAC,UAAU,CAAC,IAAI,IAAC,MAAM,uBAAa,qBAAqB,YACvD,KAAC,YAAY,IACX,MAAM,EAAE,OAAO,CAAC,KAAK,KACjB,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS;4CACjC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE;4CAChC,CAAC,CAAC,EAAE,CAAC,GACP,GACc,EAEjB,OAAO,CAAC,cAAc,KAAK,SAAS;oCACrC,OAAO,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAClC,KAAC,UAAU,CAAC,IAAI,IACd,IAAI,EAAC,WAAW,EAChB,QAAQ,uBACI,wBAAwB,YAEnC,OAAO,CAAC,cAAc,GACP,CACnB,CAAC,CAAC,CAAC,IAAI,IACH,GACI,EAEZ,KAAK,CAAC,YAAY,KAAK,SAAS,IAAI,CACnC,cAAK,KAAK,EAAE,KAAK,iBAAc,qBAAqB,YACjD,KAAK,CAAC,YAAY,GACf,CACP,EAIA,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACrC,KAAC,aAAa,IACZ,SAAS,EAAE,OAAO,CAAC,EAAE,EACrB,SAAS,EAAE,OAAO,CAAC,YAAY,EAC/B,MAAM,EAAC,wBAAwB,EAC/B,KAAK,EAAE,KAAK,GACZ,CACH,IACG,IACI,CACb,CAAC;AACJ,CAAC;AAED;yEACyE;AACzE,SAAS,SAAS,CAAC,KAA2B;IAC5C,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC,aAAa,KAAK,SAAS;YACtC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE;YAC1D,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;IAChE,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * `<ListingSerpCard>` — the one-column result card of a phone SERP.
3
+ *
4
+ * `<ListingCard>` is the GRID card: a photo, a price, a title and a place,
5
+ * sized so twenty-four of them tile a desktop catalogue. This is the other
6
+ * shape a classified needs and the one the mobile refs are drawn to (refs §3):
7
+ * one card per row, the full width of the screen, a swipeable photo strip, and
8
+ * a column of actions down the right-hand side. They are two components rather
9
+ * than a `variant` prop for the reason the fleet keeps writing down — a mode
10
+ * switch produces one component nobody can photograph either arm of, and here
11
+ * the two arms differ in reading ORDER, not only in size.
12
+ *
13
+ * ── The price is first, and it is the biggest thing on the card ────────────
14
+ *
15
+ * On a grid card the photo carries the layout. On a one-column result the
16
+ * photo is already full-bleed above the text, so the first LINE is where the
17
+ * eye lands — and on a classified that line is the price. Title at regular
18
+ * weight underneath it, the seller's own spec line muted under that. This is
19
+ * the ref's order and it is also the order the fleet's own storefront review
20
+ * asked for: a person scanning a SERP is comparing prices, not reading names.
21
+ *
22
+ * ── Why the photo strip is OUTSIDE the anchor ─────────────────────────────
23
+ *
24
+ * `<ListingCard>` puts its photo inside the card's anchor, because a still
25
+ * `<img>` inside a link is just a bigger link. A `<SkinCarousel>` is not: it
26
+ * is a scroll container with its own tab stop, and a horizontal swipe that
27
+ * ends inside an `<a>` is a swipe the browser may deliver as a click. Putting
28
+ * the strip in the anchor would mean every attempt to look at photo two
29
+ * navigated to the listing — the defect that makes phone galleries unusable.
30
+ *
31
+ * So the strip sits above the anchor as a sibling, exactly as the heart sits
32
+ * below it as a sibling, and for the same class of reason: a link may not
33
+ * contain a control, and a swipeable strip is a control. The anchor still
34
+ * covers the four things that identify the listing — price, title, specs,
35
+ * badges — so the card opens from everything a person reads.
36
+ *
37
+ * ── The two slots, and what they are honestly for ─────────────────────────
38
+ *
39
+ * `sellerSlot` is the seller's name and rating. It is a SLOT because a rating
40
+ * aggregate belongs to `@stapel/reviews-react` and this pair does not import
41
+ * another L2 pair; the container is the seam. **The caveat a host must know:**
42
+ * `<RatingBadge>` FETCHES — one request per card. On a page of twenty results
43
+ * that is twenty requests for a decoration. A container drawing this in a list
44
+ * should render the name plus a bare `<Rate>` from an aggregate the row
45
+ * already carries, and render nothing at all when the row carries none. It
46
+ * lives outside the anchor because a seller's name is usually a link to the
47
+ * seller, and a link inside a link is neither valid nor operable.
48
+ *
49
+ * `actionsRail` is the vertical column at the trailing edge — "call", "write"
50
+ * — filled by the container from `@stapel/chat-react` and from whatever the
51
+ * deployment uses for a phone number. The pair supplies the COLUMN and the
52
+ * favourite heart at the end of it, and nothing else: this package has no
53
+ * conversation and no telephone number, and the search projection carries no
54
+ * phone (gap G-2's neighbour, recorded in the wave spec §4).
55
+ *
56
+ * ── The price trend is a seam over data that does not exist yet ───────────
57
+ *
58
+ * `priceTrend` renders the ref's "was 1 800 000 ₽ ↓". The search projection
59
+ * carries no price history — no `price_was`, no direction — so nothing on a
60
+ * live SERP fills this today (wave gap **G-2**). It ships anyway, and the
61
+ * demos show it against fixture data, because the alternative is a card that
62
+ * has to be re-laid-out the day the projection grows the field. The seam is
63
+ * the honest half; the missing data is written down rather than faked.
64
+ */
65
+ import type { ReactElement, ReactNode } from "react";
66
+ import type { ListingCard as ListingCardData } from "../api/types.js";
67
+ import type { ListingCardOpenProps } from "./ListingCard.js";
68
+ import type { ThemeModeProp } from "./types.js";
69
+ /**
70
+ * A price that moved, as the ref draws it: the old figure struck through and
71
+ * an arrow saying which way.
72
+ *
73
+ * `oldPrice` is a decimal STRING in the listing's own currency — the same
74
+ * dialect `Listing.price` speaks — so it is formatted by the same
75
+ * `<ListingPrice>` and never by a template literal.
76
+ */
77
+ export interface ListingPriceTrend {
78
+ /** The previous asking price, as the wire spells money: a decimal string. */
79
+ readonly oldPrice: string;
80
+ /** Which way it moved. `"down"` is the one a classified shouts about. */
81
+ readonly direction: "down" | "up";
82
+ }
83
+ export interface ListingSerpCardBaseProps extends ThemeModeProp {
84
+ readonly listing: ListingCardData;
85
+ /**
86
+ * The seller's own one-line summary — "Petrol 1.5 (147 hp), robot, front".
87
+ *
88
+ * A STRING the host derived, not a projection this card reads: the ref's
89
+ * spec line is a deployment's editorial choice about which features belong
90
+ * on a result and in what order, and that is a decision no library can take
91
+ * for a category it has never seen. When the host has no opinion the card
92
+ * falls back to the row's `features_title` — the projection's own summary is
93
+ * a better default line than an empty one (§83: the default skin does the
94
+ * right thing before the host wires anything).
95
+ */
96
+ readonly specsLine?: string;
97
+ /** See the file header — a seam over data the projection does not carry. */
98
+ readonly priceTrend?: ListingPriceTrend;
99
+ /** The seller's name and rating. See the file header for the per-card fetch
100
+ * caveat a container has to avoid. */
101
+ readonly sellerSlot?: ReactNode;
102
+ /** The vertical action column at the trailing edge — call, write. The
103
+ * favourite heart is added at its end by this component. */
104
+ readonly actionsRail?: ReactNode;
105
+ /** Extra chrome above the price (a `promoted` tag from search — DSA Art. 26
106
+ * marking belongs to the pair that receives it). */
107
+ readonly badge?: ReactNode;
108
+ /** Hide the favourite entirely — for a surface where it makes no sense (the
109
+ * owner's own listings). NOT a way to hide it from visitors. */
110
+ readonly showFavorite?: boolean;
111
+ }
112
+ export type ListingSerpCardProps = ListingSerpCardBaseProps & ListingCardOpenProps;
113
+ export declare function ListingSerpCard(props: ListingSerpCardProps): ReactElement;
114
+ //# sourceMappingURL=ListingSerpCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ListingSerpCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingSerpCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMpE,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAetE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC7D,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IACxC;0CACsC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC;IAChC;gEAC4D;IAC5D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC;IACjC;wDACoD;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B;oEACgE;IAChE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,GAAG,oBAAoB,CAAC;AAUnF,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,CAkNzE"}
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Card, Flex, Typography, theme as antdTheme } from "antd";
3
+ import { SkinCarousel, SkinTheme } from "@stapel/tokens-antd/skin";
4
+ import { useT } from "@stapel/core";
5
+ import { fontSize, spacing } from "@stapel/tokens";
6
+ import { FeatureBadges } from "@stapel/attributes-react/default";
7
+ import { asFeatureDaoList, featuresDtoFromDaoList, featuresFromDaoList, } from "../model/features.js";
8
+ import { lifecycleCaption } from "../model/status.js";
9
+ import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
10
+ import { PriceTrendIcon } from "./icons.js";
11
+ import { FavoriteHeart } from "./favorite.js";
12
+ import { CARD_TARGET_STYLE_HREF, CardTarget, cardTargetCss, } from "./ListingCard.js";
13
+ import { LISTING_PHOTO_ASPECT, ListingPhoto } from "./ListingPhoto.js";
14
+ import { ListingPrice } from "./ListingPrice.js";
15
+ /** The trailing column. Fixed width contribution: it must not take room from
16
+ * the text as the actions grow. */
17
+ const RAIL = { flex: "0 0 auto" };
18
+ /** The reading column. `minWidth: 0` so a long unbroken word cannot push the
19
+ * rail off the card. */
20
+ const BODY = { flex: "1 1 auto", minWidth: 0 };
21
+ export function ListingSerpCard(props) {
22
+ const t = useT();
23
+ const { listing, priceTrend } = props;
24
+ const { token } = antdTheme.useToken();
25
+ const badgeDaos = asFeatureDaoList(listing.features_badges);
26
+ const badgeFeatures = featuresFromDaoList(badgeDaos);
27
+ const badgeValues = featuresDtoFromDaoList(badgeDaos);
28
+ // The fallback spec line when the host derived none: the row's own
29
+ // `features_title` projection, exactly the line `ListingCard` draws.
30
+ const titleDaos = props.specsLine !== undefined && props.specsLine.length > 0
31
+ ? []
32
+ : asFeatureDaoList(listing.features_title);
33
+ const status = listing.status === undefined ? undefined : lifecycleCaption(listing.status);
34
+ const title = listing.title ?? "";
35
+ const targetLabel = title.length > 0 ? title : t(LISTINGS_I18N_KEYS.cardUntitled);
36
+ const photos = listing.images ?? [];
37
+ const currency = listing.currency !== undefined ? { currency: listing.currency } : {};
38
+ // A one-photo strip gets neither a peek nor dots: the sliver of a next slide
39
+ // is an affordance for something that is there, and on a single photo it is
40
+ // just a strip of dead space at the trailing edge (`SkinCarousel` says the
41
+ // same thing from its own side).
42
+ const many = photos.length > 1;
43
+ const rail = props.actionsRail !== undefined || props.showFavorite !== false ? (_jsxs(Flex, { vertical: true, align: "flex-end", gap: spacing[2], style: RAIL, "data-testid": "listings-serp-actions", children: [props.actionsRail, props.showFavorite === false ? null : (_jsx(FavoriteHeart, { listingId: listing.id, favorited: listing.is_favorited, testId: "listings-serp-favorite" }))] })) : null;
44
+ return (_jsxs(SkinTheme, { surface: "bare", ...(props.mode !== undefined ? { mode: props.mode } : {}), children: [_jsx("style", { href: CARD_TARGET_STYLE_HREF, precedence: "default", children: cardTargetCss() }), _jsx(Card, { size: "small", "data-testid": "listings-serp-card", "data-listing-id": listing.id, ...(status !== undefined
45
+ ? { "data-listing-status": status.status }
46
+ : {}), styles: { body: { minWidth: 0, padding: token.paddingSM } }, style: { ["--listing-card-focus"]: token.colorPrimary }, children: _jsxs(Flex, { vertical: true, gap: spacing[3], children: [_jsx(SkinCarousel, { label: t(LISTINGS_I18N_KEYS.cardPhotos), aspectRatio: LISTING_PHOTO_ASPECT, peek: many, dots: many, "data-testid": "listings-serp-photos", children: photos.length === 0 ? (_jsx(ListingPhoto, { imageRef: undefined, alt: title.length > 0 ? title : String(listing.id) })) : (photos.map((reference, index) => (_jsx(ListingPhoto, { imageRef: reference, alt: t(LISTINGS_I18N_KEYS.detailPhotoAlt, {
47
+ index: index + 1,
48
+ total: photos.length,
49
+ }) }, reference)))) }), _jsxs(Flex, { gap: spacing[3], align: "flex-start", children: [_jsxs(Flex, { vertical: true, gap: spacing[1], style: BODY, children: [_jsxs(CardTarget, { ...openProps(props), listingId: listing.id, label: targetLabel, testId: "listings-serp-open", bodyTestId: "listings-serp-body", children: [props.badge, _jsxs(Flex, { align: "center", gap: spacing[2], wrap: true, children: [_jsx(Typography.Text, { strong: true, style: { fontSize: fontSize.xl.fontSize }, "data-testid": "listings-serp-price", children: _jsx(ListingPrice, { amount: listing.price, ...currency }) }), priceTrend !== undefined && (_jsx(PriceTrendIcon, { direction: priceTrend.direction, label: t(priceTrend.direction === "down"
50
+ ? LISTINGS_I18N_KEYS.cardPriceDropped
51
+ : LISTINGS_I18N_KEYS.cardPriceRaised) }))] }), priceTrend !== undefined && (_jsxs(Flex, { align: "baseline", gap: spacing[1], "data-testid": "listings-serp-old-price", children: [_jsx(Typography.Text, { type: "secondary", children: t(LISTINGS_I18N_KEYS.cardPriceWas) }), _jsx(Typography.Text, { type: "secondary", delete: true, children: _jsx(ListingPrice, { amount: priceTrend.oldPrice, ...currency }) })] })), _jsx(Typography.Text, { "data-testid": "listings-serp-title", children: title }), props.specsLine !== undefined && props.specsLine.length > 0 ? (_jsx(Typography.Text, { type: "secondary", ellipsis: true, "data-testid": "listings-serp-specs", children: props.specsLine })) : titleDaos.length > 0 ? (_jsx(Typography.Text, { type: "secondary", ellipsis: true, "data-testid": "listings-serp-specs", children: _jsx(FeatureBadges, { features: featuresFromDaoList(titleDaos).map((view) => view.feature), values: featuresDtoFromDaoList(titleDaos) }) })) : null, badgeFeatures.length > 0 ? (_jsx(FeatureBadges, { features: badgeFeatures.map((view) => view.feature), values: badgeValues })) : null] }), props.sellerSlot ?? null, listing.location_label !== undefined &&
52
+ listing.location_label.length > 0 ? (_jsx(Typography.Text, { type: "secondary", "data-testid": "listings-serp-location", children: listing.location_label })) : null] }), rail] })] }) })] }));
53
+ }
54
+ /**
55
+ * The three-armed open union, narrowed back out of this card's own props.
56
+ *
57
+ * Spreading `props` straight into `<CardTarget>` would hand it `listing`,
58
+ * `sellerSlot` and the rest as DOM attributes on the arms that render a plain
59
+ * `<a>`; picking the union's members explicitly keeps the anchor clean and
60
+ * keeps exactly one of the three arms reachable.
61
+ */
62
+ function openProps(props) {
63
+ if (props.href !== undefined) {
64
+ return props.linkComponent !== undefined
65
+ ? { href: props.href, linkComponent: props.linkComponent }
66
+ : { href: props.href };
67
+ }
68
+ if (props.onOpen !== undefined)
69
+ return { onOpen: props.onOpen };
70
+ return {};
71
+ }
72
+ //# sourceMappingURL=ListingSerpCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ListingSerpCard.js","sourceRoot":"","sources":["../../src/default/ListingSerpCard.tsx"],"names":[],"mappings":"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"}