@stapel/listings-react 0.25.5 → 0.25.7

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,36 @@
1
1
  # @stapel/listings-react
2
2
 
3
+ ## 0.25.7
4
+
5
+ ### Patch Changes
6
+
7
+ - A card's photographs can be swiped on a phone again — the strip's own scroll position is now the truth.
8
+
9
+ Measured on the stand with a real touch sequence (probe p23): the gesture and the CSS were fine, the strip received the swipe and scrolled to 335.5px — and 284ms later the same code scrolled it back to 0. Wheel and scripted scrolls ended at 307; every touch drag and fling ended at 0, so on a phone no card's second photograph could be reached at all.
10
+
11
+ The cause was two disagreeing answers to "which photograph is on screen". A native scroll moves the strip without telling `useCardGallery`, so its `active` stayed 0 while the browser was showing 1 — and the hook's effect, on the next render, imposed the stale answer by scrolling back. Two changes, both about who owns the position:
12
+
13
+ - the effect now drives the strip ONLY for a move the hook asked for (a `requested` ref, honoured once and cleared); an `active` that arrives from the strip is reported, never re-imposed;
14
+ - `<SkinCarousel onSlideChange>` — which the card already mounts for its dots, so it costs no second listener — feeds the scroll-derived index back in, making the browser's position the source of truth for touch;
15
+ - and the rewind-to-first on `pointerleave` is a HOVER rule again: a cursor crossing a grid must leave forty tiles as it found them, but `pointerleave` fires for a touch pointer the moment it lifts, which is what rewound every swipe a fraction of a second after it landed.
16
+
17
+ The regression test drives a native scroll to the third slide and the re-render that follows a finger lifting, and asserts nothing scrolls back; it fails against the previous code.
18
+
19
+ Measured with dependencies held constant: the `default` bundle 24.59 → 24.64 KB, inside its 25 KB ceiling.
20
+
21
+ ## 0.25.6
22
+
23
+ ### Patch Changes
24
+
25
+ - 33b10b3: The card's favourite heart is never inert for a visitor. Where the surface
26
+ hands in a `signIn` door, a press GOES THROUGH IT — `?next=` and all — and the
27
+ heart is no longer announced as unavailable, because a control that acts on
28
+ press must not call itself disabled. Measured on the phone feed: the heart was
29
+ `aria-disabled`, a tap produced nothing whatever, and the only explanation
30
+ lived in a hover disclosure a finger never triggers. The reason stays where it
31
+ was, wired by `aria-describedby` and disclosed on hover as a hint. With no door
32
+ in hand nothing changes: blocked, alive, saying why.
33
+
3
34
  ## 0.25.5
4
35
 
5
36
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"ListingPhoto.d.ts","sourceRoot":"","sources":["../../src/default/ListingPhoto.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAMzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAYlD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAE5D;;4BAE4B;AAC5B,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,uEAAuE;IACvE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAwBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAmEnE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;sDACkD;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;CACxC,GAAG,YAAY,CAgGf"}
1
+ {"version":3,"file":"ListingPhoto.d.ts","sourceRoot":"","sources":["../../src/default/ListingPhoto.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAMzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAYlD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAE5D;;4BAE4B;AAC5B,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,uEAAuE;IACvE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAwBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAmEnE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;sDACkD;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;CACxC,GAAG,YAAY,CAsGf"}
@@ -130,7 +130,13 @@ export function ListingPhotoStrip(props) {
130
130
  const Link = props.linkComponent;
131
131
  return Link !== undefined ? (_jsx(Link, { href: props.href, className: PHOTO_LINK_CLASS, "aria-hidden": "true", tabIndex: -1, "data-testid": "listings-photo-link", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: photo }, key)) : (_jsx("a", { href: props.href, className: PHOTO_LINK_CLASS, "aria-hidden": "true", tabIndex: -1, "data-testid": "listings-photo-link", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: photo }, key));
132
132
  };
133
- return (_jsxs("div", { ref: gallery.ref, className: CARD_GALLERY_CLASS, "data-testid": `${props.testId}-gallery`, "data-gallery-photos": String(images.length), "data-gallery-active": String(gallery.active), "data-scrubbing": String(gallery.scrubbing), "data-analytics": "none", "data-analytics-reason": "a look, not an outcome \u2014 scrubbing photographs navigates nowhere and changes no record", onPointerMove: gallery.onPointerMove, onPointerDown: gallery.onPointerDown, onPointerUp: gallery.onPointerUp, onPointerCancel: gallery.onPointerCancel, onPointerLeave: gallery.onPointerLeave, children: [_jsx("style", { href: CARD_GALLERY_STYLE_HREF, precedence: "default", children: cardGalleryCss() }), _jsx(SkinCarousel, { label: t(LISTINGS_I18N_KEYS.cardPhotos), aspectRatio: LISTING_PHOTO_ASPECT, peek: many, dots: many, "data-testid": props.testId, children: images.length === 0
133
+ return (_jsxs("div", { ref: gallery.ref, className: CARD_GALLERY_CLASS, "data-testid": `${props.testId}-gallery`, "data-gallery-photos": String(images.length), "data-gallery-active": String(gallery.active), "data-scrubbing": String(gallery.scrubbing), "data-analytics": "none", "data-analytics-reason": "a look, not an outcome \u2014 scrubbing photographs navigates nowhere and changes no record", onPointerMove: gallery.onPointerMove, onPointerDown: gallery.onPointerDown, onPointerUp: gallery.onPointerUp, onPointerCancel: gallery.onPointerCancel, onPointerLeave: gallery.onPointerLeave, children: [_jsx("style", { href: CARD_GALLERY_STYLE_HREF, precedence: "default", children: cardGalleryCss() }), _jsx(SkinCarousel, { label: t(LISTINGS_I18N_KEYS.cardPhotos), aspectRatio: LISTING_PHOTO_ASPECT, peek: many, dots: many,
134
+ // WHERE THE STRIP ACTUALLY IS, back into the gallery. A finger scrolls
135
+ // this element natively; without this the hook's `active` was stale
136
+ // the moment a swipe settled, and its effect scrolled the strip back
137
+ // to the stale one. See `cardGallery.ts` — the carousel already
138
+ // measures this for its dots, so it costs no second listener.
139
+ onSlideChange: gallery.onSlideChange, "data-testid": props.testId, children: images.length === 0
134
140
  ? slide(_jsx(ListingPhoto, { imageRef: undefined, alt: title }), "empty")
135
141
  : images.map((reference, index) => slide(_jsx(ListingPhoto, { imageRef: reference, alt: many
136
142
  ? t(LISTINGS_I18N_KEYS.detailPhotoAlt, {
@@ -1 +1 @@
1
- {"version":3,"file":"ListingPhoto.js","sourceRoot":"","sources":["../../src/default/ListingPhoto.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ListingPhoto.js","sourceRoot":"","sources":["../../src/default/ListingPhoto.tsx"],"names":[],"mappings":";AAwBA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGrD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAE5D;;4BAE4B;AAC5B,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAU5C;;wEAEwE;AACxE,SAAS,WAAW;IAClB,OAAO,CACL,eACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,KAAK,EACjB,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,iBACV,MAAM,aAElB,eAAM,CAAC,EAAC,2HAA2H,GAAG,EACtI,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,MAAM,EAAC,CAAC,EAAC,KAAK,GAAG,IAChC,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAwB;IACnD,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;IACvC,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;IACrC,wEAAwE;IACxE,4EAA4E;IAC5E,wEAAwE;IACxE,2EAA2E;IAC3E,2EAA2E;IAC3E,mDAAmD;IACnD,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC;IACrC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChC,MAAM,IAAI,GAAG,OAAO,CAClB,GAAG,EAAE,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EACvF,CAAC,QAAQ,EAAE,OAAO,CAAC,CACpB,CAAC;IAEF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,CAAC,CACf,KAAK,CAAC,QAAQ,KAAK,SAAS;YAC1B,CAAC,CAAC,kBAAkB,CAAC,WAAW;YAChC,CAAC,CAAC,kBAAkB,CAAC,oBAAoB,CAC5C,CAAC;QACF,OAAO,CACL,eACE,IAAI,EAAC,KAAK,gBACE,OAAO,iBACP,uBAAuB,EACnC,KAAK,EAAE;gBACL,OAAO,EAAE,MAAM;gBACf,aAAa,EAAE,QAAQ;gBACvB,UAAU,EAAE,QAAQ;gBACpB,cAAc,EAAE,QAAQ;gBACxB,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;gBACf,KAAK,EAAE,MAAM;gBACb,WAAW,EAAE,oBAAoB;gBACjC,UAAU,EAAE,KAAK,CAAC,mBAAmB;gBACrC,KAAK,EAAE,KAAK,CAAC,mBAAmB;gBAChC,YAAY,EAAE,KAAK,CAAC,EAAE;gBACtB,GAAG,KAAK,CAAC,KAAK;aACf,aAED,KAAC,WAAW,KAAG,EACf,KAAC,UAAU,CAAC,IAAI,IACd,IAAI,EAAC,WAAW,EAChB,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,YAEzD,OAAO,GACQ,IACd,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,KAAC,KAAK,IACJ,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,KAAK,CAAC,GAAG,iBACF,gBAAgB,EAC5B,KAAK,EAAE;YACL,KAAK,EAAE,MAAM;YACb,WAAW,EAAE,oBAAoB;YACjC,SAAS,EAAE,OAAO;YAClB,YAAY,EAAE,KAAK,CAAC,EAAE;YACtB,GAAG,KAAK,CAAC,KAAK;SACf,GACD,CACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAgBjC;IACC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAChC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/B,2EAA2E;IAC3E,4DAA4D;IAC5D,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAE9C;;;;;;;OAOG;IACH,MAAM,KAAK,GAAG,CAAC,KAAmB,EAAE,GAAW,EAAgB,EAAE;QAC/D,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,CAAC;QACjC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAC1B,KAAC,IAAI,IAEH,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,SAAS,EAAE,gBAAgB,iBACf,MAAM,EAClB,QAAQ,EAAE,CAAC,CAAC,iBACA,qBAAqB,oBAClB,MAAM,2BACC,8DAAyD,YAE9E,KAAK,IATD,GAAG,CAUH,CACR,CAAC,CAAC,CAAC,CACF,YAEE,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,SAAS,EAAE,gBAAgB,iBACf,MAAM,EAClB,QAAQ,EAAE,CAAC,CAAC,iBACA,qBAAqB,oBAClB,MAAM,2BACC,8DAAyD,YAE9E,KAAK,IATD,GAAG,CAUN,CACL,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,CACL,eACE,GAAG,EAAE,OAAO,CAAC,GAAG,EAChB,SAAS,EAAE,kBAAkB,iBAChB,GAAG,KAAK,CAAC,MAAM,UAAU,yBACjB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,yBACrB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,oBAC3B,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,oBAC1B,MAAM,2BACC,6FAAwF,EAC9G,aAAa,EAAE,OAAO,CAAC,aAAa,EACpC,aAAa,EAAE,OAAO,CAAC,aAAa,EACpC,WAAW,EAAE,OAAO,CAAC,WAAW,EAChC,eAAe,EAAE,OAAO,CAAC,eAAe,EACxC,cAAc,EAAE,OAAO,CAAC,cAAc,aAEtC,gBAAO,IAAI,EAAE,uBAAuB,EAAE,UAAU,EAAC,SAAS,YACvD,cAAc,EAAE,GACX,EACR,KAAC,YAAY,IACX,KAAK,EAAE,CAAC,CAAC,kBAAkB,CAAC,UAAU,CAAC,EACvC,WAAW,EAAE,oBAAoB,EACjC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,IAAI;gBACV,uEAAuE;gBACvE,oEAAoE;gBACpE,qEAAqE;gBACrE,gEAAgE;gBAChE,8DAA8D;gBAC9D,aAAa,EAAE,OAAO,CAAC,aAAa,iBACvB,KAAK,CAAC,MAAM,YAExB,MAAM,CAAC,MAAM,KAAK,CAAC;oBAClB,CAAC,CAAC,KAAK,CAAC,KAAC,YAAY,IAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,GAAI,EAAE,OAAO,CAAC;oBACnE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAC9B,KAAK,CACH,KAAC,YAAY,IAEX,QAAQ,EAAE,SAAS,EACnB,GAAG,EACD,IAAI;4BACF,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,cAAc,EAAE;gCACnC,KAAK,EAAE,KAAK,GAAG,CAAC;gCAChB,KAAK,EAAE,MAAM,CAAC,MAAM;6BACrB,CAAC;4BACJ,CAAC,CAAC,KAAK,IARN,SAAS,CAUd,EACF,SAAS,CACV,CACF,GACQ,IACX,CACP,CAAC;AACJ,CAAC"}
@@ -56,6 +56,19 @@ export interface CardGallery {
56
56
  readonly ref: RefObject<HTMLDivElement | null>;
57
57
  /** The photograph currently on screen. */
58
58
  readonly active: number;
59
+ /**
60
+ * THE STRIP SAYS WHERE IT IS — wire to `<SkinCarousel onSlideChange>`.
61
+ *
62
+ * A native touch scroll (a drag, a fling, a snap settling) moves the strip
63
+ * without this hook being told, and until it was told there were two
64
+ * disagreeing answers to "which photograph is on screen": the browser's,
65
+ * which was true, and `active`, which was stale. The stale one won, through
66
+ * the effect below, by scrolling the strip back — measured on the stand
67
+ * (probe p23): a swipe scrolled to 335.5px and 284ms later the same code
68
+ * scrolled to 0. This is how the browser's answer gets in, and an index
69
+ * that arrives this way is REPORTED, never re-imposed.
70
+ */
71
+ readonly onSlideChange: (index: number) => void;
59
72
  /** True while a pointer is scrubbing — the box publishes it so the strip's
60
73
  * smooth-scroll can be switched off for the duration. */
61
74
  readonly scrubbing: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"cardGallery.d.ts","sourceRoot":"","sources":["../../src/default/cardGallery.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,YAAY,IAAI,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1E,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,2BAA2B,CAAC;AAC3D,oEAAoE;AACpE,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE,qEAAqE;AACrE,eAAO,MAAM,WAAW,uCAAuC,CAAC;AAEhE;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIlF;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAK5D;AAED;;;qDAGqD;AACrD,wBAAgB,cAAc,IAAI,OAAO,CAOxC;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,OAAO,CA0BxC;AAED,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC/C,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;6DACyD;IACzD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IAC3E,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IAC3E,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IACzE,QAAQ,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IAC7E,QAAQ,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;CAC7E;AAoBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CA0EzD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAevC"}
1
+ {"version":3,"file":"cardGallery.d.ts","sourceRoot":"","sources":["../../src/default/cardGallery.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,YAAY,IAAI,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1E,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,2BAA2B,CAAC;AAC3D,oEAAoE;AACpE,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE,qEAAqE;AACrE,eAAO,MAAM,WAAW,uCAAuC,CAAC;AAEhE;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIlF;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAK5D;AAED;;;qDAGqD;AACrD,wBAAgB,cAAc,IAAI,OAAO,CAOxC;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,OAAO,CA0BxC;AAED,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC/C,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD;6DACyD;IACzD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IAC3E,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IAC3E,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IACzE,QAAQ,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IAC7E,QAAQ,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;CAC7E;AAoBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAgIzD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAevC"}
@@ -178,14 +178,52 @@ export function useCardGallery(count) {
178
178
  // The origin of the drag in progress, or `null`. A ref rather than state:
179
179
  // it changes on every move and no render depends on it.
180
180
  const origin = useRef(null);
181
+ /**
182
+ * THE INDEX THIS HOOK ASKED FOR, and the whole of the fix.
183
+ *
184
+ * `active` has two sources now: the pair's OWN controls (the hover scrub,
185
+ * the swipe commit, the leave reset), and the strip reporting where a
186
+ * native scroll left it. Only the first kind may drive the strip — the
187
+ * second IS the strip, and scrolling it to where it already is means, in
188
+ * practice, scrolling it back to where it was: the effect below used to run
189
+ * on the render that followed a finger's own scroll and undo it.
190
+ *
191
+ * So a request is recorded here, the effect honours exactly that one, and
192
+ * clears it. An `active` that does not match a standing request came from
193
+ * the browser and is left alone.
194
+ */
195
+ const requested = useRef(null);
181
196
  const fine = useFinePointer();
182
197
  const many = count > 1;
183
- // The one place the strip is driven. `active` is the whole state of both
184
- // gestures, so neither handler talks to the DOM.
198
+ /** Move the strip. The only way `active` is set by anything of ours. */
199
+ const request = useCallback((next) => {
200
+ setActive((current) => {
201
+ const value = typeof next === "function" ? next(current) : next;
202
+ requested.current = value;
203
+ return value;
204
+ });
205
+ }, []);
206
+ /**
207
+ * Where the STRIP says it is, after a scroll nobody here asked for.
208
+ *
209
+ * Recorded, never acted on: `requested` is untouched, so the effect below
210
+ * sees an `active` it did not ask for and keeps its hands off the DOM. This
211
+ * is also what keeps the dots, the keyboard and this hook agreeing about
212
+ * the photograph on screen after a fling.
213
+ */
214
+ const onSlideChange = useCallback((index) => {
215
+ setActive(index);
216
+ }, []);
217
+ // The one place the strip is driven — and only for a move this hook asked
218
+ // for. `active` is the whole state of both gestures, so neither handler
219
+ // talks to the DOM.
185
220
  useEffect(() => {
186
221
  const box = ref.current;
187
222
  if (box === null || !many)
188
223
  return;
224
+ if (requested.current !== active)
225
+ return;
226
+ requested.current = null;
189
227
  showSlide(box, active, scrubbing);
190
228
  }, [active, scrubbing, many]);
191
229
  const onPointerMove = useCallback((event) => {
@@ -199,7 +237,7 @@ export function useCardGallery(count) {
199
237
  return;
200
238
  const rect = box.getBoundingClientRect();
201
239
  setScrubbing(true);
202
- setActive(segmentIndex(event.clientX - rect.left, rect.width, count));
240
+ request(segmentIndex(event.clientX - rect.left, rect.width, count));
203
241
  return;
204
242
  }
205
243
  const from = origin.current;
@@ -212,8 +250,8 @@ export function useCardGallery(count) {
212
250
  // photograph per threshold rather than one per gesture.
213
251
  origin.current = { x: event.clientX, y: event.clientY };
214
252
  setScrubbing(false);
215
- setActive((current) => Math.min(count - 1, Math.max(0, current + step)));
216
- }, [count, fine, many]);
253
+ request((current) => Math.min(count - 1, Math.max(0, current + step)));
254
+ }, [count, fine, many, request]);
217
255
  const onPointerDown = useCallback((event) => {
218
256
  if (!many || event.pointerType === "mouse")
219
257
  return;
@@ -222,19 +260,29 @@ export function useCardGallery(count) {
222
260
  const endDrag = useCallback(() => {
223
261
  origin.current = null;
224
262
  }, []);
225
- const onPointerLeave = useCallback(() => {
263
+ const onPointerLeave = useCallback((event) => {
226
264
  origin.current = null;
227
265
  if (!many)
228
266
  return;
229
- // The card goes back to the photograph it was drawn with. A hover is a
230
- // look, not an edit.
267
+ // THE REWIND IS A HOVER RULE, AND ONLY A HOVER RULE.
268
+ //
269
+ // The card goes back to the photograph it was drawn with after a
270
+ // CURSOR passes over it: a hover is a look, not an edit, and a grid of
271
+ // forty tiles must not end up forty different tiles because a cursor
272
+ // crossed them. A finger is the opposite — a swipe is a choice the
273
+ // person made — and `pointerleave` fires for a touch pointer the
274
+ // moment it is lifted, so this arm used to rewind every swipe a
275
+ // fraction of a second after it landed.
276
+ if (event.pointerType !== "mouse")
277
+ return;
231
278
  setScrubbing(false);
232
- setActive(0);
233
- }, [many]);
279
+ request(0);
280
+ }, [many, request]);
234
281
  return {
235
282
  ref,
236
283
  active,
237
284
  scrubbing,
285
+ onSlideChange,
238
286
  onPointerMove,
239
287
  onPointerDown,
240
288
  onPointerUp: endDrag,
@@ -1 +1 @@
1
- {"version":3,"file":"cardGallery.js","sourceRoot":"","sources":["../../src/default/cardGallery.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cardGallery.js","sourceRoot":"","sources":["../../src/default/cardGallery.ts"],"names":[],"mappings":"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"}
@@ -49,9 +49,17 @@ export interface FavoriteHeartProps {
49
49
  * tree either way — see {@link GateReasonPopover}.
50
50
  */
51
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. */
52
+ /**
53
+ * The surface's sign-in door.
54
+ *
55
+ * Given, it is where a VISITOR'S PRESS GOES, in both volumes (D431) — the
56
+ * heart stops being a control that refuses and becomes the way in, `?next=`
57
+ * and all. The `"popover"` arm additionally renders it inside the
58
+ * disclosure, as it always did.
59
+ *
60
+ * Absent, nothing changes: the control stays blocked-but-alive and states
61
+ * its reason, which is all a surface with no sign-in route can offer.
62
+ */
55
63
  readonly signIn?: SignInCta;
56
64
  readonly style?: CSSProperties;
57
65
  }
@@ -72,6 +80,34 @@ export interface FavoriteHeartProps {
72
80
  * was supposed to explain it. Measured on a phone: a signed-out visitor's
73
81
  * heart produced no toast, no reason and no navigation, and on a touch device
74
82
  * there is no hover to fall back on.
83
+ *
84
+ * ── And where there is a DOOR, it is not "blocked" at all (D431) ──────────
85
+ *
86
+ * Live-but-refusing was half the answer. On the deployed phone SERP the
87
+ * visitor's heart still carried `aria-disabled="true"`, a tap still produced
88
+ * nothing at all, and the only explanation was a hover disclosure a finger
89
+ * never triggers — the reason had been pooled into the pane's footnote, and
90
+ * neither this control nor `GatedControl` had ever been handed
91
+ * `onBlockedActivate`, so the gesture fell into the same hole `GatedControl`
92
+ * was written to close.
93
+ *
94
+ * The owner's ruling: **the control is never inert for a visitor.** Where the
95
+ * surface hands in a `signIn` door, a press GOES THROUGH IT, and the heart is
96
+ * therefore not announced as disabled either — a control that acts on press
97
+ * and calls itself unavailable is lying to exactly the people who most depend
98
+ * on the announcement. The reason stays where it was (registered with the
99
+ * pane's scope, wired by `aria-describedby`, hover disclosure and all on the
100
+ * surfaces that have one); it is now a HINT beside a live control rather than
101
+ * the only thing behind a dead one.
102
+ *
103
+ * Where the host hands in NO door there is nothing better for a press to do,
104
+ * and the control keeps the gated shape it has always had — blocked, alive,
105
+ * saying why.
106
+ *
107
+ * The `href` arm renders the heart as an anchor, deliberately: the door is a
108
+ * navigation (`/login?next=…`), and an anchor keeps the `next`, the middle
109
+ * click, and the person who is not running our JavaScript. `aria-pressed`
110
+ * comes off in that arm, because a link is not a toggle.
75
111
  */
76
112
  export declare function FavoriteHeart(props: FavoriteHeartProps): ReactElement;
77
113
  //# sourceMappingURL=favorite.d.ts.map
@@ -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;AAIzD,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;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,CAoFrE"}
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;AAIzD,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;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,CAkHrE"}
@@ -24,6 +24,34 @@ import { HeartIcon } from "./icons.js";
24
24
  * was supposed to explain it. Measured on a phone: a signed-out visitor's
25
25
  * heart produced no toast, no reason and no navigation, and on a touch device
26
26
  * there is no hover to fall back on.
27
+ *
28
+ * ── And where there is a DOOR, it is not "blocked" at all (D431) ──────────
29
+ *
30
+ * Live-but-refusing was half the answer. On the deployed phone SERP the
31
+ * visitor's heart still carried `aria-disabled="true"`, a tap still produced
32
+ * nothing at all, and the only explanation was a hover disclosure a finger
33
+ * never triggers — the reason had been pooled into the pane's footnote, and
34
+ * neither this control nor `GatedControl` had ever been handed
35
+ * `onBlockedActivate`, so the gesture fell into the same hole `GatedControl`
36
+ * was written to close.
37
+ *
38
+ * The owner's ruling: **the control is never inert for a visitor.** Where the
39
+ * surface hands in a `signIn` door, a press GOES THROUGH IT, and the heart is
40
+ * therefore not announced as disabled either — a control that acts on press
41
+ * and calls itself unavailable is lying to exactly the people who most depend
42
+ * on the announcement. The reason stays where it was (registered with the
43
+ * pane's scope, wired by `aria-describedby`, hover disclosure and all on the
44
+ * surfaces that have one); it is now a HINT beside a live control rather than
45
+ * the only thing behind a dead one.
46
+ *
47
+ * Where the host hands in NO door there is nothing better for a press to do,
48
+ * and the control keeps the gated shape it has always had — blocked, alive,
49
+ * saying why.
50
+ *
51
+ * The `href` arm renders the heart as an anchor, deliberately: the door is a
52
+ * navigation (`/login?next=…`), and an anchor keeps the `next`, the middle
53
+ * click, and the person who is not running our JavaScript. `aria-pressed`
54
+ * comes off in that arm, because a link is not a toggle.
27
55
  */
28
56
  export function FavoriteHeart(props) {
29
57
  const t = useT();
@@ -38,13 +66,35 @@ export function FavoriteHeart(props) {
38
66
  const icon = (_jsx(HeartIcon, { filled: favorite.favorited, ...(favorite.favorited ? { color: token.colorPrimary } : {}) }));
39
67
  // The RESOLVED sentence, not the gate's key — `useActionGate` is the one
40
68
  // place a blocked reason becomes words in this fleet.
41
- const reason = useActionGate(favorite.gate).reason;
69
+ const gate = useActionGate(favorite.gate);
70
+ const reason = gate.reason;
71
+ // D431. A blocked gate plus a door the surface handed in: the press is not
72
+ // refused, it is ROUTED — through the door, keeping whatever `?next=` the
73
+ // container put in the href. With no door there is nothing better for the
74
+ // press to do and the gated shape stands, exactly as before.
75
+ const door = props.signIn;
76
+ const opensDoor = gate.disabled && door !== undefined;
77
+ // Exactly one arm, never both — `SignInCta`'s own rule. The `href` arm
78
+ // makes antd render an anchor, which is the honest element for a door.
79
+ const doorPress = !opensDoor || door === undefined
80
+ ? {}
81
+ : door.href !== undefined
82
+ ? { href: door.href }
83
+ : { onClick: door.onSignIn };
84
+ // The heart, as the visitor with a door meets it: live, not announced as
85
+ // unavailable, and not pretending to be a toggle when it is a link.
86
+ const asDoor = opensDoor
87
+ ? {
88
+ ...doorPress,
89
+ ...(door?.href !== undefined ? {} : { "aria-pressed": favorite.favorited }),
90
+ }
91
+ : { "aria-pressed": favorite.favorited, onClick: favorite.toggle };
42
92
  // A failed save, stated where the heart is. The rollback already put the
43
93
  // icon back; this says why it went back.
44
94
  const failure = (_jsx(ErrorAlert, { testId: `${props.testId}-error`, thrown: favorite.error, variant: "inline" }));
45
95
  if (props.blockedReason === "popover" && reason !== undefined) {
46
- 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: icon, ...(props.style !== undefined ? { style: props.style } : {}) })) }));
96
+ return (_jsx(GateReasonPopover, { reason: reason, cta: props.signIn, testId: `${props.testId}-reason`, signInTestId: `${props.testId}-sign-in`, children: (bind) => (_jsx(Button, { shape: "circle", ...(opensDoor ? {} : { "aria-disabled": true }), ...bind, "aria-label": label, "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()", ...asDoor, icon: icon, ...(props.style !== undefined ? { style: props.style } : {}) })) }));
47
97
  }
48
- return (_jsx(GatedControl, { gate: favorite.gate, testId: `${props.testId}-gate`, ...(props.layout !== undefined ? { layout: props.layout } : {}), ...(props.style !== undefined ? { style: props.style } : {}), children: (bind) => (_jsxs(_Fragment, { children: [_jsx(Button, { shape: "circle", ...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: icon }), failure] })) }));
98
+ return (_jsx(GatedControl, { gate: favorite.gate, testId: `${props.testId}-gate`, ...(opensDoor ? { whenBlocked: "annotate" } : {}), ...(props.layout !== undefined ? { layout: props.layout } : {}), ...(props.style !== undefined ? { style: props.style } : {}), children: (bind) => (_jsxs(_Fragment, { children: [_jsx(Button, { shape: "circle", ...bind, "aria-label": label, "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()", ...asDoor, icon: icon }), failure] })) }));
49
99
  }
50
100
  //# sourceMappingURL=favorite.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"favorite.js","sourceRoot":"","sources":["../../src/default/favorite.tsx"],"names":[],"mappings":";AA4BA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,UAAU,IAAI,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAEpE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA+BvC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;IACvC,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,0EAA0E;IAC1E,4EAA4E;IAC5E,kEAAkE;IAClE,MAAM,IAAI,GAAG,CACX,KAAC,SAAS,IACR,MAAM,EAAE,QAAQ,CAAC,SAAS,KACtB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAC7D,CACH,CAAC;IACF,yEAAyE;IACzE,sDAAsD;IACtD,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IACnD,yEAAyE;IACzE,yCAAyC;IACzC,MAAM,OAAO,GAAG,CACd,KAAC,UAAU,IACT,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,QAAQ,EAC/B,MAAM,EAAE,QAAQ,CAAC,KAAK,EACtB,OAAO,EAAC,QAAQ,GAChB,CACH,CAAC;IACF,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9D,OAAO,CACL,KAAC,iBAAiB,IAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,KAAK,CAAC,MAAM,EACjB,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,SAAS,EAChC,YAAY,EAAE,GAAG,KAAK,CAAC,MAAM,UAAU,YAEtC,CAAC,IAAI,EAAE,EAAE,CAAC,CACT,KAAC,MAAM,IACL,KAAK,EAAC,QAAQ,4BAEV,IAAI,gBACI,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,IAAI,KACN,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAC7D,CACH,GACiB,CACrB,CAAC;IACJ,CAAC;IACD,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,8BACE,KAAC,MAAM,IACL,KAAK,EAAC,QAAQ,KAEV,IAAI,gBACI,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,IAAI,GACV,EACD,OAAO,IACP,CACJ,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.25.5
1
+ # @stapel/listings-react 0.25.7
2
2
 
3
3
  Headless React flow pair for stapel-listings (contract >=0.21 <0.22) — 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.25.5",
4
+ "version": "0.25.7",
5
5
  "backend": {
6
6
  "module": "stapel-listings",
7
7
  "contract": ">=0.21 <0.22"
package/nav-manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@stapel/listings-react",
3
- "version": "0.25.5",
3
+ "version": "0.25.7",
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.25.5",
3
+ "version": "0.25.7",
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": {
@@ -102,13 +102,13 @@
102
102
  "size-limit": "^11.2.0",
103
103
  "typescript": "^5.8.3",
104
104
  "vitest": "^3.2.4",
105
- "@stapel/attributes-react": "^0.16.1",
105
+ "@stapel/attributes-react": "^0.16.2",
106
106
  "@stapel/core": "^0.25.4",
107
107
  "@stapel/currencies-react": "^0.4.0",
108
108
  "@stapel/image": "^0.4.2",
109
109
  "@stapel/showcase": "^0.3.0",
110
- "@stapel/tokens-antd": "^0.17.2",
111
- "@stapel/tokens": "^0.8.0"
110
+ "@stapel/tokens": "^0.8.0",
111
+ "@stapel/tokens-antd": "^0.17.3"
112
112
  },
113
113
  "engines": {
114
114
  "node": ">=22"
@@ -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.25.5",
4
+ "version": "0.25.7",
5
5
  "defined": [],
6
6
  "flows": []
7
7
  }
@@ -284,6 +284,12 @@ export function ListingPhotoStrip(props: {
284
284
  aspectRatio={LISTING_PHOTO_ASPECT}
285
285
  peek={many}
286
286
  dots={many}
287
+ // WHERE THE STRIP ACTUALLY IS, back into the gallery. A finger scrolls
288
+ // this element natively; without this the hook's `active` was stale
289
+ // the moment a swipe settled, and its effect scrolled the strip back
290
+ // to the stale one. See `cardGallery.ts` — the carousel already
291
+ // measures this for its dots, so it costs no second listener.
292
+ onSlideChange={gallery.onSlideChange}
287
293
  data-testid={props.testId}
288
294
  >
289
295
  {images.length === 0
@@ -154,6 +154,19 @@ export interface CardGallery {
154
154
  readonly ref: RefObject<HTMLDivElement | null>;
155
155
  /** The photograph currently on screen. */
156
156
  readonly active: number;
157
+ /**
158
+ * THE STRIP SAYS WHERE IT IS — wire to `<SkinCarousel onSlideChange>`.
159
+ *
160
+ * A native touch scroll (a drag, a fling, a snap settling) moves the strip
161
+ * without this hook being told, and until it was told there were two
162
+ * disagreeing answers to "which photograph is on screen": the browser's,
163
+ * which was true, and `active`, which was stale. The stale one won, through
164
+ * the effect below, by scrolling the strip back — measured on the stand
165
+ * (probe p23): a swipe scrolled to 335.5px and 284ms later the same code
166
+ * scrolled to 0. This is how the browser's answer gets in, and an index
167
+ * that arrives this way is REPORTED, never re-imposed.
168
+ */
169
+ readonly onSlideChange: (index: number) => void;
157
170
  /** True while a pointer is scrubbing — the box publishes it so the strip's
158
171
  * smooth-scroll can be switched off for the duration. */
159
172
  readonly scrubbing: boolean;
@@ -195,14 +208,56 @@ export function useCardGallery(count: number): CardGallery {
195
208
  // The origin of the drag in progress, or `null`. A ref rather than state:
196
209
  // it changes on every move and no render depends on it.
197
210
  const origin = useRef<{ x: number; y: number } | null>(null);
211
+ /**
212
+ * THE INDEX THIS HOOK ASKED FOR, and the whole of the fix.
213
+ *
214
+ * `active` has two sources now: the pair's OWN controls (the hover scrub,
215
+ * the swipe commit, the leave reset), and the strip reporting where a
216
+ * native scroll left it. Only the first kind may drive the strip — the
217
+ * second IS the strip, and scrolling it to where it already is means, in
218
+ * practice, scrolling it back to where it was: the effect below used to run
219
+ * on the render that followed a finger's own scroll and undo it.
220
+ *
221
+ * So a request is recorded here, the effect honours exactly that one, and
222
+ * clears it. An `active` that does not match a standing request came from
223
+ * the browser and is left alone.
224
+ */
225
+ const requested = useRef<number | null>(null);
198
226
  const fine = useFinePointer();
199
227
  const many = count > 1;
200
228
 
201
- // The one place the strip is driven. `active` is the whole state of both
202
- // gestures, so neither handler talks to the DOM.
229
+ /** Move the strip. The only way `active` is set by anything of ours. */
230
+ const request = useCallback(
231
+ (next: number | ((current: number) => number)): void => {
232
+ setActive((current) => {
233
+ const value = typeof next === "function" ? next(current) : next;
234
+ requested.current = value;
235
+ return value;
236
+ });
237
+ },
238
+ []
239
+ );
240
+
241
+ /**
242
+ * Where the STRIP says it is, after a scroll nobody here asked for.
243
+ *
244
+ * Recorded, never acted on: `requested` is untouched, so the effect below
245
+ * sees an `active` it did not ask for and keeps its hands off the DOM. This
246
+ * is also what keeps the dots, the keyboard and this hook agreeing about
247
+ * the photograph on screen after a fling.
248
+ */
249
+ const onSlideChange = useCallback((index: number): void => {
250
+ setActive(index);
251
+ }, []);
252
+
253
+ // The one place the strip is driven — and only for a move this hook asked
254
+ // for. `active` is the whole state of both gestures, so neither handler
255
+ // talks to the DOM.
203
256
  useEffect(() => {
204
257
  const box = ref.current;
205
258
  if (box === null || !many) return;
259
+ if (requested.current !== active) return;
260
+ requested.current = null;
206
261
  showSlide(box, active, scrubbing);
207
262
  }, [active, scrubbing, many]);
208
263
 
@@ -215,7 +270,7 @@ export function useCardGallery(count: number): CardGallery {
215
270
  if (box === null) return;
216
271
  const rect = box.getBoundingClientRect();
217
272
  setScrubbing(true);
218
- setActive(segmentIndex(event.clientX - rect.left, rect.width, count));
273
+ request(segmentIndex(event.clientX - rect.left, rect.width, count));
219
274
  return;
220
275
  }
221
276
  const from = origin.current;
@@ -226,9 +281,9 @@ export function useCardGallery(count: number): CardGallery {
226
281
  // photograph per threshold rather than one per gesture.
227
282
  origin.current = { x: event.clientX, y: event.clientY };
228
283
  setScrubbing(false);
229
- setActive((current) => Math.min(count - 1, Math.max(0, current + step)));
284
+ request((current) => Math.min(count - 1, Math.max(0, current + step)));
230
285
  },
231
- [count, fine, many]
286
+ [count, fine, many, request]
232
287
  );
233
288
 
234
289
  const onPointerDown = useCallback(
@@ -243,19 +298,31 @@ export function useCardGallery(count: number): CardGallery {
243
298
  origin.current = null;
244
299
  }, []);
245
300
 
246
- const onPointerLeave = useCallback((): void => {
247
- origin.current = null;
248
- if (!many) return;
249
- // The card goes back to the photograph it was drawn with. A hover is a
250
- // look, not an edit.
251
- setScrubbing(false);
252
- setActive(0);
253
- }, [many]);
301
+ const onPointerLeave = useCallback(
302
+ (event: ReactPointerEvent<HTMLDivElement>): void => {
303
+ origin.current = null;
304
+ if (!many) return;
305
+ // THE REWIND IS A HOVER RULE, AND ONLY A HOVER RULE.
306
+ //
307
+ // The card goes back to the photograph it was drawn with after a
308
+ // CURSOR passes over it: a hover is a look, not an edit, and a grid of
309
+ // forty tiles must not end up forty different tiles because a cursor
310
+ // crossed them. A finger is the opposite — a swipe is a choice the
311
+ // person made — and `pointerleave` fires for a touch pointer the
312
+ // moment it is lifted, so this arm used to rewind every swipe a
313
+ // fraction of a second after it landed.
314
+ if (event.pointerType !== "mouse") return;
315
+ setScrubbing(false);
316
+ request(0);
317
+ },
318
+ [many, request]
319
+ );
254
320
 
255
321
  return {
256
322
  ref,
257
323
  active,
258
324
  scrubbing,
325
+ onSlideChange,
259
326
  onPointerMove,
260
327
  onPointerDown,
261
328
  onPointerUp: endDrag,
@@ -58,9 +58,17 @@ export interface FavoriteHeartProps {
58
58
  * tree either way — see {@link GateReasonPopover}.
59
59
  */
60
60
  readonly blockedReason?: "text" | "popover";
61
- /** The surface's sign-in door, rendered INSIDE the disclosure. Absent: the
62
- * disclosure holds the reason alone. Only read in the `"popover"` arm; the
63
- * standing arm's door is the container's, as it always was. */
61
+ /**
62
+ * The surface's sign-in door.
63
+ *
64
+ * Given, it is where a VISITOR'S PRESS GOES, in both volumes (D431) — the
65
+ * heart stops being a control that refuses and becomes the way in, `?next=`
66
+ * and all. The `"popover"` arm additionally renders it inside the
67
+ * disclosure, as it always did.
68
+ *
69
+ * Absent, nothing changes: the control stays blocked-but-alive and states
70
+ * its reason, which is all a surface with no sign-in route can offer.
71
+ */
64
72
  readonly signIn?: SignInCta;
65
73
  readonly style?: CSSProperties;
66
74
  }
@@ -82,6 +90,34 @@ export interface FavoriteHeartProps {
82
90
  * was supposed to explain it. Measured on a phone: a signed-out visitor's
83
91
  * heart produced no toast, no reason and no navigation, and on a touch device
84
92
  * there is no hover to fall back on.
93
+ *
94
+ * ── And where there is a DOOR, it is not "blocked" at all (D431) ──────────
95
+ *
96
+ * Live-but-refusing was half the answer. On the deployed phone SERP the
97
+ * visitor's heart still carried `aria-disabled="true"`, a tap still produced
98
+ * nothing at all, and the only explanation was a hover disclosure a finger
99
+ * never triggers — the reason had been pooled into the pane's footnote, and
100
+ * neither this control nor `GatedControl` had ever been handed
101
+ * `onBlockedActivate`, so the gesture fell into the same hole `GatedControl`
102
+ * was written to close.
103
+ *
104
+ * The owner's ruling: **the control is never inert for a visitor.** Where the
105
+ * surface hands in a `signIn` door, a press GOES THROUGH IT, and the heart is
106
+ * therefore not announced as disabled either — a control that acts on press
107
+ * and calls itself unavailable is lying to exactly the people who most depend
108
+ * on the announcement. The reason stays where it was (registered with the
109
+ * pane's scope, wired by `aria-describedby`, hover disclosure and all on the
110
+ * surfaces that have one); it is now a HINT beside a live control rather than
111
+ * the only thing behind a dead one.
112
+ *
113
+ * Where the host hands in NO door there is nothing better for a press to do,
114
+ * and the control keeps the gated shape it has always had — blocked, alive,
115
+ * saying why.
116
+ *
117
+ * The `href` arm renders the heart as an anchor, deliberately: the door is a
118
+ * navigation (`/login?next=…`), and an anchor keeps the `next`, the middle
119
+ * click, and the person who is not running our JavaScript. `aria-pressed`
120
+ * comes off in that arm, because a link is not a toggle.
85
121
  */
86
122
  export function FavoriteHeart(props: FavoriteHeartProps): ReactElement {
87
123
  const t = useT();
@@ -103,7 +139,30 @@ export function FavoriteHeart(props: FavoriteHeartProps): ReactElement {
103
139
  );
104
140
  // The RESOLVED sentence, not the gate's key — `useActionGate` is the one
105
141
  // place a blocked reason becomes words in this fleet.
106
- const reason = useActionGate(favorite.gate).reason;
142
+ const gate = useActionGate(favorite.gate);
143
+ const reason = gate.reason;
144
+ // D431. A blocked gate plus a door the surface handed in: the press is not
145
+ // refused, it is ROUTED — through the door, keeping whatever `?next=` the
146
+ // container put in the href. With no door there is nothing better for the
147
+ // press to do and the gated shape stands, exactly as before.
148
+ const door = props.signIn;
149
+ const opensDoor = gate.disabled && door !== undefined;
150
+ // Exactly one arm, never both — `SignInCta`'s own rule. The `href` arm
151
+ // makes antd render an anchor, which is the honest element for a door.
152
+ const doorPress =
153
+ !opensDoor || door === undefined
154
+ ? {}
155
+ : door.href !== undefined
156
+ ? { href: door.href }
157
+ : { onClick: door.onSignIn };
158
+ // The heart, as the visitor with a door meets it: live, not announced as
159
+ // unavailable, and not pretending to be a toggle when it is a link.
160
+ const asDoor = opensDoor
161
+ ? {
162
+ ...doorPress,
163
+ ...(door?.href !== undefined ? {} : { "aria-pressed": favorite.favorited }),
164
+ }
165
+ : { "aria-pressed": favorite.favorited, onClick: favorite.toggle };
107
166
  // A failed save, stated where the heart is. The rollback already put the
108
167
  // icon back; this says why it went back.
109
168
  const failure = (
@@ -124,15 +183,17 @@ export function FavoriteHeart(props: FavoriteHeartProps): ReactElement {
124
183
  {(bind) => (
125
184
  <Button
126
185
  shape="circle"
127
- aria-disabled
186
+ // Announced unavailable ONLY where a press has nothing to do —
187
+ // see D431 in the header. With a door in hand it is a live
188
+ // control, and the disclosure stays as the hint beside it.
189
+ {...(opensDoor ? {} : { "aria-disabled": true })}
128
190
  {...bind}
129
191
  aria-label={label}
130
- aria-pressed={favorite.favorited}
131
192
  data-testid={props.testId}
132
193
  data-favorited={String(favorite.favorited)}
133
194
  data-analytics="none"
134
195
  data-analytics-reason="business action — host app wraps with its own tracked()"
135
- onClick={favorite.toggle}
196
+ {...asDoor}
136
197
  icon={icon}
137
198
  {...(props.style !== undefined ? { style: props.style } : {})}
138
199
  />
@@ -144,6 +205,12 @@ export function FavoriteHeart(props: FavoriteHeartProps): ReactElement {
144
205
  <GatedControl
145
206
  gate={favorite.gate}
146
207
  testId={`${props.testId}-gate`}
208
+ // With a door in hand the gate no longer REFUSES the person, it
209
+ // annotates the control: the reason is still registered with the pane's
210
+ // scope and still wired by `aria-describedby`, and the press — which is
211
+ // now the way in, not a hole — reaches the handler untouched. Without a
212
+ // door the gate keeps its default "live" refusal.
213
+ {...(opensDoor ? { whenBlocked: "annotate" as const } : {})}
147
214
  {...(props.layout !== undefined ? { layout: props.layout } : {})}
148
215
  {...(props.style !== undefined ? { style: props.style } : {})}
149
216
  >
@@ -154,12 +221,11 @@ export function FavoriteHeart(props: FavoriteHeartProps): ReactElement {
154
221
  // See `<ListingCard>`: the substrate's binding, spread whole.
155
222
  {...bind}
156
223
  aria-label={label}
157
- aria-pressed={favorite.favorited}
158
224
  data-testid={props.testId}
159
225
  data-favorited={String(favorite.favorited)}
160
226
  data-analytics="none"
161
227
  data-analytics-reason="business action — host app wraps with its own tracked()"
162
- onClick={favorite.toggle}
228
+ {...asDoor}
163
229
  icon={icon}
164
230
  />
165
231
  {failure}