@stapel/listings-react 0.25.10 → 0.27.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 (144) hide show
  1. package/CHANGELOG.md +259 -0
  2. package/MODULE.md +3 -1
  3. package/README.md +122 -9
  4. package/dist/api/generated/schema.d.ts +22 -12
  5. package/dist/api/generated/schema.d.ts.map +1 -1
  6. package/dist/default/CardBadges.d.ts.map +1 -1
  7. package/dist/default/CardBadges.js +3 -1
  8. package/dist/default/CardBadges.js.map +1 -1
  9. package/dist/default/ListingActions.d.ts +108 -0
  10. package/dist/default/ListingActions.d.ts.map +1 -0
  11. package/dist/default/ListingActions.js +29 -0
  12. package/dist/default/ListingActions.js.map +1 -0
  13. package/dist/default/ListingCard.d.ts.map +1 -1
  14. package/dist/default/ListingCard.js +15 -8
  15. package/dist/default/ListingCard.js.map +1 -1
  16. package/dist/default/ListingDetailPane.d.ts +170 -62
  17. package/dist/default/ListingDetailPane.d.ts.map +1 -1
  18. package/dist/default/ListingDetailPane.js +284 -28
  19. package/dist/default/ListingDetailPane.js.map +1 -1
  20. package/dist/default/ListingFeedCard.d.ts.map +1 -1
  21. package/dist/default/ListingFeedCard.js +2 -1
  22. package/dist/default/ListingFeedCard.js.map +1 -1
  23. package/dist/default/ListingPhoto.d.ts.map +1 -1
  24. package/dist/default/ListingPhoto.js +5 -2
  25. package/dist/default/ListingPhoto.js.map +1 -1
  26. package/dist/default/ListingSerpCard.d.ts.map +1 -1
  27. package/dist/default/ListingSerpCard.js +18 -4
  28. package/dist/default/ListingSerpCard.js.map +1 -1
  29. package/dist/default/MyListingsPane.d.ts.map +1 -1
  30. package/dist/default/MyListingsPane.js +49 -21
  31. package/dist/default/MyListingsPane.js.map +1 -1
  32. package/dist/default/ShareAction.d.ts +32 -0
  33. package/dist/default/ShareAction.d.ts.map +1 -0
  34. package/dist/default/ShareAction.js +116 -0
  35. package/dist/default/ShareAction.js.map +1 -0
  36. package/dist/default/actionRow.d.ts +58 -0
  37. package/dist/default/actionRow.d.ts.map +1 -0
  38. package/dist/default/actionRow.js +128 -0
  39. package/dist/default/actionRow.js.map +1 -0
  40. package/dist/default/cardGallery.d.ts +2 -0
  41. package/dist/default/cardGallery.d.ts.map +1 -1
  42. package/dist/default/cardGallery.js +31 -0
  43. package/dist/default/cardGallery.js.map +1 -1
  44. package/dist/default/detailGallery.d.ts +77 -0
  45. package/dist/default/detailGallery.d.ts.map +1 -0
  46. package/dist/default/detailGallery.js +91 -0
  47. package/dist/default/detailGallery.js.map +1 -0
  48. package/dist/default/favorite.d.ts +25 -0
  49. package/dist/default/favorite.d.ts.map +1 -1
  50. package/dist/default/favorite.js +29 -3
  51. package/dist/default/favorite.js.map +1 -1
  52. package/dist/default/icons.d.ts +19 -0
  53. package/dist/default/icons.d.ts.map +1 -1
  54. package/dist/default/icons.js +23 -0
  55. package/dist/default/icons.js.map +1 -1
  56. package/dist/default/index.d.ts +11 -2
  57. package/dist/default/index.d.ts.map +1 -1
  58. package/dist/default/index.js +10 -1
  59. package/dist/default/index.js.map +1 -1
  60. package/dist/default/movableCluster.d.ts +18 -0
  61. package/dist/default/movableCluster.d.ts.map +1 -0
  62. package/dist/default/movableCluster.js +110 -0
  63. package/dist/default/movableCluster.js.map +1 -0
  64. package/dist/default/notice.d.ts +14 -0
  65. package/dist/default/notice.d.ts.map +1 -0
  66. package/dist/default/notice.js +62 -0
  67. package/dist/default/notice.js.map +1 -0
  68. package/dist/headless/MyListings.d.ts +61 -19
  69. package/dist/headless/MyListings.d.ts.map +1 -1
  70. package/dist/headless/MyListings.js +58 -19
  71. package/dist/headless/MyListings.js.map +1 -1
  72. package/dist/headless/Share.d.ts +103 -0
  73. package/dist/headless/Share.d.ts.map +1 -0
  74. package/dist/headless/Share.js +190 -0
  75. package/dist/headless/Share.js.map +1 -0
  76. package/dist/i18n/es.d.ts.map +1 -1
  77. package/dist/i18n/es.js +21 -0
  78. package/dist/i18n/es.js.map +1 -1
  79. package/dist/i18n/generated/errors.es.gen.d.ts +1 -1
  80. package/dist/i18n/generated/errors.es.gen.js +1 -1
  81. package/dist/i18n/generated/errors.gen.d.ts +18 -0
  82. package/dist/i18n/generated/errors.gen.d.ts.map +1 -1
  83. package/dist/i18n/generated/errors.gen.js +9 -0
  84. package/dist/i18n/generated/errors.gen.js.map +1 -1
  85. package/dist/i18n/generated/errors.ru.gen.d.ts +1 -1
  86. package/dist/i18n/generated/errors.ru.gen.js +1 -1
  87. package/dist/i18n/keys.d.ts +45 -0
  88. package/dist/i18n/keys.d.ts.map +1 -1
  89. package/dist/i18n/keys.js +60 -0
  90. package/dist/i18n/keys.js.map +1 -1
  91. package/dist/i18n/ru.d.ts.map +1 -1
  92. package/dist/i18n/ru.js +21 -0
  93. package/dist/i18n/ru.js.map +1 -1
  94. package/dist/index.d.ts +5 -3
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +2 -1
  97. package/dist/index.js.map +1 -1
  98. package/dist/model/cardBadges.d.ts +52 -3
  99. package/dist/model/cardBadges.d.ts.map +1 -1
  100. package/dist/model/cardBadges.js +74 -12
  101. package/dist/model/cardBadges.js.map +1 -1
  102. package/dist/model/mineSource.d.ts +9 -3
  103. package/dist/model/mineSource.d.ts.map +1 -1
  104. package/dist/model/mineSource.js.map +1 -1
  105. package/dist/model/status.d.ts +79 -15
  106. package/dist/model/status.d.ts.map +1 -1
  107. package/dist/model/status.js +86 -17
  108. package/dist/model/status.js.map +1 -1
  109. package/llms.txt +8 -3
  110. package/manifest.json +96 -4
  111. package/nav-manifest.json +1 -1
  112. package/package.json +10 -10
  113. package/src/analytics/generated/events.json +1 -1
  114. package/src/api/generated/schema.ts +22 -12
  115. package/src/default/CardBadges.tsx +3 -1
  116. package/src/default/ListingActions.tsx +196 -0
  117. package/src/default/ListingCard.tsx +79 -43
  118. package/src/default/ListingDetailPane.tsx +520 -89
  119. package/src/default/ListingFeedCard.tsx +2 -0
  120. package/src/default/ListingPhoto.tsx +28 -0
  121. package/src/default/ListingSerpCard.tsx +39 -12
  122. package/src/default/MyListingsPane.tsx +81 -44
  123. package/src/default/ShareAction.tsx +260 -0
  124. package/src/default/actionRow.ts +131 -0
  125. package/src/default/cardGallery.ts +31 -0
  126. package/src/default/detailGallery.ts +97 -0
  127. package/src/default/favorite.tsx +68 -1
  128. package/src/default/icons.tsx +60 -0
  129. package/src/default/index.ts +37 -1
  130. package/src/default/movableCluster.tsx +137 -0
  131. package/src/default/notice.ts +68 -0
  132. package/src/headless/MyListings.tsx +130 -39
  133. package/src/headless/Share.tsx +278 -0
  134. package/src/i18n/es.ts +28 -0
  135. package/src/i18n/generated/errors.es.gen.ts +1 -1
  136. package/src/i18n/generated/errors.gen.ts +9 -0
  137. package/src/i18n/generated/errors.json +31 -0
  138. package/src/i18n/generated/errors.ru.gen.ts +1 -1
  139. package/src/i18n/keys.ts +63 -0
  140. package/src/i18n/ru.ts +28 -0
  141. package/src/index.ts +22 -0
  142. package/src/model/cardBadges.ts +124 -12
  143. package/src/model/mineSource.ts +9 -3
  144. package/src/model/status.ts +102 -20
@@ -118,6 +118,11 @@ import { isListingViewed } from "../model/engagement.js";
118
118
  import { useEngagedListing } from "../headless/Engagement.js";
119
119
  import { useFavoriteToggle } from "../headless/Favorites.js";
120
120
  import { FavoriteHeart } from "./favorite.js";
121
+ import {
122
+ LISTING_ACTIONS_CLASS,
123
+ LISTING_ACTIONS_OVERLAY_CLASS,
124
+ LISTING_CARD_ACTION_CLASS,
125
+ } from "./actionRow.js";
121
126
  import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
122
127
  import { CardBadges, CardSpecLine } from "./CardBadges.js";
123
128
  import { SignInLink } from "./SignInLink.js";
@@ -330,7 +335,10 @@ export function cardTargetCss(): string {
330
335
  // of pictures with a torn right edge. One radius, stated by the card,
331
336
  // applied to the box that holds the pictures; the slides inside it are
332
337
  // square because the well is what has the shape.
333
- `${media}{min-inline-size:0;overflow:hidden;` +
338
+ // `position:relative` is the containing block the favourite overlay is
339
+ // pinned inside (see `LISTING_ACTIONS_OVERLAY_CLASS`). A `relative` with
340
+ // no offsets moves no pixel of what is already in the well.
341
+ `${media}{position:relative;min-inline-size:0;overflow:hidden;` +
334
342
  `border-start-start-radius:var(--listing-card-radius);` +
335
343
  `border-start-end-radius:var(--listing-card-radius)}`,
336
344
  `${media} .${SKIN_CAROUSEL_SLIDE_CLASS}{border-radius:0}`,
@@ -670,53 +678,84 @@ export function ListingCard(props: ListingCardProps): ReactElement {
670
678
  ? { linkComponent: props.linkComponent }
671
679
  : {})}
672
680
  />
673
- </div>
674
-
675
- <div className={CARD_MAIN_CLASS}>
676
- <CardTarget {...props} listingId={listing.id} label={targetLabel}>
677
- {content}
678
- </CardTarget>
679
681
 
680
- {/* The heart, and only the heart, is a separate CONTROL outside the
681
- anchor: a button inside a link is neither valid HTML nor operable.
682
- Its refusal gets a line of its own here, which is the whole reason
683
- it is a row under the card rather than a glyph floating on the
684
- photograph. */}
682
+ {/* THE HEART IS ON THE PHOTOGRAPH (owner, 2026-09-06).
683
+ It used to be a row UNDER the card, on the argument that a
684
+ blocked visitor's reason needs a line of text to live on and
685
+ there is nowhere to put one on top of a picture. Half of
686
+ that survived: the reason and the door still stand in their
687
+ own row below (see the block after this one), and only the
688
+ CONTROL moved. What it buys is the ergonomics every
689
+ reference classified has and this card did not — the heart
690
+ in the corner of the picture, where a thumb already is,
691
+ reachable without opening the listing and without the eye
692
+ travelling past a price, a title, a spec line and a place to
693
+ find it.
694
+
695
+ Top-trailing is the one free corner: the strip's dots own
696
+ the bottom centre and the "3 of 16" counter owns the bottom
697
+ trailing corner (`cardGalleryCss`).
698
+
699
+ `stopPropagation` on the bubble phase, not the capture: the
700
+ heart is already a SIBLING of the slide anchors rather than
701
+ a child, so nothing should reach the card from here — but a
702
+ press that did would open the listing instead of saving it,
703
+ and a capture-phase stop would swallow the disclosure's own
704
+ activation on the way in. */}
685
705
  {props.showFavorite === false ? null : (
686
706
  <div
687
- style={{
688
- paddingInline: token.paddingSM,
689
- paddingBlockEnd: token.paddingSM,
707
+ className={`${LISTING_ACTIONS_CLASS} ${LISTING_ACTIONS_OVERLAY_CLASS}`}
708
+ data-testid="listings-card-favorite-overlay"
709
+ onClick={(event) => {
710
+ event.stopPropagation();
690
711
  }}
691
712
  >
692
713
  {/* THE SHARED HEART, not a second copy of it.
693
714
  This card drew its own — a `<Button aria-disabled>` in
694
715
  the popover arm with `onClick={favorite.toggle}` behind
695
716
  it — and that copy stopped tracking `<FavoriteHeart>` the
696
- day the door landed (D431): on the desktop GRID a
697
- visitor's press reached a no-op toggle, announced itself
698
- as unavailable, and went nowhere, while the same press on
699
- the SERP row and the feed card opened the sign-in door.
700
- One control, one refusal shape, one door.
717
+ day the door landed (D431). One control, one refusal
718
+ shape, one door.
701
719
 
702
720
  The two derived ids are pinned to what this card has
703
721
  always published, because a rename is a breaking change
704
722
  dressed as a refactor. */}
705
- <Flex justify="flex-end" style={{ width: "100%" }}>
706
- <FavoriteHeart
707
- listingId={listing.id}
708
- favorited={listing.is_favorited}
709
- testId="listings-card-favorite"
710
- gateTestId="listings-card-actions"
711
- signInTestId="listings-card-sign-in"
712
- style={{ width: "100%" }}
713
- {...(blockedReason === "popover"
714
- ? { blockedReason: "popover" as const }
715
- : {})}
716
- {...(props.signIn !== undefined ? { signIn: props.signIn } : {})}
717
- />
718
- </Flex>
723
+ <FavoriteHeart
724
+ listingId={listing.id}
725
+ favorited={listing.is_favorited}
726
+ testId="listings-card-favorite"
727
+ gateTestId="listings-card-actions"
728
+ signInTestId="listings-card-sign-in"
729
+ className={LISTING_CARD_ACTION_CLASS}
730
+ {...(blockedReason === "popover"
731
+ ? { blockedReason: "popover" as const }
732
+ : {})}
733
+ {...(props.signIn !== undefined ? { signIn: props.signIn } : {})}
734
+ />
735
+ </div>
736
+ )}
737
+ </div>
719
738
 
739
+ <div className={CARD_MAIN_CLASS}>
740
+ <CardTarget {...props} listingId={listing.id} label={targetLabel}>
741
+ {content}
742
+ </CardTarget>
743
+
744
+ {/* THE REFUSAL'S OWN LINE, which is all that is left down here.
745
+ The control moved onto the photograph; the sentence a
746
+ blocked visitor needs cannot follow it there, so it keeps
747
+ the row it has always had — and the row is drawn only when
748
+ there is something to put in it, rather than as an empty
749
+ strip of padding under every card on the page. */}
750
+ {props.showFavorite === false ||
751
+ favoriteGate.reason === undefined ||
752
+ blockedReason !== "text" ? null : (
753
+ <div
754
+ style={{
755
+ paddingInline: token.paddingSM,
756
+ paddingBlockEnd: token.paddingSM,
757
+ }}
758
+ >
720
759
  {/* The DOOR AS A STANDING LINE, which is this card's own
721
760
  decision and not the heart's: `blockedReason="line"`
722
761
  drops it (twenty-four doors to one place is not
@@ -724,15 +763,12 @@ export function ListingCard(props: ListingCardProps): ReactElement {
724
763
  put it inside the disclosure. The press itself routes
725
764
  through the door in every arm — that part is the heart's.
726
765
  */}
727
- {favoriteGate.reason === undefined ||
728
- blockedReason !== "text" ? null : (
729
- <Typography.Text
730
- type="secondary"
731
- data-testid="listings-card-favorite-blocked"
732
- >
733
- <SignInLink cta={props.signIn} testId="listings-card-sign-in" />
734
- </Typography.Text>
735
- )}
766
+ <Typography.Text
767
+ type="secondary"
768
+ data-testid="listings-card-favorite-blocked"
769
+ >
770
+ <SignInLink cta={props.signIn} testId="listings-card-sign-in" />
771
+ </Typography.Text>
736
772
  </div>
737
773
  )}
738
774
  </div>