@stapel/listings-react 0.26.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.
- package/CHANGELOG.md +129 -0
- package/MODULE.md +3 -1
- package/README.md +102 -8
- package/dist/api/generated/schema.d.ts +20 -10
- package/dist/api/generated/schema.d.ts.map +1 -1
- package/dist/default/ListingActions.d.ts +8 -1
- package/dist/default/ListingActions.d.ts.map +1 -1
- package/dist/default/ListingActions.js +1 -1
- package/dist/default/ListingActions.js.map +1 -1
- package/dist/default/ListingDetailPane.d.ts +148 -9
- package/dist/default/ListingDetailPane.d.ts.map +1 -1
- package/dist/default/ListingDetailPane.js +131 -19
- package/dist/default/ListingDetailPane.js.map +1 -1
- package/dist/default/MyListingsPane.d.ts.map +1 -1
- package/dist/default/MyListingsPane.js +10 -6
- package/dist/default/MyListingsPane.js.map +1 -1
- package/dist/default/ShareAction.d.ts +16 -1
- package/dist/default/ShareAction.d.ts.map +1 -1
- package/dist/default/ShareAction.js +16 -9
- package/dist/default/ShareAction.js.map +1 -1
- package/dist/default/actionRow.d.ts +24 -0
- package/dist/default/actionRow.d.ts.map +1 -1
- package/dist/default/actionRow.js +50 -7
- package/dist/default/actionRow.js.map +1 -1
- package/dist/default/detailGallery.d.ts +77 -0
- package/dist/default/detailGallery.d.ts.map +1 -0
- package/dist/default/detailGallery.js +91 -0
- package/dist/default/detailGallery.js.map +1 -0
- package/dist/default/index.d.ts +4 -2
- package/dist/default/index.d.ts.map +1 -1
- package/dist/default/index.js +5 -1
- package/dist/default/index.js.map +1 -1
- package/dist/default/movableCluster.d.ts +18 -0
- package/dist/default/movableCluster.d.ts.map +1 -0
- package/dist/default/movableCluster.js +110 -0
- package/dist/default/movableCluster.js.map +1 -0
- package/dist/headless/MyListings.d.ts +31 -10
- package/dist/headless/MyListings.d.ts.map +1 -1
- package/dist/headless/MyListings.js +19 -2
- package/dist/headless/MyListings.js.map +1 -1
- package/dist/headless/Share.d.ts +60 -2
- package/dist/headless/Share.d.ts.map +1 -1
- package/dist/headless/Share.js +55 -3
- package/dist/headless/Share.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/model/cardBadges.d.ts +25 -0
- package/dist/model/cardBadges.d.ts.map +1 -1
- package/dist/model/cardBadges.js +41 -5
- package/dist/model/cardBadges.js.map +1 -1
- package/dist/model/draft.d.ts +42 -1
- package/dist/model/draft.d.ts.map +1 -1
- package/dist/model/draft.js +40 -3
- package/dist/model/draft.js.map +1 -1
- package/dist/model/status.d.ts +31 -16
- package/dist/model/status.d.ts.map +1 -1
- package/dist/model/status.js +30 -15
- package/dist/model/status.js.map +1 -1
- package/llms.txt +1 -1
- package/manifest.json +8 -1
- package/nav-manifest.json +1 -1
- package/package.json +3 -3
- package/src/analytics/generated/events.json +1 -1
- package/src/api/generated/schema.ts +20 -10
- package/src/default/ListingActions.tsx +9 -1
- package/src/default/ListingDetailPane.tsx +316 -18
- package/src/default/MyListingsPane.tsx +10 -6
- package/src/default/ShareAction.tsx +36 -10
- package/src/default/actionRow.ts +55 -7
- package/src/default/detailGallery.ts +97 -0
- package/src/default/index.ts +15 -1
- package/src/default/movableCluster.tsx +137 -0
- package/src/headless/MyListings.tsx +52 -12
- package/src/headless/Share.tsx +98 -5
- package/src/index.ts +7 -0
- package/src/model/cardBadges.ts +42 -5
- package/src/model/draft.ts +61 -3
- package/src/model/status.ts +31 -16
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CSSProperties, ReactElement } from "react";
|
|
2
|
-
import type { ShareChannel } from "../headless/Share.js";
|
|
2
|
+
import type { ShareChannel, SharePreference } from "../headless/Share.js";
|
|
3
3
|
export interface ShareActionProps {
|
|
4
4
|
/**
|
|
5
5
|
* The listing's CANONICAL address — the route the container built, absolute
|
|
@@ -26,6 +26,21 @@ export interface ShareActionProps {
|
|
|
26
26
|
* for it.
|
|
27
27
|
*/
|
|
28
28
|
readonly shape?: "default" | "circle";
|
|
29
|
+
/**
|
|
30
|
+
* WHICH ARM THIS SURFACE WANTS — see {@link SharePreference}.
|
|
31
|
+
*
|
|
32
|
+
* Default `"auto"`: the platform sheet where the primary pointer is coarse
|
|
33
|
+
* AND `navigator.share` exists, this pair's menu everywhere else. The
|
|
34
|
+
* default changed in this release, and it changed because of a measurement
|
|
35
|
+
* (§25): desktop Chrome on macOS reports `navigator.share`, so a pair that
|
|
36
|
+
* asked only the capability opened the OS sheet on the desktop and the
|
|
37
|
+
* copy-link menu — three networks and a clipboard row, built for exactly
|
|
38
|
+
* that platform — could not be reached there at all.
|
|
39
|
+
*
|
|
40
|
+
* `"native"` restores the capability-only behaviour by name; `"menu"` pins
|
|
41
|
+
* this pair's menu on every device.
|
|
42
|
+
*/
|
|
43
|
+
readonly prefer?: SharePreference;
|
|
29
44
|
readonly style?: CSSProperties;
|
|
30
45
|
}
|
|
31
46
|
export declare function ShareAction(props: ShareActionProps): ReactElement;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShareAction.d.ts","sourceRoot":"","sources":["../../src/default/ShareAction.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ShareAction.d.ts","sourceRoot":"","sources":["../../src/default/ShareAction.tsx"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAQzD,OAAO,KAAK,EACV,YAAY,EAEZ,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAkB9B,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;eACW;IACX,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;0EACsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IACpD;2EACuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACtC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,YAAY,CAuJjE"}
|
|
@@ -11,16 +11,22 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
11
11
|
*
|
|
12
12
|
* ── One control, two renderings, and the DEVICE picks ─────────────────────
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* On a device driven with a THUMB the press opens the PLATFORM's own sheet:
|
|
15
|
+
* the person's own apps, in their own order, including the ones we have never
|
|
16
|
+
* heard of. A library that drew its own list of four networks on a phone would
|
|
17
|
+
* be offering a worse version of something the operating system already does
|
|
18
|
+
* better.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* is
|
|
20
|
+
* Everywhere else the press opens a small menu: copy the link, and the three
|
|
21
|
+
* networks a Russian-speaking marketplace actually receives traffic from. This
|
|
22
|
+
* is the DESKTOP rendering, not a fallback for old browsers, which is why it
|
|
23
|
+
* is built rather than apologised for.
|
|
24
|
+
*
|
|
25
|
+
* The reading that decides is the primary POINTER and not `navigator.share`
|
|
26
|
+
* alone: desktop Chrome on macOS has the API, so the capability question sent
|
|
27
|
+
* every desktop share to the OS sheet and left this menu unreachable on the
|
|
28
|
+
* platform it was written for (§25). {@link ShareActionProps.prefer} is where
|
|
29
|
+
* a host overrules the reading in either direction.
|
|
24
30
|
*
|
|
25
31
|
* ── The menu is a Popover, and that is an exception with an argument ──────
|
|
26
32
|
*
|
|
@@ -78,6 +84,7 @@ export function ShareAction(props) {
|
|
|
78
84
|
url: props.url,
|
|
79
85
|
title: props.title,
|
|
80
86
|
text: props.text,
|
|
87
|
+
...(props.prefer !== undefined ? { prefer: props.prefer } : {}),
|
|
81
88
|
...(props.onShared !== undefined ? { onShared: props.onShared } : {}),
|
|
82
89
|
});
|
|
83
90
|
const label = t(LISTINGS_I18N_KEYS.shareAction);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShareAction.js","sourceRoot":"","sources":["../../src/default/ShareAction.tsx"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"ShareAction.js","sourceRoot":"","sources":["../../src/default/ShareAction.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEzD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACxC,gRAAgR;AAChR,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,UAAU,IAAI,MAAM,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAMhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,0BAA0B,EAC1B,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,6CAA6C;AAC7C,MAAM,aAAa,GAA2C;IAC5D,QAAQ,EAAE,kBAAkB,CAAC,aAAa;IAC1C,QAAQ,EAAE,kBAAkB,CAAC,aAAa;IAC1C,EAAE,EAAE,kBAAkB,CAAC,OAAO;CAC/B,CAAC;AA8CF,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,gBAAgB,CAAC;IAChD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,QAAQ,CAAC;QACrB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtE,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC;IACxC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAE5B,yEAAyE;IACzE,yDAAyD;IACzD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM;YAAE,MAAM,CAAC,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAC;IACxD,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAExB,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,OAAqB,EAAQ,EAAE;QAC9B,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC,EACD,CAAC,KAAK,CAAC,CACR,CAAC;IAEF;;;;;;;;OAQG;IACH,MAAM,OAAO,GAAG,CACd,KAAC,MAAM,OACD,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAChD,SAAS,EAAE,oBAAoB,gBACnB,KAAK,iBACJ,MAAM,qBAKF,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,oBAClC,MAAM,2BACC,8DAAyD,EAC/E,IAAI,EAAE,KAAC,SAAS,KAAG,KACf,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACzD,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YAEzD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACf,eAAM,SAAS,EAAE,0BAA0B,YAAG,KAAK,GAAQ,CAC5D,GACM,CACV,CAAC;IAEF,MAAM,KAAK,GAAG,CACZ,8BACE,gBAAO,IAAI,EAAE,0BAA0B,EAAE,UAAU,EAAC,SAAS,YAC1D,YAAY,EAAE,GACT,EACP,OAAO,IACP,CACJ,CAAC;IAEF,oEAAoE;IACpE,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAE/B,MAAM,IAAI,GAAG,CACX,MAAC,IAAI,IAAC,QAAQ,QAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,iBAAe,GAAG,MAAM,OAAO,aAC3D,KAAC,MAAM,IACL,IAAI,EAAC,MAAM,EACX,SAAS,EAAE,oBAAoB,EAC/B,IAAI,EAAE,KAAC,QAAQ,KAAG,iBACL,GAAG,MAAM,OAAO,oBACd,MAAM,2BACC,8DAAyD,EAC/E,OAAO,EAAE,KAAK,CAAC,IAAI,YAElB,CAAC,CAAC,kBAAkB,CAAC,SAAS,CAAC,GACzB,EAOR,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CACd,KAAC,UAAU,CAAC,IAAI,IACd,IAAI,EAAC,SAAS,eACJ,QAAQ,iBACL,GAAG,MAAM,SAAS,YAE9B,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,GAClB,CACnB,CAAC,CAAC,CAAC,IAAI,EAKP,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAClB,KAAC,UAAU,IACT,MAAM,EAAE,GAAG,MAAM,aAAa,EAC9B,OAAO,EAAE,CAAC,CAAC,kBAAkB,CAAC,eAAe,CAAC,EAC9C,OAAO,EAAC,QAAQ,GAChB,CACH,CAAC,CAAC,CAAC,IAAI,EAEP,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACzB,YAEE,IAAI,EAAE,IAAI,CAAC,IAAI,EACf,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,qBAAqB,EACzB,SAAS,EAAE,oBAAoB,iBAClB,GAAG,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,oBACzB,MAAM,2BACC,8DAAyD,EAC/E,OAAO,EAAE,GAAG,EAAE;oBACZ,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC1B,CAAC,YAEA,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAZ1B,IAAI,CAAC,OAAO,CAaf,CACL,CAAC,IACG,CACR,CAAC;IAEF,OAAO,CACL,8BACE,gBAAO,IAAI,EAAE,0BAA0B,EAAE,UAAU,EAAC,SAAS,YAC1D,YAAY,EAAE,GACT,EAER,KAAC,OAAO,IACN,OAAO,EAAE,CAAC,OAAO,CAAC,EAClB,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,OAAO,EACrB,OAAO,EAAE,IAAI,iBACA,GAAG,MAAM,UAAU,YAE/B,OAAO,GACA,IACT,CACJ,CAAC;AACJ,CAAC"}
|
|
@@ -54,5 +54,29 @@ export declare const LISTING_ACTION_HIT: number;
|
|
|
54
54
|
* is no such thing as a small touch target.
|
|
55
55
|
*/
|
|
56
56
|
export declare const LISTING_CARD_ACTION_HIT = 36;
|
|
57
|
+
/**
|
|
58
|
+
* HOW MANY TIMES THE HIT-TARGET CLASS IS REPEATED IN ITS OWN SELECTOR — and
|
|
59
|
+
* why a repeat rather than a number typed once (D450).
|
|
60
|
+
*
|
|
61
|
+
* Measured on the live listing page: the heart and the share glyph were
|
|
62
|
+
* **32 × 44**, not 44 × 44. The block axis survived and the inline one did
|
|
63
|
+
* not, because antd's circle shape ships
|
|
64
|
+
*
|
|
65
|
+
* `:where(…).ant-btn.ant-btn-circle.ant-btn{min-width:var(--ant-control-height)}`
|
|
66
|
+
*
|
|
67
|
+
* — three classes, specificity (0,3,0), against this sheet's single class
|
|
68
|
+
* (0,1,0). `:where()` adds nothing, and a media query adds nothing either, so
|
|
69
|
+
* the only thing that decides is the class count: antd's 32 won and there was
|
|
70
|
+
* no viewport at which it did not.
|
|
71
|
+
*
|
|
72
|
+
* A repeated class is the one way to outrank it without `!important`. Four
|
|
73
|
+
* repeats — (0,4,0) — clear antd's three with one to spare, and the selector
|
|
74
|
+
* still matches exactly the same element, so nothing about WHAT the rule
|
|
75
|
+
* applies to changes. `!important` was refused deliberately: a host that
|
|
76
|
+
* genuinely wants a different target must be able to say so with a selector,
|
|
77
|
+
* and this sheet's whole argument (see `movableCluster.tsx`) is that a pair's
|
|
78
|
+
* geometry should never force one on somebody else.
|
|
79
|
+
*/
|
|
80
|
+
export declare const LISTING_ACTION_SPECIFICITY = 4;
|
|
57
81
|
export declare function actionRowCss(): string;
|
|
58
82
|
//# sourceMappingURL=actionRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actionRow.d.ts","sourceRoot":"","sources":["../../src/default/actionRow.ts"],"names":[],"mappings":"AA6BA,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAC5D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,+BAA+B,CAAC;AACtE,2CAA2C;AAC3C,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAC9D;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,gCAAgC,CAAC;AAC3E;wEACwE;AACxE,eAAO,MAAM,0BAA0B,gCAAgC,CAAC;AACxE,uEAAuE;AACvE,eAAO,MAAM,0BAA0B,+BAA+B,CAAC;AAEvE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAiC,CAAC;AAEnE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,wBAAgB,YAAY,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"actionRow.d.ts","sourceRoot":"","sources":["../../src/default/actionRow.ts"],"names":[],"mappings":"AA6BA,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAC5D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,+BAA+B,CAAC;AACtE,2CAA2C;AAC3C,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAC9D;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,gCAAgC,CAAC;AAC3E;wEACwE;AACxE,eAAO,MAAM,0BAA0B,gCAAgC,CAAC;AACxE,uEAAuE;AACvE,eAAO,MAAM,0BAA0B,+BAA+B,CAAC;AAEvE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAiC,CAAC;AAEnE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AA2B5C,wBAAgB,YAAY,IAAI,MAAM,CAwCrC"}
|
|
@@ -82,13 +82,57 @@ export const LISTING_ACTION_HIT = controls["height-phone"];
|
|
|
82
82
|
* is no such thing as a small touch target.
|
|
83
83
|
*/
|
|
84
84
|
export const LISTING_CARD_ACTION_HIT = 36;
|
|
85
|
+
/**
|
|
86
|
+
* HOW MANY TIMES THE HIT-TARGET CLASS IS REPEATED IN ITS OWN SELECTOR — and
|
|
87
|
+
* why a repeat rather than a number typed once (D450).
|
|
88
|
+
*
|
|
89
|
+
* Measured on the live listing page: the heart and the share glyph were
|
|
90
|
+
* **32 × 44**, not 44 × 44. The block axis survived and the inline one did
|
|
91
|
+
* not, because antd's circle shape ships
|
|
92
|
+
*
|
|
93
|
+
* `:where(…).ant-btn.ant-btn-circle.ant-btn{min-width:var(--ant-control-height)}`
|
|
94
|
+
*
|
|
95
|
+
* — three classes, specificity (0,3,0), against this sheet's single class
|
|
96
|
+
* (0,1,0). `:where()` adds nothing, and a media query adds nothing either, so
|
|
97
|
+
* the only thing that decides is the class count: antd's 32 won and there was
|
|
98
|
+
* no viewport at which it did not.
|
|
99
|
+
*
|
|
100
|
+
* A repeated class is the one way to outrank it without `!important`. Four
|
|
101
|
+
* repeats — (0,4,0) — clear antd's three with one to spare, and the selector
|
|
102
|
+
* still matches exactly the same element, so nothing about WHAT the rule
|
|
103
|
+
* applies to changes. `!important` was refused deliberately: a host that
|
|
104
|
+
* genuinely wants a different target must be able to say so with a selector,
|
|
105
|
+
* and this sheet's whole argument (see `movableCluster.tsx`) is that a pair's
|
|
106
|
+
* geometry should never force one on somebody else.
|
|
107
|
+
*/
|
|
108
|
+
export const LISTING_ACTION_SPECIFICITY = 4;
|
|
109
|
+
/** A class name repeated {@link LISTING_ACTION_SPECIFICITY} times — the
|
|
110
|
+
* selector that beats antd's circle. */
|
|
111
|
+
function outranking(className) {
|
|
112
|
+
return `.${className}`.repeat(LISTING_ACTION_SPECIFICITY);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The floor, in BOTH spellings of the same axis.
|
|
116
|
+
*
|
|
117
|
+
* A browser cascades `min-inline-size` and `min-width` together and keeps the
|
|
118
|
+
* winner, so in a browser the logical pair alone would be enough once the
|
|
119
|
+
* selector outranks antd's. The physical pair is written beside it because
|
|
120
|
+
* the declaration being beaten is spelled physically, and an engine that does
|
|
121
|
+
* NOT merge the two names (jsdom, where this rule is asserted) would leave
|
|
122
|
+
* antd's `min-width:32px` standing beside our `min-inline-size:44px` and call
|
|
123
|
+
* that a pass. Two spellings of one number, and the number has one source.
|
|
124
|
+
*/
|
|
125
|
+
function floor(size) {
|
|
126
|
+
const px = `${String(size)}px`;
|
|
127
|
+
return (`min-inline-size:${px};min-block-size:${px};` +
|
|
128
|
+
`min-width:${px};min-height:${px}`);
|
|
129
|
+
}
|
|
85
130
|
export function actionRowCss() {
|
|
86
|
-
const action =
|
|
87
|
-
const cardAction =
|
|
131
|
+
const action = outranking(LISTING_ACTION_CLASS);
|
|
132
|
+
const cardAction = outranking(LISTING_CARD_ACTION_CLASS);
|
|
88
133
|
const row = `.${LISTING_ACTIONS_CLASS}`;
|
|
89
134
|
const over = `.${LISTING_ACTIONS_OVERLAY_CLASS}`;
|
|
90
135
|
const label = `.${LISTING_ACTION_LABEL_CLASS}`;
|
|
91
|
-
const hit = String(LISTING_ACTION_HIT);
|
|
92
136
|
const phone = `(max-width:${String(breakpoints.tablet - 1)}px)`;
|
|
93
137
|
return [
|
|
94
138
|
// The floor. `min-*` rather than `width`/`height`: a share button with a
|
|
@@ -96,14 +140,13 @@ export function actionRowCss() {
|
|
|
96
140
|
// `controlHeight` already reaches the height on a phone — this is the
|
|
97
141
|
// guarantee for every OTHER viewport and for a host-registered button
|
|
98
142
|
// that never read the antd token at all.
|
|
99
|
-
`${action}{
|
|
143
|
+
`${action}{${floor(LISTING_ACTION_HIT)};` +
|
|
100
144
|
`display:inline-flex;align-items:center;justify-content:center}`,
|
|
101
145
|
// A card's control: one tier smaller where there is a cursor, the same
|
|
102
146
|
// 44px where there is a thumb. See LISTING_CARD_ACTION_HIT.
|
|
103
|
-
`${cardAction}{
|
|
104
|
-
`min-block-size:${String(LISTING_CARD_ACTION_HIT)}px;` +
|
|
147
|
+
`${cardAction}{${floor(LISTING_CARD_ACTION_HIT)};` +
|
|
105
148
|
`display:inline-flex;align-items:center;justify-content:center}`,
|
|
106
|
-
`@media ${phone}{${cardAction}{
|
|
149
|
+
`@media ${phone}{${cardAction}{${floor(LISTING_ACTION_HIT)}}}`,
|
|
107
150
|
// The cluster. `align-items:flex-end` so a blocked heart's reason — the
|
|
108
151
|
// one thing here that can be two lines — stacks against the same edge
|
|
109
152
|
// instead of pushing the controls inwards (the arrangement
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actionRow.js","sourceRoot":"","sources":["../../src/default/actionRow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEvE,2EAA2E;AAC3E,MAAM,CAAC,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAC5D;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,4BAA4B,CAAC;AACtE,2CAA2C;AAC3C,MAAM,CAAC,MAAM,qBAAqB,GAAG,wBAAwB,CAAC;AAC9D;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,6BAA6B,CAAC;AAC3E;wEACwE;AACxE,MAAM,CAAC,MAAM,0BAA0B,GAAG,6BAA6B,CAAC;AACxE,uEAAuE;AACvE,MAAM,CAAC,MAAM,0BAA0B,GAAG,4BAA4B,CAAC;AAEvE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAW,QAAQ,CAAC,cAAc,CAAC,CAAC;AAEnE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAE1C,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,
|
|
1
|
+
{"version":3,"file":"actionRow.js","sourceRoot":"","sources":["../../src/default/actionRow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEvE,2EAA2E;AAC3E,MAAM,CAAC,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAC5D;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,4BAA4B,CAAC;AACtE,2CAA2C;AAC3C,MAAM,CAAC,MAAM,qBAAqB,GAAG,wBAAwB,CAAC;AAC9D;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,6BAA6B,CAAC;AAC3E;wEACwE;AACxE,MAAM,CAAC,MAAM,0BAA0B,GAAG,6BAA6B,CAAC;AACxE,uEAAuE;AACvE,MAAM,CAAC,MAAM,0BAA0B,GAAG,4BAA4B,CAAC;AAEvE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAW,QAAQ,CAAC,cAAc,CAAC,CAAC;AAEnE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAE5C;wCACwC;AACxC,SAAS,UAAU,CAAC,SAAiB;IACnC,OAAO,IAAI,SAAS,EAAE,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,KAAK,CAAC,IAAY;IACzB,MAAM,EAAE,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;IAC/B,OAAO,CACL,mBAAmB,EAAE,mBAAmB,EAAE,GAAG;QAC7C,aAAa,EAAE,eAAe,EAAE,EAAE,CACnC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,UAAU,CAAC,yBAAyB,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,IAAI,qBAAqB,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,6BAA6B,EAAE,CAAC;IACjD,MAAM,KAAK,GAAG,IAAI,0BAA0B,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAG,cAAc,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC;IAChE,OAAO;QACL,yEAAyE;QACzE,iEAAiE;QACjE,sEAAsE;QACtE,sEAAsE;QACtE,yCAAyC;QACzC,GAAG,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,GAAG;YACvC,gEAAgE;QAClE,uEAAuE;QACvE,4DAA4D;QAC5D,GAAG,UAAU,IAAI,KAAK,CAAC,uBAAuB,CAAC,GAAG;YAChD,gEAAgE;QAClE,UAAU,KAAK,IAAI,UAAU,IAAI,KAAK,CAAC,kBAAkB,CAAC,IAAI;QAC9D,wEAAwE;QACxE,sEAAsE;QACtE,2DAA2D;QAC3D,sCAAsC;QACtC,GAAG,GAAG,0CAA0C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK;QACvE,0EAA0E;QAC1E,yEAAyE;QACzE,4DAA4D;QAC5D,GAAG,IAAI,wCAAwC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK;YACpE,oBAAoB,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,eAAe;YACrD,6CAA6C;YAC7C,iBAAiB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK;QAC1C,qEAAqE;QACrE,iEAAiE;QACjE,yEAAyE;QACzE,0EAA0E;QAC1E,uEAAuE;QACvE,gDAAgD;QAChD,UAAU,KAAK,IAAI,KAAK,iBAAiB;KAC1C,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HOW THE LISTING PAGE LAYS OUT ITS PHOTOGRAPHS — and why the answer is a
|
|
3
|
+
* class and a prop rather than an inline `display`.
|
|
4
|
+
*
|
|
5
|
+
* ── The defect a container was carrying ───────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* The gallery is an element-width grid (`repeat(auto-fit, minmax(14rem,
|
|
8
|
+
* 1fr))`), which on a 390px phone resolves to ONE column: a listing with three
|
|
9
|
+
* pictures pushes its own title and price nearly three screens down, and the
|
|
10
|
+
* first thing a person sees after tapping a search result is a photograph with
|
|
11
|
+
* nothing beside it. A phone lays photographs out as a snap-scrolling STRIP —
|
|
12
|
+
* one visible with the next peeking, title and price directly under it.
|
|
13
|
+
*
|
|
14
|
+
* That is layout, and layout is the container's to decide. But the pane wrote
|
|
15
|
+
* `display: grid` INLINE, and an inline declaration is beaten by nothing that
|
|
16
|
+
* is not `!important` — so a live storefront carried
|
|
17
|
+
* `[data-testid="listings-detail-gallery"] { display: flex !important }`
|
|
18
|
+
* against a pair's own geometry, named by a test id, to say a thing the pair
|
|
19
|
+
* offered no way to say (a client storefront's own stylesheet, §2).
|
|
20
|
+
*
|
|
21
|
+
* ── The seam ──────────────────────────────────────────────────────────────
|
|
22
|
+
*
|
|
23
|
+
* Two halves, and both matter:
|
|
24
|
+
*
|
|
25
|
+
* 1. `<ListingDetailPane galleryLayout>` — the pane ships BOTH layouts and
|
|
26
|
+
* the host names one, exactly as it already names `layout="split"` and
|
|
27
|
+
* `gutter`. The host is the side that knows the viewport it granted;
|
|
28
|
+
* a media query guessed in a leaf is the thing this package does not do.
|
|
29
|
+
* 2. `display` and the track are no longer inline. They live on this
|
|
30
|
+
* stylesheet, at one class plus one attribute, so a host that wants
|
|
31
|
+
* something neither arm offers can still write CSS for it at its own
|
|
32
|
+
* breakpoints — and needs no `!important` to be heard, only a selector of
|
|
33
|
+
* its own (`[data-testid="listings-detail-gallery"][data-gallery-layout]`
|
|
34
|
+
* ties; add any third condition and it wins).
|
|
35
|
+
*
|
|
36
|
+
* What stays inline is what nobody overrides and what a stylesheet would make
|
|
37
|
+
* worse: the `gap` (the page's own responsive gutter token, D418 — a var, so
|
|
38
|
+
* a resize reflows it) and `position: relative`, which is the containing block
|
|
39
|
+
* the `actionsPlacement="gallery"` overlay is pinned to.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* The narrowest a gallery tile may get before the grid drops a column. A
|
|
43
|
+
* measure rather than a pixel: the tiles then fill whatever the ELEMENT is,
|
|
44
|
+
* which is §83's geometry rule — one photo per row on a phone, three on a
|
|
45
|
+
* desktop pane, and no `width: 320` that is near-full-bleed on one and a
|
|
46
|
+
* postage stamp on the other.
|
|
47
|
+
*
|
|
48
|
+
* Declared here rather than in `<ListingDetailPane>` because this is the file
|
|
49
|
+
* that writes the track it feeds; the pane re-exports it, so the public name
|
|
50
|
+
* is unchanged.
|
|
51
|
+
*/
|
|
52
|
+
export declare const DETAIL_PHOTO_MIN = "14rem";
|
|
53
|
+
/** Which shape the photographs take. See the file header. */
|
|
54
|
+
export type ListingGalleryLayout = "grid" | "strip";
|
|
55
|
+
/** The class the gallery box carries. */
|
|
56
|
+
export declare const LISTINGS_GALLERY_CLASS = "stapel-listings-detail-gallery";
|
|
57
|
+
/** The `href` the hoisted gallery stylesheet is deduplicated by. */
|
|
58
|
+
export declare const LISTINGS_GALLERY_STYLE_HREF = "stapel-listings-detail-gallery";
|
|
59
|
+
/**
|
|
60
|
+
* How much of the strip's width ONE photograph takes.
|
|
61
|
+
*
|
|
62
|
+
* Not 100: the remaining sliver of the next picture is the only thing on a
|
|
63
|
+
* phone that says the strip scrolls at all. Exported so a host laying out
|
|
64
|
+
* beside it measures against the same number instead of guessing it back out
|
|
65
|
+
* of a screenshot.
|
|
66
|
+
*/
|
|
67
|
+
export declare const LISTINGS_GALLERY_STRIP_BASIS = "86%";
|
|
68
|
+
/**
|
|
69
|
+
* The gallery's layout rules, for the hoisted `<style>`.
|
|
70
|
+
*
|
|
71
|
+
* The strip's child rule (`> *`) is the reason this is a stylesheet and not
|
|
72
|
+
* two more inline properties: an inline style cannot reach a child, and the
|
|
73
|
+
* flex basis is what makes the strip a strip rather than a row of squeezed
|
|
74
|
+
* photographs.
|
|
75
|
+
*/
|
|
76
|
+
export declare function detailGalleryCss(): string;
|
|
77
|
+
//# sourceMappingURL=detailGallery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detailGallery.d.ts","sourceRoot":"","sources":["../../src/default/detailGallery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,UAAU,CAAC;AAExC,6DAA6D;AAC7D,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpD,yCAAyC;AACzC,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AAEvE,oEAAoE;AACpE,eAAO,MAAM,2BAA2B,mCAAmC,CAAC;AAE5E;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,QAAQ,CAAC;AAElD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAgBzC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HOW THE LISTING PAGE LAYS OUT ITS PHOTOGRAPHS — and why the answer is a
|
|
3
|
+
* class and a prop rather than an inline `display`.
|
|
4
|
+
*
|
|
5
|
+
* ── The defect a container was carrying ───────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* The gallery is an element-width grid (`repeat(auto-fit, minmax(14rem,
|
|
8
|
+
* 1fr))`), which on a 390px phone resolves to ONE column: a listing with three
|
|
9
|
+
* pictures pushes its own title and price nearly three screens down, and the
|
|
10
|
+
* first thing a person sees after tapping a search result is a photograph with
|
|
11
|
+
* nothing beside it. A phone lays photographs out as a snap-scrolling STRIP —
|
|
12
|
+
* one visible with the next peeking, title and price directly under it.
|
|
13
|
+
*
|
|
14
|
+
* That is layout, and layout is the container's to decide. But the pane wrote
|
|
15
|
+
* `display: grid` INLINE, and an inline declaration is beaten by nothing that
|
|
16
|
+
* is not `!important` — so a live storefront carried
|
|
17
|
+
* `[data-testid="listings-detail-gallery"] { display: flex !important }`
|
|
18
|
+
* against a pair's own geometry, named by a test id, to say a thing the pair
|
|
19
|
+
* offered no way to say (a client storefront's own stylesheet, §2).
|
|
20
|
+
*
|
|
21
|
+
* ── The seam ──────────────────────────────────────────────────────────────
|
|
22
|
+
*
|
|
23
|
+
* Two halves, and both matter:
|
|
24
|
+
*
|
|
25
|
+
* 1. `<ListingDetailPane galleryLayout>` — the pane ships BOTH layouts and
|
|
26
|
+
* the host names one, exactly as it already names `layout="split"` and
|
|
27
|
+
* `gutter`. The host is the side that knows the viewport it granted;
|
|
28
|
+
* a media query guessed in a leaf is the thing this package does not do.
|
|
29
|
+
* 2. `display` and the track are no longer inline. They live on this
|
|
30
|
+
* stylesheet, at one class plus one attribute, so a host that wants
|
|
31
|
+
* something neither arm offers can still write CSS for it at its own
|
|
32
|
+
* breakpoints — and needs no `!important` to be heard, only a selector of
|
|
33
|
+
* its own (`[data-testid="listings-detail-gallery"][data-gallery-layout]`
|
|
34
|
+
* ties; add any third condition and it wins).
|
|
35
|
+
*
|
|
36
|
+
* What stays inline is what nobody overrides and what a stylesheet would make
|
|
37
|
+
* worse: the `gap` (the page's own responsive gutter token, D418 — a var, so
|
|
38
|
+
* a resize reflows it) and `position: relative`, which is the containing block
|
|
39
|
+
* the `actionsPlacement="gallery"` overlay is pinned to.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* The narrowest a gallery tile may get before the grid drops a column. A
|
|
43
|
+
* measure rather than a pixel: the tiles then fill whatever the ELEMENT is,
|
|
44
|
+
* which is §83's geometry rule — one photo per row on a phone, three on a
|
|
45
|
+
* desktop pane, and no `width: 320` that is near-full-bleed on one and a
|
|
46
|
+
* postage stamp on the other.
|
|
47
|
+
*
|
|
48
|
+
* Declared here rather than in `<ListingDetailPane>` because this is the file
|
|
49
|
+
* that writes the track it feeds; the pane re-exports it, so the public name
|
|
50
|
+
* is unchanged.
|
|
51
|
+
*/
|
|
52
|
+
export const DETAIL_PHOTO_MIN = "14rem";
|
|
53
|
+
/** The class the gallery box carries. */
|
|
54
|
+
export const LISTINGS_GALLERY_CLASS = "stapel-listings-detail-gallery";
|
|
55
|
+
/** The `href` the hoisted gallery stylesheet is deduplicated by. */
|
|
56
|
+
export const LISTINGS_GALLERY_STYLE_HREF = "stapel-listings-detail-gallery";
|
|
57
|
+
/**
|
|
58
|
+
* How much of the strip's width ONE photograph takes.
|
|
59
|
+
*
|
|
60
|
+
* Not 100: the remaining sliver of the next picture is the only thing on a
|
|
61
|
+
* phone that says the strip scrolls at all. Exported so a host laying out
|
|
62
|
+
* beside it measures against the same number instead of guessing it back out
|
|
63
|
+
* of a screenshot.
|
|
64
|
+
*/
|
|
65
|
+
export const LISTINGS_GALLERY_STRIP_BASIS = "86%";
|
|
66
|
+
/**
|
|
67
|
+
* The gallery's layout rules, for the hoisted `<style>`.
|
|
68
|
+
*
|
|
69
|
+
* The strip's child rule (`> *`) is the reason this is a stylesheet and not
|
|
70
|
+
* two more inline properties: an inline style cannot reach a child, and the
|
|
71
|
+
* flex basis is what makes the strip a strip rather than a row of squeezed
|
|
72
|
+
* photographs.
|
|
73
|
+
*/
|
|
74
|
+
export function detailGalleryCss() {
|
|
75
|
+
return `
|
|
76
|
+
.${LISTINGS_GALLERY_CLASS}[data-gallery-layout="grid"] {
|
|
77
|
+
display: grid;
|
|
78
|
+
grid-template-columns: repeat(auto-fit, minmax(${DETAIL_PHOTO_MIN}, 1fr));
|
|
79
|
+
}
|
|
80
|
+
.${LISTINGS_GALLERY_CLASS}[data-gallery-layout="strip"] {
|
|
81
|
+
display: flex;
|
|
82
|
+
overflow-x: auto;
|
|
83
|
+
scroll-snap-type: x mandatory;
|
|
84
|
+
}
|
|
85
|
+
.${LISTINGS_GALLERY_CLASS}[data-gallery-layout="strip"] > * {
|
|
86
|
+
flex: 0 0 ${LISTINGS_GALLERY_STRIP_BASIS};
|
|
87
|
+
scroll-snap-align: start;
|
|
88
|
+
}
|
|
89
|
+
`.trim();
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=detailGallery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detailGallery.js","sourceRoot":"","sources":["../../src/default/detailGallery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAKxC,yCAAyC;AACzC,MAAM,CAAC,MAAM,sBAAsB,GAAG,gCAAgC,CAAC;AAEvE,oEAAoE;AACpE,MAAM,CAAC,MAAM,2BAA2B,GAAG,gCAAgC,CAAC;AAE5E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;GACN,sBAAsB;;mDAE0B,gBAAgB;;GAEhE,sBAAsB;;;;;GAKtB,sBAAsB;cACX,4BAA4B;;;CAGzC,CAAC,IAAI,EAAE,CAAC;AACT,CAAC"}
|
package/dist/default/index.d.ts
CHANGED
|
@@ -42,7 +42,9 @@ export type { ListingFeedCardProps, ListingFeedCardBaseProps, } from "./ListingF
|
|
|
42
42
|
export { FeedGrid, FEED_GRID_COLUMNS } from "./FeedGrid.js";
|
|
43
43
|
export type { FeedGridProps } from "./FeedGrid.js";
|
|
44
44
|
export { ListingDetailPane, DETAIL_MEASURE, DETAIL_SPLIT_MEASURE, DETAIL_SPLIT_ASIDE, DETAIL_PHOTO_MIN, DETAIL_GALLERY_GUTTER, } from "./ListingDetailPane.js";
|
|
45
|
-
export type { ListingDetailPaneProps } from "./ListingDetailPane.js";
|
|
45
|
+
export type { ListingDetailPaneProps, ListingActionsPlacement, } from "./ListingDetailPane.js";
|
|
46
|
+
export { LISTINGS_GALLERY_CLASS, LISTINGS_GALLERY_STYLE_HREF, LISTINGS_GALLERY_STRIP_BASIS, detailGalleryCss, } from "./detailGallery.js";
|
|
47
|
+
export type { ListingGalleryLayout } from "./detailGallery.js";
|
|
46
48
|
export { ListingComposerPage, COMPOSER_DETAILS_PLACEMENT, COMPOSER_MEASURE, composerFieldId, } from "./ListingComposerPage.js";
|
|
47
49
|
export type { ListingComposerPageProps, ComposerCategorySlot, ComposerCurrencySlot, ComposerLocationSlot, ComposerLocationValue, ComposerLocationPickerProps, } from "./ListingComposerPage.js";
|
|
48
50
|
export { MyListingsPane } from "./MyListingsPane.js";
|
|
@@ -63,7 +65,7 @@ export { ListingActions } from "./ListingActions.js";
|
|
|
63
65
|
export type { ListingActionsConfig, ListingActionsProps, } from "./ListingActions.js";
|
|
64
66
|
export { ShareAction } from "./ShareAction.js";
|
|
65
67
|
export type { ShareActionProps } from "./ShareAction.js";
|
|
66
|
-
export { LISTING_ACTIONS_CLASS, LISTING_ACTIONS_OVERLAY_CLASS, LISTING_ACTIONS_STYLE_HREF, LISTING_ACTION_CLASS, LISTING_ACTION_HIT, LISTING_ACTION_LABEL_CLASS, LISTING_CARD_ACTION_CLASS, LISTING_CARD_ACTION_HIT, actionRowCss, } from "./actionRow.js";
|
|
68
|
+
export { LISTING_ACTIONS_CLASS, LISTING_ACTIONS_OVERLAY_CLASS, LISTING_ACTIONS_STYLE_HREF, LISTING_ACTION_CLASS, LISTING_ACTION_HIT, LISTING_ACTION_LABEL_CLASS, LISTING_ACTION_SPECIFICITY, LISTING_CARD_ACTION_CLASS, LISTING_CARD_ACTION_HIT, actionRowCss, } from "./actionRow.js";
|
|
67
69
|
export { NOTICE_SECONDS, useNotice } from "./notice.js";
|
|
68
70
|
export type { Notice } from "./notice.js";
|
|
69
71
|
export { SignInLink } from "./SignInLink.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EACL,WAAW,EAGX,oBAAoB,EACpB,sBAAsB,EAGtB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EACV,oBAAoB,EACpB,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EACV,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC5D,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EACL,iBAAiB,EACjB,cAAc,EAGd,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"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"}
|
package/dist/default/index.js
CHANGED
|
@@ -47,6 +47,10 @@ export { ListingDetailPane, DETAIL_MEASURE,
|
|
|
47
47
|
// The split layout's two geometry constants, for a container laying out
|
|
48
48
|
// against the same tracks (and for a test that measures them).
|
|
49
49
|
DETAIL_SPLIT_MEASURE, DETAIL_SPLIT_ASIDE, DETAIL_PHOTO_MIN, DETAIL_GALLERY_GUTTER, } from "./ListingDetailPane.js";
|
|
50
|
+
// The gallery's two shapes, its class and the stylesheet behind them — a host
|
|
51
|
+
// that wants a third writes CSS against these rather than `!important` over an
|
|
52
|
+
// inline `display`.
|
|
53
|
+
export { LISTINGS_GALLERY_CLASS, LISTINGS_GALLERY_STYLE_HREF, LISTINGS_GALLERY_STRIP_BASIS, detailGalleryCss, } from "./detailGallery.js";
|
|
50
54
|
export { ListingComposerPage, COMPOSER_DETAILS_PLACEMENT, COMPOSER_MEASURE, composerFieldId, } from "./ListingComposerPage.js";
|
|
51
55
|
export { MyListingsPane } from "./MyListingsPane.js";
|
|
52
56
|
export { FavoritesPane, FAVORITES_CARD_MIN } from "./FavoritesPane.js";
|
|
@@ -60,7 +64,7 @@ export { SWIPE_AXIS_RATIO, SWIPE_MIN_PX, cardGalleryCss, hasFinePointer, segment
|
|
|
60
64
|
// ── the reader's two actions: save it, or send it to somebody ───────────────
|
|
61
65
|
export { ListingActions } from "./ListingActions.js";
|
|
62
66
|
export { ShareAction } from "./ShareAction.js";
|
|
63
|
-
export { LISTING_ACTIONS_CLASS, LISTING_ACTIONS_OVERLAY_CLASS, LISTING_ACTIONS_STYLE_HREF, LISTING_ACTION_CLASS, LISTING_ACTION_HIT, LISTING_ACTION_LABEL_CLASS, LISTING_CARD_ACTION_CLASS, LISTING_CARD_ACTION_HIT, actionRowCss, } from "./actionRow.js";
|
|
67
|
+
export { LISTING_ACTIONS_CLASS, LISTING_ACTIONS_OVERLAY_CLASS, LISTING_ACTIONS_STYLE_HREF, LISTING_ACTION_CLASS, LISTING_ACTION_HIT, LISTING_ACTION_LABEL_CLASS, LISTING_ACTION_SPECIFICITY, LISTING_CARD_ACTION_CLASS, LISTING_CARD_ACTION_HIT, actionRowCss, } from "./actionRow.js";
|
|
64
68
|
export { NOTICE_SECONDS, useNotice } from "./notice.js";
|
|
65
69
|
export { SignInLink } from "./SignInLink.js";
|
|
66
70
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EACL,WAAW;AACX,6EAA6E;AAC7E,6EAA6E;AAC7E,oBAAoB,EACpB,sBAAsB;AACtB,0EAA0E;AAC1E,4EAA4E;AAC5E,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAO1B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAMvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAKvD,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE5D,OAAO,EACL,iBAAiB,EACjB,cAAc;AACd,wEAAwE;AACxE,+DAA+D;AAC/D,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EACL,WAAW;AACX,6EAA6E;AAC7E,6EAA6E;AAC7E,oBAAoB,EACpB,sBAAsB;AACtB,0EAA0E;AAC1E,4EAA4E;AAC5E,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAO1B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAMvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAKvD,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE5D,OAAO,EACL,iBAAiB,EACjB,cAAc;AACd,wEAAwE;AACxE,+DAA+D;AAC/D,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAKhC,8EAA8E;AAC9E,+EAA+E;AAC/E,oBAAoB;AACpB,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC3B,4BAA4B,EAC5B,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,gBAAgB,EAChB,eAAe,GAChB,MAAM,0BAA0B,CAAC;AASlC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAOvE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAG3B,gFAAgF;AAChF,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG3D,+EAA+E;AAC/E,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,YAAY,EACZ,SAAS,EACT,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,+EAA+E;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EACL,qBAAqB,EACrB,6BAA6B,EAC7B,0BAA0B,EAC1B,oBAAoB,EACpB,kBAAkB,EAClB,0BAA0B,EAC1B,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from "react";
|
|
2
|
+
export interface MovableCluster {
|
|
3
|
+
/**
|
|
4
|
+
* True when there is a document to portal into. `false` under SSR, where
|
|
5
|
+
* the caller renders the cluster inline at its primary placement instead —
|
|
6
|
+
* a server render has no scrolling and therefore no second placement.
|
|
7
|
+
*/
|
|
8
|
+
readonly portable: boolean;
|
|
9
|
+
/** Render the cluster ONCE, anywhere in the tree. Returns the portal. */
|
|
10
|
+
render(cluster: ReactNode): ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* A mount point. The highest `priority` currently on screen holds the
|
|
13
|
+
* cluster; when it goes, the next one down gets it back.
|
|
14
|
+
*/
|
|
15
|
+
slot(priority: number, name: string): ReactElement;
|
|
16
|
+
}
|
|
17
|
+
export declare function useMovableCluster(enabled: boolean): MovableCluster;
|
|
18
|
+
//# sourceMappingURL=movableCluster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"movableCluster.d.ts","sourceRoot":"","sources":["../../src/default/movableCluster.tsx"],"names":[],"mappings":"AAoDA,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMpE,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IACtC;;;OAGG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAAC;CACpD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CA8DlE"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ONE CLUSTER, TWO PLACES ON THE PAGE — the same instance, moved.
|
|
4
|
+
*
|
|
5
|
+
* ── The ask this answers, and why the obvious shape does not ──────────────
|
|
6
|
+
*
|
|
7
|
+
* A listing page wants the reader's two verbs beside the title AND in the
|
|
8
|
+
* condensed bar that arrives when the title scrolls away (reference §24). A
|
|
9
|
+
* host cannot do that today: `<ListingDetailPane>` mounts its cluster at
|
|
10
|
+
* exactly one `actionsPlacement`, so a container that wants the second one
|
|
11
|
+
* mounts a `<ListingActions>` of its own — which is a SECOND
|
|
12
|
+
* `useFavoriteToggle` on one page, two hearts that agree only after a refetch,
|
|
13
|
+
* and two copies of a control whose geometry this pair exists to rule once.
|
|
14
|
+
*
|
|
15
|
+
* The shapes that do NOT close it, stated because each looks like it would:
|
|
16
|
+
*
|
|
17
|
+
* a render prop handed the cluster ELEMENT
|
|
18
|
+
* A React element is a description, not an instance. Rendered in two
|
|
19
|
+
* places it mounts twice — exactly the defect, now with the pair's name
|
|
20
|
+
* on it.
|
|
21
|
+
*
|
|
22
|
+
* the host moving one element between two parents
|
|
23
|
+
* React unmounts and remounts across a parent change: the hook state
|
|
24
|
+
* goes, the DOM node is a different node, and an optimistic favourite
|
|
25
|
+
* in flight is lost mid-write.
|
|
26
|
+
*
|
|
27
|
+
* two mounts kept in step by lifting the state
|
|
28
|
+
* Two controls, one state — and then the page has two `aria-pressed`
|
|
29
|
+
* hearts, two focus targets and two things for a probe to count.
|
|
30
|
+
*
|
|
31
|
+
* ── What this does ────────────────────────────────────────────────────────
|
|
32
|
+
*
|
|
33
|
+
* The cluster is rendered ONCE, through a portal, into a `<div>` this hook
|
|
34
|
+
* owns. The page draws SLOTS — empty `display: contents` divs — wherever the
|
|
35
|
+
* cluster may sit, and the container element is `appendChild`-ed into whichever
|
|
36
|
+
* slot is currently on screen with the highest priority. Moving a DOM node
|
|
37
|
+
* between parents is not a React tree change, so:
|
|
38
|
+
*
|
|
39
|
+
* - `<ListingActions>` (and everything under it) mounts exactly once;
|
|
40
|
+
* - the favourite button is literally the SAME `HTMLElement` in both
|
|
41
|
+
* placements — `test/detailActionsPlacements.test.tsx` holds the node
|
|
42
|
+
* across the move and asserts identity, which is the only assertion that
|
|
43
|
+
* can tell this apart from a well-behaved remount;
|
|
44
|
+
* - a slot that unmounts hands the cluster back to the next one down,
|
|
45
|
+
* rather than taking it off the page.
|
|
46
|
+
*
|
|
47
|
+
* `display: contents` on both the slot and the container is load-bearing: the
|
|
48
|
+
* cluster must remain a direct flex item of the heading row (it is laid out by
|
|
49
|
+
* `justify="space-between"`) and must stay absolutely positionable against the
|
|
50
|
+
* gallery's containing block in the overlay arm. A wrapper that generated a
|
|
51
|
+
* box would change both.
|
|
52
|
+
*/
|
|
53
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
54
|
+
import { createPortal } from "react-dom";
|
|
55
|
+
/** Neither slot nor container may generate a box — see the file header. */
|
|
56
|
+
const CONTENTS = { display: "contents" };
|
|
57
|
+
export function useMovableCluster(enabled) {
|
|
58
|
+
const container = useRef(null);
|
|
59
|
+
if (enabled && container.current === null && typeof document !== "undefined") {
|
|
60
|
+
const host = document.createElement("div");
|
|
61
|
+
host.style.display = "contents";
|
|
62
|
+
host.setAttribute("data-listings-cluster-portal", "");
|
|
63
|
+
container.current = host;
|
|
64
|
+
}
|
|
65
|
+
const slots = useRef(new Map());
|
|
66
|
+
const refs = useRef(new Map());
|
|
67
|
+
/** Put the container in the highest-priority slot that is on the page. */
|
|
68
|
+
const settle = useCallback(() => {
|
|
69
|
+
const host = container.current;
|
|
70
|
+
if (host === null)
|
|
71
|
+
return;
|
|
72
|
+
let best;
|
|
73
|
+
let bestPriority = -1;
|
|
74
|
+
for (const [priority, node] of slots.current) {
|
|
75
|
+
// `isConnected` matters during a commit that removes a slot: React may
|
|
76
|
+
// hand back a node it is about to drop, and appending into it would take
|
|
77
|
+
// the cluster off the page with it.
|
|
78
|
+
if (node.isConnected && priority > bestPriority) {
|
|
79
|
+
bestPriority = priority;
|
|
80
|
+
best = node;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (best !== undefined && host.parentNode !== best)
|
|
84
|
+
best.appendChild(host);
|
|
85
|
+
}, []);
|
|
86
|
+
// The belt. A slot's ref cleanup is what normally moves the cluster, but a
|
|
87
|
+
// subtree removed around it would leave the container detached with nothing
|
|
88
|
+
// to notice; this runs after every commit and repairs that in one comparison.
|
|
89
|
+
useEffect(settle);
|
|
90
|
+
const refFor = useCallback((priority) => {
|
|
91
|
+
let ref = refs.current.get(priority);
|
|
92
|
+
if (ref === undefined) {
|
|
93
|
+
ref = (node) => {
|
|
94
|
+
if (node === null)
|
|
95
|
+
slots.current.delete(priority);
|
|
96
|
+
else
|
|
97
|
+
slots.current.set(priority, node);
|
|
98
|
+
settle();
|
|
99
|
+
};
|
|
100
|
+
refs.current.set(priority, ref);
|
|
101
|
+
}
|
|
102
|
+
return ref;
|
|
103
|
+
}, [settle]);
|
|
104
|
+
return {
|
|
105
|
+
portable: container.current !== null,
|
|
106
|
+
render: (cluster) => container.current === null ? null : createPortal(cluster, container.current),
|
|
107
|
+
slot: (priority, name) => (_jsx("div", { ref: refFor(priority), style: CONTENTS, "data-listings-cluster-slot": name })),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=movableCluster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"movableCluster.js","sourceRoot":"","sources":["../../src/default/movableCluster.tsx"],"names":[],"mappings":"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"}
|