@stapel/listings-react 0.27.0 → 0.28.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 (52) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +56 -7
  3. package/dist/default/ListingActions.d.ts +8 -1
  4. package/dist/default/ListingActions.d.ts.map +1 -1
  5. package/dist/default/ListingActions.js +1 -1
  6. package/dist/default/ListingActions.js.map +1 -1
  7. package/dist/default/ListingDetailPane.d.ts +52 -1
  8. package/dist/default/ListingDetailPane.d.ts.map +1 -1
  9. package/dist/default/ListingDetailPane.js +4 -2
  10. package/dist/default/ListingDetailPane.js.map +1 -1
  11. package/dist/default/ShareAction.d.ts +16 -1
  12. package/dist/default/ShareAction.d.ts.map +1 -1
  13. package/dist/default/ShareAction.js +16 -9
  14. package/dist/default/ShareAction.js.map +1 -1
  15. package/dist/default/actionRow.d.ts +24 -0
  16. package/dist/default/actionRow.d.ts.map +1 -1
  17. package/dist/default/actionRow.js +50 -7
  18. package/dist/default/actionRow.js.map +1 -1
  19. package/dist/default/index.d.ts +1 -1
  20. package/dist/default/index.d.ts.map +1 -1
  21. package/dist/default/index.js +1 -1
  22. package/dist/default/index.js.map +1 -1
  23. package/dist/headless/Share.d.ts +60 -2
  24. package/dist/headless/Share.d.ts.map +1 -1
  25. package/dist/headless/Share.js +55 -3
  26. package/dist/headless/Share.js.map +1 -1
  27. package/dist/index.d.ts +4 -4
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -2
  30. package/dist/index.js.map +1 -1
  31. package/dist/model/cardBadges.d.ts +25 -0
  32. package/dist/model/cardBadges.d.ts.map +1 -1
  33. package/dist/model/cardBadges.js +41 -5
  34. package/dist/model/cardBadges.js.map +1 -1
  35. package/dist/model/draft.d.ts +42 -1
  36. package/dist/model/draft.d.ts.map +1 -1
  37. package/dist/model/draft.js +40 -3
  38. package/dist/model/draft.js.map +1 -1
  39. package/llms.txt +1 -1
  40. package/manifest.json +8 -1
  41. package/nav-manifest.json +1 -1
  42. package/package.json +1 -1
  43. package/src/analytics/generated/events.json +1 -1
  44. package/src/default/ListingActions.tsx +9 -1
  45. package/src/default/ListingDetailPane.tsx +57 -3
  46. package/src/default/ShareAction.tsx +36 -10
  47. package/src/default/actionRow.ts +55 -7
  48. package/src/default/index.ts +1 -0
  49. package/src/headless/Share.tsx +98 -5
  50. package/src/index.ts +7 -0
  51. package/src/model/cardBadges.ts +42 -5
  52. package/src/model/draft.ts +61 -3
@@ -98,6 +98,26 @@ export declare function draftValuesFromWire(draft: ListingDraft, options?: Empty
98
98
  * is the one place the published values live.
99
99
  */
100
100
  export declare function draftValuesFromDetail(detail: ListingDetail, featuresDto: FeaturesDto, options?: EmptyDraftOptions): ListingDraftValues;
101
+ /** One field of the `save-draft` body, by the name the wire uses. */
102
+ export type ListingDraftField = keyof ListingDraftPatch;
103
+ /** Options for {@link draftPatchFromValues}. */
104
+ export interface DraftPatchOptions {
105
+ /**
106
+ * NAME THE FIELDS THIS SAVE IS WRITING, and the body carries no others.
107
+ *
108
+ * `save-draft` REPLACES every field in the body — it does not merge — so a
109
+ * body is not "the values I have", it is "the fields I am claiming". A save
110
+ * fired by one control (a photo settling, a blurred title) previously
111
+ * claimed all fourteen, which is only harmless while every one of them is
112
+ * loaded and true.
113
+ *
114
+ * Given, only these keys are spelled; the omission rules below still apply
115
+ * inside the selection, so naming `features_draft` without a schema still
116
+ * writes nothing. Absent, the whole body is sent, which is right for the
117
+ * composer's own save — it holds every value on the form.
118
+ */
119
+ readonly fields?: readonly ListingDraftField[];
120
+ }
101
121
  /**
102
122
  * The composer's values → the `save-draft` body.
103
123
  *
@@ -108,8 +128,29 @@ export declare function draftValuesFromDetail(detail: ListingDetail, featuresDto
108
128
  * (`error.400.listing_feature_not_allowed`), which is why
109
129
  * {@link retainKnownFeatureValues} prunes on the way in rather than letting
110
130
  * the server explain it.
131
+ *
132
+ * ── NO SCHEMA IS NOT AN EMPTY ANSWER SHEET ───────────────────────────────
133
+ *
134
+ * An absent or empty `features` used to produce `features_draft: {}`, and
135
+ * `save-draft` REPLACES that map rather than merging into it — so a save that
136
+ * left before the category's schema arrived DELETED every characteristic the
137
+ * row was holding, while the form on screen still showed them. Measured by a
138
+ * live container over two cold loads of one draft: a reopen's own
139
+ * settled-photo save fires in the first commit, before the row's category has
140
+ * even been adopted, and the row alternated between the draft's answers and
141
+ * none of them (a client storefront's README, "Named gaps").
142
+ *
143
+ * `{}` is a claim — "this listing has no characteristics" — and a caller with
144
+ * no schema is in no position to make it. So the key is OMITTED entirely
145
+ * whenever there is no schema to tag values with, and `save-draft` then
146
+ * leaves the stored map exactly as it was. A category that genuinely declares
147
+ * no features writes nothing either, which is the same answer arrived at
148
+ * honestly: there is nothing to say and the row already says it.
149
+ *
150
+ * {@link DraftPatchOptions.fields} is the general form of the same rule — a
151
+ * save that names what it is writing cannot erase what it is not.
111
152
  */
112
- export declare function draftPatchFromValues(values: ListingDraftValues, features: readonly FeatureDef[]): ListingDraftPatch;
153
+ export declare function draftPatchFromValues(values: ListingDraftValues, features: readonly FeatureDef[] | undefined, options?: DraftPatchOptions): ListingDraftPatch;
113
154
  /**
114
155
  * The body for CREATING a draft: the category if there is one, `{}` if there
115
156
  * is not.
@@ -1 +1 @@
1
- {"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/model/draft.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/model/draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AAGzB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;mEAC+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,cAAc,EAAE,eAM5B,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;iFAE6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;iDACiD;AACjD,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,iBAAsB,GAC9B,kBAAkB,CAepB;AAMD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,iBAAsB,GAC9B,kBAAkB,CAwBpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,iBAAsB,GAC9B,kBAAkB,CA4BpB;AA4BD,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG,MAAM,iBAAiB,CAAC;AAExD,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,EAC3C,OAAO,GAAE,iBAAsB,GAC9B,iBAAiB,CA0CnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAItE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,EAAE,SAAS,UAAU,EAAE,GAC9B,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAOnC;AAED;;;eAGe;AACf,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,EAAE,SAAS,UAAU,EAAE,GAC9B,SAAS,MAAM,EAAE,CAKnB"}
@@ -126,9 +126,30 @@ function toWireFeatures(features, values) {
126
126
  * (`error.400.listing_feature_not_allowed`), which is why
127
127
  * {@link retainKnownFeatureValues} prunes on the way in rather than letting
128
128
  * the server explain it.
129
+ *
130
+ * ── NO SCHEMA IS NOT AN EMPTY ANSWER SHEET ───────────────────────────────
131
+ *
132
+ * An absent or empty `features` used to produce `features_draft: {}`, and
133
+ * `save-draft` REPLACES that map rather than merging into it — so a save that
134
+ * left before the category's schema arrived DELETED every characteristic the
135
+ * row was holding, while the form on screen still showed them. Measured by a
136
+ * live container over two cold loads of one draft: a reopen's own
137
+ * settled-photo save fires in the first commit, before the row's category has
138
+ * even been adopted, and the row alternated between the draft's answers and
139
+ * none of them (a client storefront's README, "Named gaps").
140
+ *
141
+ * `{}` is a claim — "this listing has no characteristics" — and a caller with
142
+ * no schema is in no position to make it. So the key is OMITTED entirely
143
+ * whenever there is no schema to tag values with, and `save-draft` then
144
+ * leaves the stored map exactly as it was. A category that genuinely declares
145
+ * no features writes nothing either, which is the same answer arrived at
146
+ * honestly: there is nothing to say and the row already says it.
147
+ *
148
+ * {@link DraftPatchOptions.fields} is the general form of the same rule — a
149
+ * save that names what it is writing cannot erase what it is not.
129
150
  */
130
- export function draftPatchFromValues(values, features) {
131
- return {
151
+ export function draftPatchFromValues(values, features, options = {}) {
152
+ const full = {
132
153
  // Omitted while unchosen rather than sent as `""`: a draft is allowed to
133
154
  // have no category (0.21.4), and `""` is not "no category" on the wire —
134
155
  // it is an empty id the serializer refuses. The category is written by
@@ -147,7 +168,11 @@ export function draftPatchFromValues(values, features) {
147
168
  // sent here is discarded. Sending one would be a claim the wire ignores.
148
169
  lat_draft: values.location.lat,
149
170
  lon_draft: values.location.lon,
150
- features_draft: toWireFeatures(features, values.features),
171
+ // Omitted, not emptied, when there is no schema to tag with — see the
172
+ // doc above. `{}` is a claim about the listing; silence is not.
173
+ ...(features !== undefined && features.length > 0
174
+ ? { features_draft: toWireFeatures(features, values.features) }
175
+ : {}),
151
176
  countable: values.countable,
152
177
  // The pair mirrors the model's cross-field rule rather than sending a
153
178
  // contradiction: a service carries no quantity, and `validate_countable
@@ -155,6 +180,18 @@ export function draftPatchFromValues(values, features) {
155
180
  stock_quantity: values.countable ? values.stockQuantity : null,
156
181
  auto_republish: values.autoRepublish,
157
182
  };
183
+ const named = options.fields;
184
+ if (named === undefined)
185
+ return full;
186
+ // A selection, not a second body: the field's VALUE is still whatever the
187
+ // rules above produced, so a named field the rules omit stays omitted.
188
+ const wanted = new Set(named);
189
+ const out = {};
190
+ for (const [key, value] of Object.entries(full)) {
191
+ if (wanted.has(key))
192
+ out[key] = value;
193
+ }
194
+ return out;
158
195
  }
159
196
  /**
160
197
  * The body for CREATING a draft: the category if there is one, `{}` if there
@@ -1 +1 @@
1
- {"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/model/draft.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/model/draft.ts"],"names":[],"mappings":"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"}
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @stapel/listings-react 0.27.0
1
+ # @stapel/listings-react 0.28.0
2
2
 
3
3
  Headless React flow pair for stapel-listings (contract >=0.22 <0.23) — 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.27.0",
4
+ "version": "0.28.0",
5
5
  "backend": {
6
6
  "module": "stapel-listings",
7
7
  "contract": ">=0.22 <0.23"
@@ -1266,6 +1266,7 @@
1266
1266
  "NO_ADDRESS",
1267
1267
  "OWNER_TRANSITIONS",
1268
1268
  "PRICE_FIELD",
1269
+ "SHARE_COARSE_MEDIA",
1269
1270
  "SHARE_COPIED_MS",
1270
1271
  "SHARE_NETWORKS",
1271
1272
  "Share",
@@ -1276,6 +1277,7 @@
1276
1277
  "browserAddress",
1277
1278
  "canDelete",
1278
1279
  "canTransition",
1280
+ "captionName",
1279
1281
  "cardBadgeText",
1280
1282
  "cardBadgeTexts",
1281
1283
  "countedTabOf",
@@ -1301,6 +1303,7 @@
1301
1303
  "flowEndpoints",
1302
1304
  "formatSpecValue",
1303
1305
  "hasCardBadgeContract",
1306
+ "hasCoarsePointer",
1304
1307
  "hasNativeShare",
1305
1308
  "isBatchValid",
1306
1309
  "isListingViewed",
@@ -1321,6 +1324,7 @@
1321
1324
  "navEntries",
1322
1325
  "ownerMoves",
1323
1326
  "pageKey",
1327
+ "preferNativeShare",
1324
1328
  "publishRefusal",
1325
1329
  "registerListingsI18n",
1326
1330
  "resolveShareUrl",
@@ -1373,6 +1377,7 @@
1373
1377
  "ComposeStage",
1374
1378
  "CreateListingsRuntimeOptions",
1375
1379
  "DeleteResponse",
1380
+ "DraftPatchOptions",
1376
1381
  "EmptyDraftOptions",
1377
1382
  "FavoriteInput",
1378
1383
  "FavoriteToggleBag",
@@ -1391,6 +1396,7 @@
1391
1396
  "ListingDetailBag",
1392
1397
  "ListingDetailData",
1393
1398
  "ListingDraft",
1399
+ "ListingDraftField",
1394
1400
  "ListingDraftLimits",
1395
1401
  "ListingDraftPatch",
1396
1402
  "ListingDraftValues",
@@ -1444,6 +1450,7 @@
1444
1450
  "ShareChannel",
1445
1451
  "ShareLink",
1446
1452
  "ShareNetwork",
1453
+ "SharePreference",
1447
1454
  "ShareTarget",
1448
1455
  "UseFavoritesOptions",
1449
1456
  "UseListingActionsOptions",
package/nav-manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@stapel/listings-react",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
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.27.0",
3
+ "version": "0.28.0",
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": {
@@ -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.27.0",
4
+ "version": "0.28.0",
5
5
  "defined": [],
6
6
  "flows": []
7
7
  }
@@ -46,7 +46,7 @@ import { Flex, Typography } from "antd";
46
46
  import type { SignInCta } from "@stapel/core";
47
47
  import { useT } from "@stapel/core";
48
48
  import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
49
- import type { ShareChannel } from "../headless/Share.js";
49
+ import type { ShareChannel, SharePreference } from "../headless/Share.js";
50
50
  import { FavoriteHeart } from "./favorite.js";
51
51
  import { ShareAction } from "./ShareAction.js";
52
52
  import {
@@ -92,6 +92,13 @@ export interface ListingActionsProps {
92
92
  readonly shareTitle?: string;
93
93
  /** A line under it in the sheet — the price, typically. */
94
94
  readonly shareText?: string;
95
+ /**
96
+ * Which arm the share control uses — handed straight to
97
+ * `<ShareAction prefer>`, which is where the reading and its measurement
98
+ * are written down. Default `"auto"`: the platform sheet on a thumb, this
99
+ * pair's menu on a mouse.
100
+ */
101
+ readonly sharePrefer?: SharePreference;
95
102
  readonly onShared?: (channel: ShareChannel) => void;
96
103
  /** The container's sign-in door for the heart. */
97
104
  readonly signIn?: SignInCta;
@@ -187,6 +194,7 @@ export function ListingActions(props: ListingActionsProps): ReactElement | null
187
194
  {...(props.shareUrl !== undefined ? { url: props.shareUrl } : {})}
188
195
  {...(props.shareTitle !== undefined ? { title: props.shareTitle } : {})}
189
196
  {...(props.shareText !== undefined ? { text: props.shareText } : {})}
197
+ {...(props.sharePrefer !== undefined ? { prefer: props.sharePrefer } : {})}
190
198
  {...(props.onShared !== undefined ? { onShared: props.onShared } : {})}
191
199
  />
192
200
  ) : null}
@@ -102,7 +102,7 @@ import { useListingActions } from "../headless/ListingActions.js";
102
102
  import { asFeatureDaoList, featureValuesForDisplay } from "../model/features.js";
103
103
  import { formatSpecValue } from "../model/featureText.js";
104
104
  import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
105
- import type { ShareChannel } from "../headless/Share.js";
105
+ import type { ShareChannel, SharePreference } from "../headless/Share.js";
106
106
  import { GateReasonPopover } from "./GateReasonPopover.js";
107
107
  import { ListingActions } from "./ListingActions.js";
108
108
  import type { ListingActionsConfig } from "./ListingActions.js";
@@ -271,6 +271,14 @@ export interface ListingDetailPaneProps
271
271
  * somebody sends to a friend. See `useShare`.
272
272
  */
273
273
  readonly shareUrl?: string;
274
+ /**
275
+ * Which arm the share control uses — handed to `<ShareAction prefer>`
276
+ * through the cluster. Default `"auto"`: the platform sheet where the
277
+ * primary pointer is coarse, this pair's menu on a mouse. See
278
+ * `SharePreference` for the measurement that made the pointer part of the
279
+ * question (§25).
280
+ */
281
+ readonly sharePrefer?: SharePreference;
274
282
  /** Analytics: which channel a completed share went through. */
275
283
  readonly onShared?: (channel: ShareChannel) => void;
276
284
  /**
@@ -412,6 +420,49 @@ export interface ListingDetailPaneProps
412
420
  * Default `3`, byte-compatible for every existing mount.
413
421
  */
414
422
  readonly headingLevel?: 1 | 2 | 3;
423
+ /**
424
+ * WHERE THE BUY COLUMN'S STICKY TOP EDGE IS — the offset of whatever chrome
425
+ * is pinned above this page (`layout="split"` only; the one-column arm has
426
+ * no sticky column).
427
+ *
428
+ * ```tsx
429
+ * // the height <PublicShell> publishes, read rather than restated
430
+ * <ListingDetailPane layout="split" buyTop="var(--stapel-header-height)" />
431
+ * ```
432
+ *
433
+ * The column is `position: sticky; top: 16px` written INLINE, and an inline
434
+ * declaration is beaten by nothing short of `!important` — so a host with a
435
+ * pinned header had no way to say "start below it". Measured on the stand
436
+ * (D456): the storefront's header is sticky and 64px tall, and at any scroll
437
+ * depth the top of the buy column — the price's own first twenty pixels —
438
+ * sat UNDER it. Same seam and same argument as `<SearchPage railTop>`, which
439
+ * this prop is deliberately spelled after.
440
+ *
441
+ * A number is pixels; a string is taken as written (a `var()`, a `calc()`,
442
+ * `"4rem"`). Default `spacing[4]` — 16px, exactly where the column has
443
+ * always started — so an existing mount is byte-compatible.
444
+ */
445
+ readonly buyTop?: number | string;
446
+ /**
447
+ * HOW WIDE THE PANE MAY GET — the `max-width` it writes on its own root.
448
+ *
449
+ * Default is the constant for the arm on screen: {@link DETAIL_MEASURE}
450
+ * (60rem) in `"column"`, {@link DETAIL_SPLIT_MEASURE} (75rem) in `"split"`,
451
+ * so no existing mount changes shape. Anything CSS `max-width` takes is
452
+ * accepted (`"80rem"`, `1280`, `"100%"`).
453
+ *
454
+ * `"none"` removes the cap, and it is the answer for a pane mounted inside
455
+ * a page frame that already decided the measure. That case is not
456
+ * hypothetical: measured on the stand at 1440 (D457), the listing page's
457
+ * content ended at x=1224 with 216px of empty gutter beside it while every
458
+ * other page of the same site ran to the frame's edge — a second, lower cap
459
+ * inside a container that already had one. The cap is written INLINE, so
460
+ * the container could not outrank it without `!important` and reached for
461
+ * `min-inline-size: 100%` instead (a minimum beats a maximum by the sizing
462
+ * rules); this prop is that workaround's replacement, and the same seam
463
+ * `<CategoryPage measure>` already offers.
464
+ */
465
+ readonly measure?: number | string;
415
466
  readonly footer?: ReactNode;
416
467
  }
417
468
 
@@ -592,7 +643,7 @@ export function ListingDetailPane(props: ListingDetailPaneProps): ReactElement {
592
643
  <SkinTheme
593
644
  surface="base"
594
645
  style={{
595
- maxWidth: split ? DETAIL_SPLIT_MEASURE : DETAIL_MEASURE,
646
+ maxWidth: props.measure ?? (split ? DETAIL_SPLIT_MEASURE : DETAIL_MEASURE),
596
647
  // See `gutter`: a frame that already placed the page edge does not get
597
648
  // a second one stacked inside it.
598
649
  padding: props.gutter === "shell" ? 0 : spacing[4],
@@ -770,6 +821,9 @@ export function ListingDetailPane(props: ListingDetailPaneProps): ReactElement {
770
821
  {...(listing.title !== undefined && listing.title !== null
771
822
  ? { shareTitle: listing.title }
772
823
  : {})}
824
+ {...(props.sharePrefer !== undefined
825
+ ? { sharePrefer: props.sharePrefer }
826
+ : {})}
773
827
  {...(props.onShared !== undefined ? { onShared: props.onShared } : {})}
774
828
  />
775
829
  );
@@ -1171,7 +1225,7 @@ export function ListingDetailPane(props: ListingDetailPaneProps): ReactElement {
1171
1225
  data-testid="listings-detail-buy-column"
1172
1226
  style={{
1173
1227
  position: "sticky",
1174
- top: spacing[4],
1228
+ top: props.buyTop ?? spacing[4],
1175
1229
  alignSelf: "start",
1176
1230
  }}
1177
1231
  >
@@ -10,16 +10,22 @@
10
10
  *
11
11
  * ── One control, two renderings, and the DEVICE picks ─────────────────────
12
12
  *
13
- * Where `navigator.share` exists which is every phone and almost no desktop
14
- * the press opens the PLATFORM's own sheet: the person's own apps, in their
15
- * own order, including the ones we have never heard of. A library that drew
16
- * its own list of four networks on a phone would be offering a worse version
17
- * of something the operating system already does better.
13
+ * On a device driven with a THUMB the press opens the PLATFORM's own sheet:
14
+ * the person's own apps, in their own order, including the ones we have never
15
+ * heard of. A library that drew its own list of four networks on a phone would
16
+ * be offering a worse version of something the operating system already does
17
+ * better.
18
18
  *
19
- * Where it does not, the press opens a small menu: copy the link, and the
20
- * three networks a Russian-speaking marketplace actually receives traffic
21
- * from. This is the DESKTOP rendering, not a fallback for old browsers, which
22
- * is why it is built rather than apologised for.
19
+ * Everywhere else the press opens a small menu: copy the link, and the three
20
+ * networks a Russian-speaking marketplace actually receives traffic from. This
21
+ * is the DESKTOP rendering, not a fallback for old browsers, which is why it
22
+ * is built rather than apologised for.
23
+ *
24
+ * The reading that decides is the primary POINTER and not `navigator.share`
25
+ * alone: desktop Chrome on macOS has the API, so the capability question sent
26
+ * every desktop share to the OS sheet and left this menu unreachable on the
27
+ * platform it was written for (§25). {@link ShareActionProps.prefer} is where
28
+ * a host overrules the reading in either direction.
23
29
  *
24
30
  * ── The menu is a Popover, and that is an exception with an argument ──────
25
31
  *
@@ -60,7 +66,11 @@ import { SkinButton as Button, ErrorAlert } from "@stapel/tokens-antd/skin";
60
66
  import { useT } from "@stapel/core";
61
67
  import { spacing } from "@stapel/tokens";
62
68
  import { useShare } from "../headless/Share.js";
63
- import type { ShareChannel, ShareNetwork } from "../headless/Share.js";
69
+ import type {
70
+ ShareChannel,
71
+ ShareNetwork,
72
+ SharePreference,
73
+ } from "../headless/Share.js";
64
74
  import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
65
75
  import {
66
76
  LISTING_ACTIONS_STYLE_HREF,
@@ -104,6 +114,21 @@ export interface ShareActionProps {
104
114
  * for it.
105
115
  */
106
116
  readonly shape?: "default" | "circle";
117
+ /**
118
+ * WHICH ARM THIS SURFACE WANTS — see {@link SharePreference}.
119
+ *
120
+ * Default `"auto"`: the platform sheet where the primary pointer is coarse
121
+ * AND `navigator.share` exists, this pair's menu everywhere else. The
122
+ * default changed in this release, and it changed because of a measurement
123
+ * (§25): desktop Chrome on macOS reports `navigator.share`, so a pair that
124
+ * asked only the capability opened the OS sheet on the desktop and the
125
+ * copy-link menu — three networks and a clipboard row, built for exactly
126
+ * that platform — could not be reached there at all.
127
+ *
128
+ * `"native"` restores the capability-only behaviour by name; `"menu"` pins
129
+ * this pair's menu on every device.
130
+ */
131
+ readonly prefer?: SharePreference;
107
132
  readonly style?: CSSProperties;
108
133
  }
109
134
 
@@ -115,6 +140,7 @@ export function ShareAction(props: ShareActionProps): ReactElement {
115
140
  url: props.url,
116
141
  title: props.title,
117
142
  text: props.text,
143
+ ...(props.prefer !== undefined ? { prefer: props.prefer } : {}),
118
144
  ...(props.onShared !== undefined ? { onShared: props.onShared } : {}),
119
145
  });
120
146
  const label = t(LISTINGS_I18N_KEYS.shareAction);
@@ -86,13 +86,62 @@ export const LISTING_ACTION_HIT: number = controls["height-phone"];
86
86
  */
87
87
  export const LISTING_CARD_ACTION_HIT = 36;
88
88
 
89
+ /**
90
+ * HOW MANY TIMES THE HIT-TARGET CLASS IS REPEATED IN ITS OWN SELECTOR — and
91
+ * why a repeat rather than a number typed once (D450).
92
+ *
93
+ * Measured on the live listing page: the heart and the share glyph were
94
+ * **32 × 44**, not 44 × 44. The block axis survived and the inline one did
95
+ * not, because antd's circle shape ships
96
+ *
97
+ * `:where(…).ant-btn.ant-btn-circle.ant-btn{min-width:var(--ant-control-height)}`
98
+ *
99
+ * — three classes, specificity (0,3,0), against this sheet's single class
100
+ * (0,1,0). `:where()` adds nothing, and a media query adds nothing either, so
101
+ * the only thing that decides is the class count: antd's 32 won and there was
102
+ * no viewport at which it did not.
103
+ *
104
+ * A repeated class is the one way to outrank it without `!important`. Four
105
+ * repeats — (0,4,0) — clear antd's three with one to spare, and the selector
106
+ * still matches exactly the same element, so nothing about WHAT the rule
107
+ * applies to changes. `!important` was refused deliberately: a host that
108
+ * genuinely wants a different target must be able to say so with a selector,
109
+ * and this sheet's whole argument (see `movableCluster.tsx`) is that a pair's
110
+ * geometry should never force one on somebody else.
111
+ */
112
+ export const LISTING_ACTION_SPECIFICITY = 4;
113
+
114
+ /** A class name repeated {@link LISTING_ACTION_SPECIFICITY} times — the
115
+ * selector that beats antd's circle. */
116
+ function outranking(className: string): string {
117
+ return `.${className}`.repeat(LISTING_ACTION_SPECIFICITY);
118
+ }
119
+
120
+ /**
121
+ * The floor, in BOTH spellings of the same axis.
122
+ *
123
+ * A browser cascades `min-inline-size` and `min-width` together and keeps the
124
+ * winner, so in a browser the logical pair alone would be enough once the
125
+ * selector outranks antd's. The physical pair is written beside it because
126
+ * the declaration being beaten is spelled physically, and an engine that does
127
+ * NOT merge the two names (jsdom, where this rule is asserted) would leave
128
+ * antd's `min-width:32px` standing beside our `min-inline-size:44px` and call
129
+ * that a pass. Two spellings of one number, and the number has one source.
130
+ */
131
+ function floor(size: number): string {
132
+ const px = `${String(size)}px`;
133
+ return (
134
+ `min-inline-size:${px};min-block-size:${px};` +
135
+ `min-width:${px};min-height:${px}`
136
+ );
137
+ }
138
+
89
139
  export function actionRowCss(): string {
90
- const action = `.${LISTING_ACTION_CLASS}`;
91
- const cardAction = `.${LISTING_CARD_ACTION_CLASS}`;
140
+ const action = outranking(LISTING_ACTION_CLASS);
141
+ const cardAction = outranking(LISTING_CARD_ACTION_CLASS);
92
142
  const row = `.${LISTING_ACTIONS_CLASS}`;
93
143
  const over = `.${LISTING_ACTIONS_OVERLAY_CLASS}`;
94
144
  const label = `.${LISTING_ACTION_LABEL_CLASS}`;
95
- const hit = String(LISTING_ACTION_HIT);
96
145
  const phone = `(max-width:${String(breakpoints.tablet - 1)}px)`;
97
146
  return [
98
147
  // The floor. `min-*` rather than `width`/`height`: a share button with a
@@ -100,14 +149,13 @@ export function actionRowCss(): string {
100
149
  // `controlHeight` already reaches the height on a phone — this is the
101
150
  // guarantee for every OTHER viewport and for a host-registered button
102
151
  // that never read the antd token at all.
103
- `${action}{min-inline-size:${hit}px;min-block-size:${hit}px;` +
152
+ `${action}{${floor(LISTING_ACTION_HIT)};` +
104
153
  `display:inline-flex;align-items:center;justify-content:center}`,
105
154
  // A card's control: one tier smaller where there is a cursor, the same
106
155
  // 44px where there is a thumb. See LISTING_CARD_ACTION_HIT.
107
- `${cardAction}{min-inline-size:${String(LISTING_CARD_ACTION_HIT)}px;` +
108
- `min-block-size:${String(LISTING_CARD_ACTION_HIT)}px;` +
156
+ `${cardAction}{${floor(LISTING_CARD_ACTION_HIT)};` +
109
157
  `display:inline-flex;align-items:center;justify-content:center}`,
110
- `@media ${phone}{${cardAction}{min-inline-size:${hit}px;min-block-size:${hit}px}}`,
158
+ `@media ${phone}{${cardAction}{${floor(LISTING_ACTION_HIT)}}}`,
111
159
  // The cluster. `align-items:flex-end` so a blocked heart's reason — the
112
160
  // one thing here that can be two lines — stacks against the same edge
113
161
  // instead of pushing the controls inwards (the arrangement
@@ -155,6 +155,7 @@ export {
155
155
  LISTING_ACTION_CLASS,
156
156
  LISTING_ACTION_HIT,
157
157
  LISTING_ACTION_LABEL_CLASS,
158
+ LISTING_ACTION_SPECIFICITY,
158
159
  LISTING_CARD_ACTION_CLASS,
159
160
  LISTING_CARD_ACTION_HIT,
160
161
  actionRowCss,