@stapel/listings-react 0.18.1 → 0.18.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @stapel/listings-react
2
2
 
3
+ ## 0.18.2
4
+
5
+ ### Patch Changes
6
+
7
+ - The phone SERP card can state a blocked heart's refusal as a gesture (D45).
8
+
9
+ `<ListingSerpCard blockedReason="popover">` — the choice the grid card got in
10
+ the desktop pass, now on the one-column card a phone actually uses. A
11
+ signed-out walker measured "sign in to do this" printed fourteen times down
12
+ one search page, once under every card, in the line where a price or a place
13
+ belongs; fourteen copies of one sentence is not fourteen pieces of help. The
14
+ mechanism is this package's existing `GateReasonPopover`: it opens on tap as
15
+ readily as on hover, its anchor is `aria-disabled` rather than html-disabled
16
+ (so the tap is not swallowed), and the sentence stays in the accessibility
17
+ tree wired to the heart. `signIn` puts the door inside the disclosure.
18
+
19
+ Default unchanged — a host that says nothing still gets the standing line.
20
+ `<FavoriteHeart>` carries both arms now, so any surface mounting it inherits
21
+ the choice.
22
+
3
23
  ## 0.18.1
4
24
 
5
25
  ### Patch Changes
@@ -74,6 +74,7 @@
74
74
  * the honest half; the missing data is written down rather than faked.
75
75
  */
76
76
  import type { ReactElement, ReactNode } from "react";
77
+ import type { SignInCta } from "@stapel/core";
77
78
  import type { ListingCard as ListingCardData } from "../api/types.js";
78
79
  import type { ListingCardOpenProps } from "./ListingCard.js";
79
80
  import type { CategoryFeaturesProp, ThemeModeProp } from "./types.js";
@@ -119,6 +120,14 @@ export interface ListingSerpCardBaseProps extends ThemeModeProp, CategoryFeature
119
120
  /** Hide the favourite entirely — for a surface where it makes no sense (the
120
121
  * owner's own listings). NOT a way to hide it from visitors. */
121
122
  readonly showFavorite?: boolean;
123
+ /** How loudly the blocked heart states its refusal (D45) — `"text"`
124
+ * (default) keeps the standing sentence, `"popover"` moves it onto the
125
+ * gesture. A phone SERP is the surface that needed the choice: fourteen
126
+ * cards printed fourteen copies of one sentence. */
127
+ readonly blockedReason?: "text" | "popover";
128
+ /** The container's sign-in door, shown inside the disclosure when the
129
+ * refusal is a `"popover"`. */
130
+ readonly signIn?: SignInCta;
122
131
  }
123
132
  export type ListingSerpCardProps = ListingSerpCardBaseProps & ListingCardOpenProps;
124
133
  export declare function ListingSerpCard(props: ListingSerpCardProps): ReactElement;
@@ -1 +1 @@
1
- {"version":3,"file":"ListingSerpCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingSerpCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMpE,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAoBtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtE;;;;;;;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,wBACf,SAAQ,aAAa,EACnB,oBAAoB;IACtB,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,CAoMzE"}
1
+ {"version":3,"file":"ListingSerpCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingSerpCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAoBtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtE;;;;;;;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,wBACf,SAAQ,aAAa,EACnB,oBAAoB;IACtB,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;IAChC;;;wDAGoD;IACpD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;mCAC+B;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,GAAG,oBAAoB,CAAC;AAUnF,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,CAwMzE"}
@@ -40,7 +40,9 @@ export function ListingSerpCard(props) {
40
40
  const targetLabel = title.length > 0 ? title : t(LISTINGS_I18N_KEYS.cardUntitled);
41
41
  const photos = listing.images ?? [];
42
42
  const currency = listing.currency !== undefined ? { currency: listing.currency } : {};
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;
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", ...(props.blockedReason !== undefined
44
+ ? { blockedReason: props.blockedReason }
45
+ : {}), ...(props.signIn !== undefined ? { signIn: props.signIn } : {}) }))] })) : null;
44
46
  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
47
  ? { "data-listing-status": status.status }
46
48
  : {}), styles: { body: { minWidth: 0, padding: token.paddingSM } }, style: { ["--listing-card-focus"]: token.colorPrimary }, children: _jsx("div", { className: CARD_QUERY_CLASS, children: _jsxs("div", { className: CARD_FRAME_CLASS, style: { gap: spacing[3] }, children: [_jsx("div", { className: CARD_MEDIA_CLASS, children: _jsx(ListingPhotoStrip, { images: photos, title: title.length > 0 ? title : String(listing.id), testId: "listings-serp-photos" }) }), _jsx("div", { className: CARD_MAIN_CLASS, children: _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"
@@ -1 +1 @@
1
- {"version":3,"file":"ListingSerpCard.js","sourceRoot":"","sources":["../../src/default/ListingSerpCard.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ListingSerpCard.js","sourceRoot":"","sources":["../../src/default/ListingSerpCard.tsx"],"names":[],"mappings":";AA4EA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEjE,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,UAAU,EACV,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AA4DjD;mCACmC;AACnC,MAAM,IAAI,GAAkB,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAEjD;wBACwB;AACxB,MAAM,IAAI,GAAkB,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAE9D,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IACtC,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;IAEvC,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5D,0EAA0E;IAC1E,+DAA+D;IAC/D,MAAM,IAAI,GACR,KAAK,CAAC,gBAAgB,KAAK,SAAS;QAClC,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EAAE;QAC9C,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,aAAa,GAAG,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC;IACtD,mEAAmE;IACnE,qEAAqE;IACrE,MAAM,SAAS,GACb,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;QACzD,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAE/C,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE9E,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;IAChE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IACpC,MAAM,QAAQ,GACZ,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvE,MAAM,IAAI,GACR,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,CAChE,MAAC,IAAI,IACH,QAAQ,QACR,KAAK,EAAC,UAAU,EAChB,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EACf,KAAK,EAAE,IAAI,iBACC,uBAAuB,aAElC,KAAK,CAAC,WAAW,EACjB,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,KAC3B,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS;oBACpC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE;oBACxC,CAAC,CAAC,EAAE,CAAC,KACH,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAChE,CACH,IACI,CACR,CAAC,CAAC,CAAC,IAAI,CAAC;IAEX,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,KAAC,IAAI,IACH,IAAI,EAAC,OAAO,iBACA,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,EACP,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,EAC3D,KAAK,EAAE,EAAE,CAAC,sBAAgC,CAAC,EAAE,KAAK,CAAC,YAAY,EAAE,YAEjE,cAAK,SAAS,EAAE,gBAAgB,YAC9B,eAAK,SAAS,EAAE,gBAAgB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,aAK1D,cAAK,SAAS,EAAE,gBAAgB,YAC9B,KAAC,iBAAiB,IAChB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EACpD,MAAM,EAAC,sBAAsB,GAC7B,GACE,EAEN,cAAK,SAAS,EAAE,eAAe,YAC7B,MAAC,IAAI,IAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAC,YAAY,aACvC,MAAC,IAAI,IAAC,QAAQ,QAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,aACzC,MAAC,UAAU,OACL,SAAS,CAAC,KAAK,CAAC,EACpB,SAAS,EAAE,OAAO,CAAC,EAAE,EACrB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAC,oBAAoB,EAC3B,UAAU,EAAC,oBAAoB,aAE9B,KAAK,CAAC,KAAK,EAMZ,MAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,mBACxC,KAAC,UAAU,CAAC,IAAI,IACd,MAAM,QACN,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAC7B,qBAAqB,YAEjC,KAAC,YAAY,IAAC,MAAM,EAAE,OAAO,CAAC,KAAK,KAAM,QAAQ,GAAI,GACrC,EACjB,UAAU,KAAK,SAAS,IAAI,CAC3B,KAAC,cAAc,IACb,SAAS,EAAE,UAAU,CAAC,SAAS,EAC/B,KAAK,EAAE,CAAC,CACN,UAAU,CAAC,SAAS,KAAK,MAAM;wEAC7B,CAAC,CAAC,kBAAkB,CAAC,gBAAgB;wEACrC,CAAC,CAAC,kBAAkB,CAAC,eAAe,CACvC,GACD,CACH,IACI,EAEN,UAAU,KAAK,SAAS,IAAI,CAC3B,MAAC,IAAI,IACH,KAAK,EAAC,UAAU,EAChB,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,iBACH,yBAAyB,aAKrC,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,YAC9B,CAAC,CAAC,kBAAkB,CAAC,YAAY,CAAC,GACnB,EAClB,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,EAAC,MAAM,kBACtC,KAAC,YAAY,IAAC,MAAM,EAAE,UAAU,CAAC,QAAQ,KAAM,QAAQ,GAAI,GAC3C,IACb,CACR,EAED,KAAC,UAAU,CAAC,IAAI,mBAAa,qBAAqB,YAC/C,KAAK,GACU,EAEjB,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAC7D,KAAC,UAAU,CAAC,IAAI,IACd,IAAI,EAAC,WAAW,EAChB,QAAQ,uBACI,qBAAqB,YAEhC,KAAK,CAAC,SAAS,GACA,CACnB,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACzB,KAAC,UAAU,CAAC,IAAI,IACd,IAAI,EAAC,WAAW,EAChB,QAAQ,uBACI,qBAAqB,YAEjC,KAAC,aAAa,IACZ,QAAQ,EAAE,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,GAAG,CAChD,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CACvB,EACD,MAAM,EAAE,sBAAsB,CAAC,SAAS,CAAC,GACzC,GACc,CACnB,CAAC,CAAC,CAAC,IAAI,EAEP,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAC1B,KAAC,aAAa,IACZ,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EACnD,MAAM,EAAE,WAAW,GACnB,CACH,CAAC,CAAC,CAAC,IAAI,IACG,EAKZ,KAAK,CAAC,UAAU,IAAI,IAAI,EAExB,OAAO,CAAC,cAAc,KAAK,SAAS;oDACrC,OAAO,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAClC,KAAC,UAAU,CAAC,IAAI,IACd,IAAI,EAAC,WAAW,iBACJ,wBAAwB,YAEnC,OAAO,CAAC,cAAc,GACP,CACnB,CAAC,CAAC,CAAC,IAAI,IACH,EAEN,IAAI,IACA,GACH,IACF,GACF,GACD,IACG,CACb,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,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"}
@@ -26,6 +26,7 @@
26
26
  * list or a grid of these cards should wrap it in one.
27
27
  */
28
28
  import type { CSSProperties, ReactElement } from "react";
29
+ import type { SignInCta } from "@stapel/core";
29
30
  export interface FavoriteHeartProps {
30
31
  readonly listingId: number;
31
32
  /** `is_favorited` off the row. `null` is the third state — a search hit
@@ -36,6 +37,22 @@ export interface FavoriteHeartProps {
36
37
  /** `"inline"` puts the reason beside the heart, `"stack"` (default) under
37
38
  * it — the choice belongs to the surface, which knows its own geometry. */
38
39
  readonly layout?: "stack" | "inline";
40
+ /**
41
+ * How loudly the refusal is stated on THIS surface (D45).
42
+ *
43
+ * `"text"` (default) keeps the standing sentence beside the heart — what
44
+ * this control has always done, and the right answer where one card is the
45
+ * screen. `"popover"` moves it onto the gesture: a signed-out phone SERP
46
+ * printed "sign in to do this" once under every one of fourteen cards, in
47
+ * the line where a price belongs, and fourteen copies of one sentence is
48
+ * not fourteen pieces of help. The reason never leaves the accessibility
49
+ * tree either way — see {@link GateReasonPopover}.
50
+ */
51
+ readonly blockedReason?: "text" | "popover";
52
+ /** The surface's sign-in door, rendered INSIDE the disclosure. Absent: the
53
+ * disclosure holds the reason alone. Only read in the `"popover"` arm; the
54
+ * standing arm's door is the container's, as it always was. */
55
+ readonly signIn?: SignInCta;
39
56
  readonly style?: CSSProperties;
40
57
  }
41
58
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"favorite.d.ts","sourceRoot":"","sources":["../../src/default/favorite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAQzD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;qEACiE;IACjE,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;+EAC2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,CAmCrE"}
1
+ {"version":3,"file":"favorite.d.ts","sourceRoot":"","sources":["../../src/default/favorite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAGzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAO9C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;qEACiE;IACjE,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;+EAC2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACrC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;mEAE+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,CAoErE"}
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { SkinButton as Button } from "@stapel/tokens-antd/skin";
3
3
  import { GatedControl } from "@stapel/tokens-antd/skin";
4
- import { useT } from "@stapel/core";
4
+ import { useActionGate, useT } from "@stapel/core";
5
5
  import { useFavoriteToggle } from "../headless/Favorites.js";
6
6
  import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
7
+ import { GateReasonPopover } from "./GateReasonPopover.js";
7
8
  import { HeartIcon } from "./icons.js";
8
9
  /**
9
10
  * The heart and its refusal. It is NEVER hidden from a visitor: it is blocked,
@@ -18,6 +19,15 @@ export function FavoriteHeart(props) {
18
19
  const label = t(favorite.favorited
19
20
  ? LISTINGS_I18N_KEYS.cardFavoriteRemove
20
21
  : LISTINGS_I18N_KEYS.cardFavoriteAdd);
22
+ // The RESOLVED sentence, not the gate's key — `useActionGate` is the one
23
+ // place a blocked reason becomes words in this fleet.
24
+ const reason = useActionGate(favorite.gate).reason;
25
+ if (props.blockedReason === "popover" && reason !== undefined) {
26
+ // `aria-disabled`, never `disabled`: the gate already refuses the action
27
+ // (`toggle` is a no-op while blocked), and an html-disabled button
28
+ // swallows the hover, the focus and the tap the disclosure opens on.
29
+ return (_jsx(GateReasonPopover, { reason: reason, cta: props.signIn, testId: `${props.testId}-reason`, signInTestId: `${props.testId}-sign-in`, children: (bind) => (_jsx(Button, { shape: "circle", "aria-disabled": true, ...bind, "aria-label": label, "aria-pressed": favorite.favorited, "data-testid": props.testId, "data-favorited": String(favorite.favorited), "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", onClick: favorite.toggle, icon: _jsx(HeartIcon, { filled: favorite.favorited }), ...(props.style !== undefined ? { style: props.style } : {}) })) }));
30
+ }
21
31
  return (_jsx(GatedControl, { gate: favorite.gate, testId: `${props.testId}-gate`, ...(props.layout !== undefined ? { layout: props.layout } : {}), ...(props.style !== undefined ? { style: props.style } : {}), children: (bind) => (_jsx(Button, { shape: "circle", disabled: bind.disabled, "data-disabled-reason": "the enclosing <GatedControl> renders the gate's reason beside this button", ...(bind["aria-describedby"] !== undefined
22
32
  ? { "aria-describedby": bind["aria-describedby"] }
23
33
  : {}), "aria-label": label, "aria-pressed": favorite.favorited, "data-testid": props.testId, "data-favorited": String(favorite.favorited), "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", onClick: favorite.toggle, icon: _jsx(HeartIcon, { filled: favorite.favorited }) })) }));
@@ -1 +1 @@
1
- {"version":3,"file":"favorite.js","sourceRoot":"","sources":["../../src/default/favorite.tsx"],"names":[],"mappings":";AA4BA,OAAO,EAAE,UAAU,IAAI,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAevC;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,CAAC,CACb,QAAQ,CAAC,SAAS;QAChB,CAAC,CAAC,kBAAkB,CAAC,kBAAkB;QACvC,CAAC,CAAC,kBAAkB,CAAC,eAAe,CACvC,CAAC;IACF,OAAO,CACL,KAAC,YAAY,IACX,IAAI,EAAE,QAAQ,CAAC,IAAI,EACnB,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,OAAO,KAC1B,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAC5D,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YAE5D,CAAC,IAAI,EAAE,EAAE,CAAC,CACT,KAAC,MAAM,IACL,KAAK,EAAC,QAAQ,EACd,QAAQ,EAAE,IAAI,CAAC,QAAQ,0BACF,2EAA2E,KAC5F,CAAC,IAAI,CAAC,kBAAkB,CAAC,KAAK,SAAS;gBACzC,CAAC,CAAC,EAAE,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,CAAC,EAAE;gBAClD,CAAC,CAAC,EAAE,CAAC,gBACK,KAAK,kBACH,QAAQ,CAAC,SAAS,iBACnB,KAAK,CAAC,MAAM,oBACT,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,oBAC3B,MAAM,2BACC,8DAAyD,EAC/E,OAAO,EAAE,QAAQ,CAAC,MAAM,EACxB,IAAI,EAAE,KAAC,SAAS,IAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,GAAI,GAC/C,CACH,GACY,CAChB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"favorite.js","sourceRoot":"","sources":["../../src/default/favorite.tsx"],"names":[],"mappings":"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"}
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @stapel/listings-react 0.18.1
1
+ # @stapel/listings-react 0.18.2
2
2
 
3
3
  Headless React flow pair for stapel-listings (contract >=0.12 <0.13) — business + state, zero visual opinion.
4
4
  Built on @stapel/core: typed client + StapelApiError envelope, auth token refresh,
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$generated": "by scripts/gen-manifest.mjs — do not edit; drift-gated (pnpm gen:manifest:check)",
3
3
  "package": "@stapel/listings-react",
4
- "version": "0.18.1",
4
+ "version": "0.18.2",
5
5
  "backend": {
6
6
  "module": "stapel-listings",
7
7
  "contract": ">=0.12 <0.13"
package/nav-manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@stapel/listings-react",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "entries": [
5
5
  {
6
6
  "id": "listings.detail",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stapel/listings-react",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "description": "Headless React pair for stapel-listings: a typed client over the draft twin, the composer that turns a category schema into a submittable listing, and the owner's dashboard. Two independent axes are rendered as two — lifecycle `status` decides visibility and `moderation_status` decides nothing about it, so an edit to a live listing stays live and says 'under review' instead of vanishing. Feature values are drawn and mirrored by @stapel/attributes-react; photos arrive as an injected upload bag whose `refs` ARE `images_draft` and whose `settled` gates the submit; a publish refusal is routed onto the control that caused it by slug. Zero visual opinion in the main entry; an opt-in /default subpath ships the antd skin.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -103,8 +103,8 @@
103
103
  "typescript": "^5.8.3",
104
104
  "vitest": "^3.2.4",
105
105
  "@stapel/attributes-react": "^0.12.1",
106
- "@stapel/core": "^0.23.1",
107
106
  "@stapel/currencies-react": "^0.3.0",
107
+ "@stapel/core": "^0.23.1",
108
108
  "@stapel/image": "^0.4.2",
109
109
  "@stapel/showcase": "^0.3.0",
110
110
  "@stapel/tokens": "^0.7.0",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$generated": "by scripts/gen-events.mjs — do not edit; drift-gated (pnpm gen:events:check)",
3
3
  "package": "@stapel/listings-react",
4
- "version": "0.18.1",
4
+ "version": "0.18.2",
5
5
  "defined": [],
6
6
  "flows": []
7
7
  }
@@ -76,6 +76,7 @@
76
76
  import type { CSSProperties, ReactElement, ReactNode } from "react";
77
77
  import { Card, Flex, Typography, theme as antdTheme } from "antd";
78
78
  import { SkinTheme } from "@stapel/tokens-antd/skin";
79
+ import type { SignInCta } from "@stapel/core";
79
80
  import { useT } from "@stapel/core";
80
81
  import { fontSize, spacing } from "@stapel/tokens";
81
82
  import { FeatureBadges } from "@stapel/attributes-react/default";
@@ -149,6 +150,14 @@ export interface ListingSerpCardBaseProps
149
150
  /** Hide the favourite entirely — for a surface where it makes no sense (the
150
151
  * owner's own listings). NOT a way to hide it from visitors. */
151
152
  readonly showFavorite?: boolean;
153
+ /** How loudly the blocked heart states its refusal (D45) — `"text"`
154
+ * (default) keeps the standing sentence, `"popover"` moves it onto the
155
+ * gesture. A phone SERP is the surface that needed the choice: fourteen
156
+ * cards printed fourteen copies of one sentence. */
157
+ readonly blockedReason?: "text" | "popover";
158
+ /** The container's sign-in door, shown inside the disclosure when the
159
+ * refusal is a `"popover"`. */
160
+ readonly signIn?: SignInCta;
152
161
  }
153
162
 
154
163
  export type ListingSerpCardProps = ListingSerpCardBaseProps & ListingCardOpenProps;
@@ -207,6 +216,10 @@ export function ListingSerpCard(props: ListingSerpCardProps): ReactElement {
207
216
  listingId={listing.id}
208
217
  favorited={listing.is_favorited}
209
218
  testId="listings-serp-favorite"
219
+ {...(props.blockedReason !== undefined
220
+ ? { blockedReason: props.blockedReason }
221
+ : {})}
222
+ {...(props.signIn !== undefined ? { signIn: props.signIn } : {})}
210
223
  />
211
224
  )}
212
225
  </Flex>
@@ -28,9 +28,11 @@
28
28
  import type { CSSProperties, ReactElement } from "react";
29
29
  import { SkinButton as Button } from "@stapel/tokens-antd/skin";
30
30
  import { GatedControl } from "@stapel/tokens-antd/skin";
31
- import { useT } from "@stapel/core";
31
+ import type { SignInCta } from "@stapel/core";
32
+ import { useActionGate, useT } from "@stapel/core";
32
33
  import { useFavoriteToggle } from "../headless/Favorites.js";
33
34
  import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
35
+ import { GateReasonPopover } from "./GateReasonPopover.js";
34
36
  import { HeartIcon } from "./icons.js";
35
37
 
36
38
  export interface FavoriteHeartProps {
@@ -43,6 +45,22 @@ export interface FavoriteHeartProps {
43
45
  /** `"inline"` puts the reason beside the heart, `"stack"` (default) under
44
46
  * it — the choice belongs to the surface, which knows its own geometry. */
45
47
  readonly layout?: "stack" | "inline";
48
+ /**
49
+ * How loudly the refusal is stated on THIS surface (D45).
50
+ *
51
+ * `"text"` (default) keeps the standing sentence beside the heart — what
52
+ * this control has always done, and the right answer where one card is the
53
+ * screen. `"popover"` moves it onto the gesture: a signed-out phone SERP
54
+ * printed "sign in to do this" once under every one of fourteen cards, in
55
+ * the line where a price belongs, and fourteen copies of one sentence is
56
+ * not fourteen pieces of help. The reason never leaves the accessibility
57
+ * tree either way — see {@link GateReasonPopover}.
58
+ */
59
+ readonly blockedReason?: "text" | "popover";
60
+ /** The surface's sign-in door, rendered INSIDE the disclosure. Absent: the
61
+ * disclosure holds the reason alone. Only read in the `"popover"` arm; the
62
+ * standing arm's door is the container's, as it always was. */
63
+ readonly signIn?: SignInCta;
46
64
  readonly style?: CSSProperties;
47
65
  }
48
66
 
@@ -61,6 +79,39 @@ export function FavoriteHeart(props: FavoriteHeartProps): ReactElement {
61
79
  ? LISTINGS_I18N_KEYS.cardFavoriteRemove
62
80
  : LISTINGS_I18N_KEYS.cardFavoriteAdd
63
81
  );
82
+ // The RESOLVED sentence, not the gate's key — `useActionGate` is the one
83
+ // place a blocked reason becomes words in this fleet.
84
+ const reason = useActionGate(favorite.gate).reason;
85
+ if (props.blockedReason === "popover" && reason !== undefined) {
86
+ // `aria-disabled`, never `disabled`: the gate already refuses the action
87
+ // (`toggle` is a no-op while blocked), and an html-disabled button
88
+ // swallows the hover, the focus and the tap the disclosure opens on.
89
+ return (
90
+ <GateReasonPopover
91
+ reason={reason}
92
+ cta={props.signIn}
93
+ testId={`${props.testId}-reason`}
94
+ signInTestId={`${props.testId}-sign-in`}
95
+ >
96
+ {(bind) => (
97
+ <Button
98
+ shape="circle"
99
+ aria-disabled
100
+ {...bind}
101
+ aria-label={label}
102
+ aria-pressed={favorite.favorited}
103
+ data-testid={props.testId}
104
+ data-favorited={String(favorite.favorited)}
105
+ data-analytics="none"
106
+ data-analytics-reason="business action — host app wraps with its own tracked()"
107
+ onClick={favorite.toggle}
108
+ icon={<HeartIcon filled={favorite.favorited} />}
109
+ {...(props.style !== undefined ? { style: props.style } : {})}
110
+ />
111
+ )}
112
+ </GateReasonPopover>
113
+ );
114
+ }
64
115
  return (
65
116
  <GatedControl
66
117
  gate={favorite.gate}