@stapel/listings-react 0.30.3 → 0.30.4
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 +28 -0
- package/dist/default/ListingCard.d.ts.map +1 -1
- package/dist/default/ListingCard.js +3 -2
- package/dist/default/ListingCard.js.map +1 -1
- package/dist/default/ListingFeedCard.d.ts +14 -12
- package/dist/default/ListingFeedCard.d.ts.map +1 -1
- package/dist/default/ListingFeedCard.js +17 -19
- package/dist/default/ListingFeedCard.js.map +1 -1
- package/dist/default/titleClamp.d.ts +47 -0
- package/dist/default/titleClamp.d.ts.map +1 -0
- package/dist/default/titleClamp.js +51 -0
- package/dist/default/titleClamp.js.map +1 -0
- package/llms.txt +1 -1
- package/manifest.json +1 -1
- package/nav-manifest.json +1 -1
- package/package.json +1 -1
- package/src/analytics/generated/events.json +1 -1
- package/src/default/ListingCard.tsx +19 -1
- package/src/default/ListingFeedCard.tsx +26 -25
- package/src/default/titleClamp.ts +56 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @stapel/listings-react
|
|
2
2
|
|
|
3
|
+
## 0.30.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4b2323b: A card title is two lines, and the cut is never inside a word
|
|
8
|
+
|
|
9
|
+
`ListingFeedCard` (the tile) clamped its title to two lines. `ListingCard` (the
|
|
10
|
+
grid card) used antd's `<Typography.Text ellipsis>`, which is ONE line and puts
|
|
11
|
+
the cut wherever the line happens to end — in the middle of a word for any real
|
|
12
|
+
title. On a live storefront's home grid that drew job titles cut mid-word,
|
|
13
|
+
while the tile beside it wrapped the same string over two lines.
|
|
14
|
+
|
|
15
|
+
Two cards, two different answers to one question, and the wrong one was on the
|
|
16
|
+
busiest surface. The answer now lives once, in `titleClamp.ts`, and both cards
|
|
17
|
+
read it: `-webkit-line-clamp: 2` (so the browser puts its own ellipsis at the
|
|
18
|
+
end of the second line) plus `overflow-wrap: normal` (so a long word moves to
|
|
19
|
+
the next line whole instead of being broken to fill the current one — the same
|
|
20
|
+
defect one level down).
|
|
21
|
+
|
|
22
|
+
It stays a hoisted stylesheet rather than a style object, because
|
|
23
|
+
`-webkit-line-clamp` and `-webkit-box-orient` are dropped silently by every
|
|
24
|
+
style serializer outside a browser: a card written that way clamps in Chrome,
|
|
25
|
+
does not clamp in a test, and nothing says which.
|
|
26
|
+
|
|
27
|
+
`FEED_TITLE_CLASS`, `FEED_CARD_STYLE_HREF` and `feedCardCss` remain as aliases.
|
|
28
|
+
Both cards now emit the sheet under the same `href`, so React hoists one copy
|
|
29
|
+
for a page showing both card shapes.
|
|
30
|
+
|
|
3
31
|
## 0.30.3
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListingCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyGG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIpE,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"ListingCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,7 @@ import { CardBadges, CardSpecLine } from "./CardBadges.js";
|
|
|
15
15
|
import { SignInLink } from "./SignInLink.js";
|
|
16
16
|
import { PHOTO_LINK_CLASS, ListingPhotoStrip } from "./ListingPhoto.js";
|
|
17
17
|
import { ListingPrice } from "./ListingPrice.js";
|
|
18
|
+
import { TITLE_CLAMP_CLASS, TITLE_CLAMP_STYLE_HREF, titleClampCss, } from "./titleClamp.js";
|
|
18
19
|
/** The class the whole-card target carries, for {@link cardTargetCss}. */
|
|
19
20
|
export const CARD_TARGET_CLASS = "stapel-listing-card-target";
|
|
20
21
|
/** The class the card's OUTER box carries — the size container the row layout
|
|
@@ -298,9 +299,9 @@ export function ListingCard(props) {
|
|
|
298
299
|
* all of them absent. That is why the photo and the price carry the layout:
|
|
299
300
|
* they are the two fields a result always has.
|
|
300
301
|
*/
|
|
301
|
-
const content = (_jsxs(Flex, { vertical: true, gap: spacing[1], style: { minWidth: 0, padding: token.paddingSM }, children: [props.badge, _jsx(Typography.Text, { strong: true, "data-testid": "listings-card-price", children: _jsx(ListingPrice, { amount: listing.price, ...(listing.currency !== undefined ? { currency: listing.currency } : {}) }) }), _jsx(Typography.Text, {
|
|
302
|
+
const content = (_jsxs(Flex, { vertical: true, gap: spacing[1], style: { minWidth: 0, padding: token.paddingSM }, children: [props.badge, _jsx(Typography.Text, { strong: true, "data-testid": "listings-card-price", children: _jsx(ListingPrice, { amount: listing.price, ...(listing.currency !== undefined ? { currency: listing.currency } : {}) }) }), _jsx(Typography.Text, { className: TITLE_CLAMP_CLASS, "data-testid": "listings-card-title", children: title }), _jsx(CardSpecLine, { rows: titleDaos, copy: copy, testId: "listings-card-specs" }), _jsx(CardBadges, { rows: badgeDaos, copy: copy, variant: "badges" }), listing.location_label !== undefined &&
|
|
302
303
|
listing.location_label.length > 0 ? (_jsx(Typography.Text, { type: "secondary", ellipsis: { tooltip: listing.location_label }, "data-testid": "listings-card-location", children: listing.location_label })) : null] }));
|
|
303
|
-
return (_jsxs(SkinTheme, { surface: "bare", ...(props.mode !== undefined ? { mode: props.mode } : {}), children: [_jsx("style", { href: CARD_TARGET_STYLE_HREF, precedence: "default", children: cardTargetCss() }), _jsx(Card, { size: "small", "data-testid": "listings-card", "data-listing-id": listing.id, ...(status !== undefined
|
|
304
|
+
return (_jsxs(SkinTheme, { surface: "bare", ...(props.mode !== undefined ? { mode: props.mode } : {}), children: [_jsx("style", { href: CARD_TARGET_STYLE_HREF, precedence: "default", children: cardTargetCss() }), _jsx("style", { href: TITLE_CLAMP_STYLE_HREF, precedence: "default", children: titleClampCss() }), _jsx(Card, { size: "small", "data-testid": "listings-card", "data-listing-id": listing.id, ...(status !== undefined
|
|
304
305
|
? { "data-listing-status": status.status }
|
|
305
306
|
: {}),
|
|
306
307
|
// `viewed` only when the server actually said so: absent and `null`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListingCard.js","sourceRoot":"","sources":["../../src/default/ListingCard.tsx"],"names":[],"mappings":";AA2GA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EACL,qBAAqB,EACrB,6BAA6B,EAC7B,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"ListingCard.js","sourceRoot":"","sources":["../../src/default/ListingCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -59,21 +59,23 @@ import type { SignInCta } from "@stapel/core";
|
|
|
59
59
|
import type { ListingCard as ListingCardData } from "../api/types.js";
|
|
60
60
|
import type { ListingCardOpenProps } from "./ListingCard.js";
|
|
61
61
|
import type { ThemeModeProp } from "./types.js";
|
|
62
|
-
/** The class the clamped title carries, for {@link feedCardCss}. */
|
|
63
|
-
export declare const FEED_TITLE_CLASS = "stapel-listing-feed-title";
|
|
64
|
-
/** The `href` the hoisted feed stylesheet is deduplicated by. */
|
|
65
|
-
export declare const FEED_CARD_STYLE_HREF = "stapel-listings-feed-card";
|
|
66
62
|
/**
|
|
67
|
-
* The clamp
|
|
63
|
+
* The title clamp is `titleClamp.ts`'s, not this file's.
|
|
68
64
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
65
|
+
* It lived here, and the grid card (`ListingCard`) answered the same question
|
|
66
|
+
* with antd's one-line `ellipsis` — which is how the busiest surface on the
|
|
67
|
+
* storefront ended up cutting job titles mid-word while the tile beside it
|
|
68
|
+
* wrapped them over two lines. One answer, one module, both cards.
|
|
69
|
+
*
|
|
70
|
+
* These three names stay as aliases because this file's tests and a reader
|
|
71
|
+
* looking for "the feed card's clamp" both expect them here.
|
|
72
|
+
*
|
|
73
|
+
* They are re-export bindings rather than `const` copies: under
|
|
74
|
+
* `--isolatedDeclarations` the declaration emitter types each export from that
|
|
75
|
+
* export alone, and `const FEED_TITLE_CLASS = TITLE_CLAMP_CLASS` gives it
|
|
76
|
+
* nothing to go on. An aliased re-export carries the original's type with it.
|
|
75
77
|
*/
|
|
76
|
-
export
|
|
78
|
+
export { TITLE_CLAMP_CLASS as FEED_TITLE_CLASS, TITLE_CLAMP_STYLE_HREF as FEED_CARD_STYLE_HREF, titleClampCss as feedCardCss, } from "./titleClamp.js";
|
|
77
79
|
export interface ListingFeedCardBaseProps extends ThemeModeProp {
|
|
78
80
|
readonly listing: ListingCardData;
|
|
79
81
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListingFeedCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingFeedCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAatE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"ListingFeedCard.d.ts","sourceRoot":"","sources":["../../src/default/ListingFeedCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAatE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAQ7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,iBAAiB,IAAI,gBAAgB,EACrC,sBAAsB,IAAI,oBAAoB,EAC9C,aAAa,IAAI,WAAW,GAC7B,MAAM,iBAAiB,CAAC;AAuBzB,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC7D,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC;IAClC;6CACyC;IACzC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;gDAC4C;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,GAAG,oBAAoB,CAAC;AAEnF,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,CAyGzE"}
|
|
@@ -12,26 +12,24 @@ import { LISTING_CARD_ACTION_CLASS } from "./actionRow.js";
|
|
|
12
12
|
import { CARD_TARGET_STYLE_HREF, CARD_VIEWED_CLASS, CardTarget, cardTargetCss, } from "./ListingCard.js";
|
|
13
13
|
import { ListingPhoto } from "./ListingPhoto.js";
|
|
14
14
|
import { ListingPrice } from "./ListingPrice.js";
|
|
15
|
-
|
|
16
|
-
const TITLE_LINES = 2;
|
|
17
|
-
/** The class the clamped title carries, for {@link feedCardCss}. */
|
|
18
|
-
export const FEED_TITLE_CLASS = "stapel-listing-feed-title";
|
|
19
|
-
/** The `href` the hoisted feed stylesheet is deduplicated by. */
|
|
20
|
-
export const FEED_CARD_STYLE_HREF = "stapel-listings-feed-card";
|
|
15
|
+
import { TITLE_CLAMP_CLASS, TITLE_CLAMP_STYLE_HREF, titleClampCss, } from "./titleClamp.js";
|
|
21
16
|
/**
|
|
22
|
-
* The clamp
|
|
17
|
+
* The title clamp is `titleClamp.ts`'s, not this file's.
|
|
23
18
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
19
|
+
* It lived here, and the grid card (`ListingCard`) answered the same question
|
|
20
|
+
* with antd's one-line `ellipsis` — which is how the busiest surface on the
|
|
21
|
+
* storefront ended up cutting job titles mid-word while the tile beside it
|
|
22
|
+
* wrapped them over two lines. One answer, one module, both cards.
|
|
23
|
+
*
|
|
24
|
+
* These three names stay as aliases because this file's tests and a reader
|
|
25
|
+
* looking for "the feed card's clamp" both expect them here.
|
|
26
|
+
*
|
|
27
|
+
* They are re-export bindings rather than `const` copies: under
|
|
28
|
+
* `--isolatedDeclarations` the declaration emitter types each export from that
|
|
29
|
+
* export alone, and `const FEED_TITLE_CLASS = TITLE_CLAMP_CLASS` gives it
|
|
30
|
+
* nothing to go on. An aliased re-export carries the original's type with it.
|
|
30
31
|
*/
|
|
31
|
-
export
|
|
32
|
-
return (`.${FEED_TITLE_CLASS}{display:-webkit-box;-webkit-box-orient:vertical;` +
|
|
33
|
-
`-webkit-line-clamp:${String(TITLE_LINES)};overflow:hidden}`);
|
|
34
|
-
}
|
|
32
|
+
export { TITLE_CLAMP_CLASS as FEED_TITLE_CLASS, TITLE_CLAMP_STYLE_HREF as FEED_CARD_STYLE_HREF, titleClampCss as feedCardCss, } from "./titleClamp.js";
|
|
35
33
|
/** The tile. `position: relative` is what the heart and the badge overlay are
|
|
36
34
|
* pinned to; `minWidth: 0` keeps a long word inside its grid track. */
|
|
37
35
|
const TILE = { position: "relative", minWidth: 0 };
|
|
@@ -60,11 +58,11 @@ export function ListingFeedCard(props) {
|
|
|
60
58
|
const targetLabel = title.length > 0 ? title : t(LISTINGS_I18N_KEYS.cardUntitled);
|
|
61
59
|
// Already seen — `false` for every response that carries no such field.
|
|
62
60
|
const viewed = isListingViewed(listing);
|
|
63
|
-
return (_jsxs(SkinTheme, { surface: "bare", ...(props.mode !== undefined ? { mode: props.mode } : {}), children: [_jsx("style", { href: CARD_TARGET_STYLE_HREF, precedence: "default", children: cardTargetCss() }), _jsx("style", { href:
|
|
61
|
+
return (_jsxs(SkinTheme, { surface: "bare", ...(props.mode !== undefined ? { mode: props.mode } : {}), children: [_jsx("style", { href: CARD_TARGET_STYLE_HREF, precedence: "default", children: cardTargetCss() }), _jsx("style", { href: TITLE_CLAMP_STYLE_HREF, precedence: "default", children: titleClampCss() }), _jsxs("div", { style: TILE, "data-testid": "listings-feed-card", "data-listing-id": listing.id, ...(status !== undefined
|
|
64
62
|
? { "data-listing-status": status.status }
|
|
65
63
|
: {}), ...(viewed
|
|
66
64
|
? { className: CARD_VIEWED_CLASS, "data-listing-viewed": "true" }
|
|
67
|
-
: {}), children: [_jsx(CardTarget, { ...openProps(props), listingId: listing.id, label: targetLabel, testId: "listings-feed-open", bodyTestId: "listings-feed-body", children: _jsxs(Flex, { vertical: true, gap: spacing[1], children: [_jsx(ListingPhoto, { imageRef: listing.images?.[0], alt: title.length > 0 ? title : String(listing.id), style: { borderRadius: radii.lg } }), _jsx(Typography.Text, { className:
|
|
65
|
+
: {}), children: [_jsx(CardTarget, { ...openProps(props), listingId: listing.id, label: targetLabel, testId: "listings-feed-open", bodyTestId: "listings-feed-body", children: _jsxs(Flex, { vertical: true, gap: spacing[1], children: [_jsx(ListingPhoto, { imageRef: listing.images?.[0], alt: title.length > 0 ? title : String(listing.id), style: { borderRadius: radii.lg } }), _jsx(Typography.Text, { className: TITLE_CLAMP_CLASS, "data-testid": "listings-feed-title", children: title }), _jsx(Typography.Text, { strong: true, "data-testid": "listings-feed-price", children: _jsx(ListingPrice, { amount: listing.price, ...(listing.currency !== undefined
|
|
68
66
|
? { currency: listing.currency }
|
|
69
67
|
: {}) }) }), listing.location_label !== undefined &&
|
|
70
68
|
listing.location_label.length > 0 ? (_jsx(Typography.Text, { type: "secondary", ellipsis: true, "data-testid": "listings-feed-location", children: listing.location_label })) : null] }) }), props.badgeOverlay !== undefined && (_jsx("div", { style: BADGE, "data-testid": "listings-feed-badge", children: props.badgeOverlay })), props.showFavorite === false ? null : (_jsx(FavoriteHeart, { listingId: listing.id, favorited: listing.is_favorited, testId: "listings-feed-favorite", className: LISTING_CARD_ACTION_CLASS, blockedReason: props.blockedReason ?? "popover", ...(props.signIn !== undefined ? { signIn: props.signIn } : {}), style: HEART }))] })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListingFeedCard.js","sourceRoot":"","sources":["../../src/default/ListingFeedCard.tsx"],"names":[],"mappings":";AAyDA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,UAAU,EACV,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"ListingFeedCard.js","sourceRoot":"","sources":["../../src/default/ListingFeedCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `titleClamp` — how many lines of a listing title a card shows, and where the
|
|
3
|
+
* cut goes when there are more.
|
|
4
|
+
*
|
|
5
|
+
* ── the defect ────────────────────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* The tile card (`ListingFeedCard`) clamped its title to two lines. The grid
|
|
8
|
+
* card (`ListingCard`) used antd's `<Typography.Text ellipsis>`, which is ONE
|
|
9
|
+
* line, and one line of a job or a part title lands in the middle of a word:
|
|
10
|
+
* the live storefront's home grid drew titles cut as "Electrician - construc…"
|
|
11
|
+
* and "Administrator - restaur…". The reference wraps the same titles over two
|
|
12
|
+
* lines and cuts, if it has to, at the end of the second.
|
|
13
|
+
*
|
|
14
|
+
* Two cards, two different answers to the same question, and the wrong one was
|
|
15
|
+
* on the busiest surface. So the answer lives here once and both cards read it.
|
|
16
|
+
*
|
|
17
|
+
* ── the rule ──────────────────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* display: -webkit-box + -webkit-box-orient: vertical + -webkit-line-clamp
|
|
20
|
+
* two lines, with the browser's own ellipsis at the end of the second.
|
|
21
|
+
* overflow-wrap: normal
|
|
22
|
+
* a long word moves to the next line whole rather than being broken across
|
|
23
|
+
* the wrap. Without it a narrow grid track will split a word to fill a
|
|
24
|
+
* line, which is the same defect one level down.
|
|
25
|
+
*
|
|
26
|
+
* ── why a hoisted stylesheet and not an inline style ──────────────────────
|
|
27
|
+
*
|
|
28
|
+
* `-webkit-line-clamp` and `-webkit-box-orient` do not survive the trip
|
|
29
|
+
* through a React style object: the serializer and every DOM implementation
|
|
30
|
+
* that is not a browser drop them silently, so a card written that way clamps
|
|
31
|
+
* in Chrome, does not clamp in a test, and nothing anywhere says which. A
|
|
32
|
+
* hoisted sheet keeps one copy for the document and makes the rule something a
|
|
33
|
+
* test can read.
|
|
34
|
+
*
|
|
35
|
+
* Both cards emit the sheet under the SAME `href`, which is how React 19
|
|
36
|
+
* deduplicates it — a second href carrying the same rule would hoist a second
|
|
37
|
+
* copy for every page showing both card shapes.
|
|
38
|
+
*/
|
|
39
|
+
/** Lines of title a card draws before it clips. */
|
|
40
|
+
export declare const TITLE_CLAMP_LINES = 2;
|
|
41
|
+
/** The class a clamped title carries. */
|
|
42
|
+
export declare const TITLE_CLAMP_CLASS = "stapel-listing-title-clamp";
|
|
43
|
+
/** The `href` the hoisted stylesheet is deduplicated by. */
|
|
44
|
+
export declare const TITLE_CLAMP_STYLE_HREF = "stapel-listings-title-clamp";
|
|
45
|
+
/** The rule itself, as text, so a test can assert it. */
|
|
46
|
+
export declare function titleClampCss(): string;
|
|
47
|
+
//# sourceMappingURL=titleClamp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"titleClamp.d.ts","sourceRoot":"","sources":["../../src/default/titleClamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,mDAAmD;AACnD,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,yCAAyC;AACzC,eAAO,MAAM,iBAAiB,+BAA+B,CAAC;AAE9D,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,gCAAgC,CAAC;AAEpE,yDAAyD;AACzD,wBAAgB,aAAa,IAAI,MAAM,CAMtC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `titleClamp` — how many lines of a listing title a card shows, and where the
|
|
3
|
+
* cut goes when there are more.
|
|
4
|
+
*
|
|
5
|
+
* ── the defect ────────────────────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* The tile card (`ListingFeedCard`) clamped its title to two lines. The grid
|
|
8
|
+
* card (`ListingCard`) used antd's `<Typography.Text ellipsis>`, which is ONE
|
|
9
|
+
* line, and one line of a job or a part title lands in the middle of a word:
|
|
10
|
+
* the live storefront's home grid drew titles cut as "Electrician - construc…"
|
|
11
|
+
* and "Administrator - restaur…". The reference wraps the same titles over two
|
|
12
|
+
* lines and cuts, if it has to, at the end of the second.
|
|
13
|
+
*
|
|
14
|
+
* Two cards, two different answers to the same question, and the wrong one was
|
|
15
|
+
* on the busiest surface. So the answer lives here once and both cards read it.
|
|
16
|
+
*
|
|
17
|
+
* ── the rule ──────────────────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* display: -webkit-box + -webkit-box-orient: vertical + -webkit-line-clamp
|
|
20
|
+
* two lines, with the browser's own ellipsis at the end of the second.
|
|
21
|
+
* overflow-wrap: normal
|
|
22
|
+
* a long word moves to the next line whole rather than being broken across
|
|
23
|
+
* the wrap. Without it a narrow grid track will split a word to fill a
|
|
24
|
+
* line, which is the same defect one level down.
|
|
25
|
+
*
|
|
26
|
+
* ── why a hoisted stylesheet and not an inline style ──────────────────────
|
|
27
|
+
*
|
|
28
|
+
* `-webkit-line-clamp` and `-webkit-box-orient` do not survive the trip
|
|
29
|
+
* through a React style object: the serializer and every DOM implementation
|
|
30
|
+
* that is not a browser drop them silently, so a card written that way clamps
|
|
31
|
+
* in Chrome, does not clamp in a test, and nothing anywhere says which. A
|
|
32
|
+
* hoisted sheet keeps one copy for the document and makes the rule something a
|
|
33
|
+
* test can read.
|
|
34
|
+
*
|
|
35
|
+
* Both cards emit the sheet under the SAME `href`, which is how React 19
|
|
36
|
+
* deduplicates it — a second href carrying the same rule would hoist a second
|
|
37
|
+
* copy for every page showing both card shapes.
|
|
38
|
+
*/
|
|
39
|
+
/** Lines of title a card draws before it clips. */
|
|
40
|
+
export const TITLE_CLAMP_LINES = 2;
|
|
41
|
+
/** The class a clamped title carries. */
|
|
42
|
+
export const TITLE_CLAMP_CLASS = "stapel-listing-title-clamp";
|
|
43
|
+
/** The `href` the hoisted stylesheet is deduplicated by. */
|
|
44
|
+
export const TITLE_CLAMP_STYLE_HREF = "stapel-listings-title-clamp";
|
|
45
|
+
/** The rule itself, as text, so a test can assert it. */
|
|
46
|
+
export function titleClampCss() {
|
|
47
|
+
return (`.${TITLE_CLAMP_CLASS}{display:-webkit-box;-webkit-box-orient:vertical;` +
|
|
48
|
+
`-webkit-line-clamp:${String(TITLE_CLAMP_LINES)};overflow:hidden;` +
|
|
49
|
+
`overflow-wrap:normal}`);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=titleClamp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"titleClamp.js","sourceRoot":"","sources":["../../src/default/titleClamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,mDAAmD;AACnD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAEnC,yCAAyC;AACzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,4BAA4B,CAAC;AAE9D,4DAA4D;AAC5D,MAAM,CAAC,MAAM,sBAAsB,GAAG,6BAA6B,CAAC;AAEpE,yDAAyD;AACzD,MAAM,UAAU,aAAa;IAC3B,OAAO,CACL,IAAI,iBAAiB,mDAAmD;QACxE,sBAAsB,MAAM,CAAC,iBAAiB,CAAC,mBAAmB;QAClE,uBAAuB,CACxB,CAAC;AACJ,CAAC"}
|
package/llms.txt
CHANGED
package/manifest.json
CHANGED
package/nav-manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stapel/listings-react",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.4",
|
|
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": {
|
|
@@ -128,6 +128,11 @@ import { CardBadges, CardSpecLine } from "./CardBadges.js";
|
|
|
128
128
|
import { SignInLink } from "./SignInLink.js";
|
|
129
129
|
import { PHOTO_LINK_CLASS, ListingPhotoStrip } from "./ListingPhoto.js";
|
|
130
130
|
import { ListingPrice } from "./ListingPrice.js";
|
|
131
|
+
import {
|
|
132
|
+
TITLE_CLAMP_CLASS,
|
|
133
|
+
TITLE_CLAMP_STYLE_HREF,
|
|
134
|
+
titleClampCss,
|
|
135
|
+
} from "./titleClamp.js";
|
|
131
136
|
import type { CategoryFeaturesProp, ThemeModeProp } from "./types.js";
|
|
132
137
|
|
|
133
138
|
/**
|
|
@@ -589,7 +594,17 @@ export function ListingCard(props: ListingCardProps): ReactElement {
|
|
|
589
594
|
/>
|
|
590
595
|
</Typography.Text>
|
|
591
596
|
|
|
592
|
-
|
|
597
|
+
{/*
|
|
598
|
+
Two lines, clipped at the end of the second — NOT antd's `ellipsis`,
|
|
599
|
+
which is one line and lands the cut in the middle of a word: this card
|
|
600
|
+
cut job titles mid-word on the live storefront's home grid while the
|
|
601
|
+
tile card beside it wrapped the same title over two lines. The rule is
|
|
602
|
+
`titleClamp.ts`'s, shared with that tile.
|
|
603
|
+
*/}
|
|
604
|
+
<Typography.Text
|
|
605
|
+
className={TITLE_CLAMP_CLASS}
|
|
606
|
+
data-testid="listings-card-title"
|
|
607
|
+
>
|
|
593
608
|
{title}
|
|
594
609
|
</Typography.Text>
|
|
595
610
|
|
|
@@ -630,6 +645,9 @@ export function ListingCard(props: ListingCardProps): ReactElement {
|
|
|
630
645
|
<style href={CARD_TARGET_STYLE_HREF} precedence="default">
|
|
631
646
|
{cardTargetCss()}
|
|
632
647
|
</style>
|
|
648
|
+
<style href={TITLE_CLAMP_STYLE_HREF} precedence="default">
|
|
649
|
+
{titleClampCss()}
|
|
650
|
+
</style>
|
|
633
651
|
<Card
|
|
634
652
|
size="small"
|
|
635
653
|
data-testid="listings-card"
|
|
@@ -76,33 +76,34 @@ import {
|
|
|
76
76
|
import type { ListingCardOpenProps } from "./ListingCard.js";
|
|
77
77
|
import { ListingPhoto } from "./ListingPhoto.js";
|
|
78
78
|
import { ListingPrice } from "./ListingPrice.js";
|
|
79
|
+
import {
|
|
80
|
+
TITLE_CLAMP_CLASS,
|
|
81
|
+
TITLE_CLAMP_STYLE_HREF,
|
|
82
|
+
titleClampCss,
|
|
83
|
+
} from "./titleClamp.js";
|
|
79
84
|
import type { ThemeModeProp } from "./types.js";
|
|
80
85
|
|
|
81
|
-
/** Lines of title a tile draws before it clips. See the file header. */
|
|
82
|
-
const TITLE_LINES = 2;
|
|
83
|
-
|
|
84
|
-
/** The class the clamped title carries, for {@link feedCardCss}. */
|
|
85
|
-
export const FEED_TITLE_CLASS = "stapel-listing-feed-title";
|
|
86
|
-
|
|
87
|
-
/** The `href` the hoisted feed stylesheet is deduplicated by. */
|
|
88
|
-
export const FEED_CARD_STYLE_HREF = "stapel-listings-feed-card";
|
|
89
|
-
|
|
90
86
|
/**
|
|
91
|
-
* The clamp
|
|
87
|
+
* The title clamp is `titleClamp.ts`'s, not this file's.
|
|
88
|
+
*
|
|
89
|
+
* It lived here, and the grid card (`ListingCard`) answered the same question
|
|
90
|
+
* with antd's one-line `ellipsis` — which is how the busiest surface on the
|
|
91
|
+
* storefront ended up cutting job titles mid-word while the tile beside it
|
|
92
|
+
* wrapped them over two lines. One answer, one module, both cards.
|
|
92
93
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
94
|
+
* These three names stay as aliases because this file's tests and a reader
|
|
95
|
+
* looking for "the feed card's clamp" both expect them here.
|
|
96
|
+
*
|
|
97
|
+
* They are re-export bindings rather than `const` copies: under
|
|
98
|
+
* `--isolatedDeclarations` the declaration emitter types each export from that
|
|
99
|
+
* export alone, and `const FEED_TITLE_CLASS = TITLE_CLAMP_CLASS` gives it
|
|
100
|
+
* nothing to go on. An aliased re-export carries the original's type with it.
|
|
99
101
|
*/
|
|
100
|
-
export
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
102
|
+
export {
|
|
103
|
+
TITLE_CLAMP_CLASS as FEED_TITLE_CLASS,
|
|
104
|
+
TITLE_CLAMP_STYLE_HREF as FEED_CARD_STYLE_HREF,
|
|
105
|
+
titleClampCss as feedCardCss,
|
|
106
|
+
} from "./titleClamp.js";
|
|
106
107
|
|
|
107
108
|
/** The tile. `position: relative` is what the heart and the badge overlay are
|
|
108
109
|
* pinned to; `minWidth: 0` keeps a long word inside its grid track. */
|
|
@@ -172,8 +173,8 @@ export function ListingFeedCard(props: ListingFeedCardProps): ReactElement {
|
|
|
172
173
|
<style href={CARD_TARGET_STYLE_HREF} precedence="default">
|
|
173
174
|
{cardTargetCss()}
|
|
174
175
|
</style>
|
|
175
|
-
<style href={
|
|
176
|
-
{
|
|
176
|
+
<style href={TITLE_CLAMP_STYLE_HREF} precedence="default">
|
|
177
|
+
{titleClampCss()}
|
|
177
178
|
</style>
|
|
178
179
|
<div
|
|
179
180
|
style={TILE}
|
|
@@ -206,7 +207,7 @@ export function ListingFeedCard(props: ListingFeedCardProps): ReactElement {
|
|
|
206
207
|
{/* Title before price: on a feed a person is browsing, not
|
|
207
208
|
comparing — the ref's order, and the reverse of the SERP's. */}
|
|
208
209
|
<Typography.Text
|
|
209
|
-
className={
|
|
210
|
+
className={TITLE_CLAMP_CLASS}
|
|
210
211
|
data-testid="listings-feed-title"
|
|
211
212
|
>
|
|
212
213
|
{title}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `titleClamp` — how many lines of a listing title a card shows, and where the
|
|
3
|
+
* cut goes when there are more.
|
|
4
|
+
*
|
|
5
|
+
* ── the defect ────────────────────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* The tile card (`ListingFeedCard`) clamped its title to two lines. The grid
|
|
8
|
+
* card (`ListingCard`) used antd's `<Typography.Text ellipsis>`, which is ONE
|
|
9
|
+
* line, and one line of a job or a part title lands in the middle of a word:
|
|
10
|
+
* the live storefront's home grid drew titles cut as "Electrician - construc…"
|
|
11
|
+
* and "Administrator - restaur…". The reference wraps the same titles over two
|
|
12
|
+
* lines and cuts, if it has to, at the end of the second.
|
|
13
|
+
*
|
|
14
|
+
* Two cards, two different answers to the same question, and the wrong one was
|
|
15
|
+
* on the busiest surface. So the answer lives here once and both cards read it.
|
|
16
|
+
*
|
|
17
|
+
* ── the rule ──────────────────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* display: -webkit-box + -webkit-box-orient: vertical + -webkit-line-clamp
|
|
20
|
+
* two lines, with the browser's own ellipsis at the end of the second.
|
|
21
|
+
* overflow-wrap: normal
|
|
22
|
+
* a long word moves to the next line whole rather than being broken across
|
|
23
|
+
* the wrap. Without it a narrow grid track will split a word to fill a
|
|
24
|
+
* line, which is the same defect one level down.
|
|
25
|
+
*
|
|
26
|
+
* ── why a hoisted stylesheet and not an inline style ──────────────────────
|
|
27
|
+
*
|
|
28
|
+
* `-webkit-line-clamp` and `-webkit-box-orient` do not survive the trip
|
|
29
|
+
* through a React style object: the serializer and every DOM implementation
|
|
30
|
+
* that is not a browser drop them silently, so a card written that way clamps
|
|
31
|
+
* in Chrome, does not clamp in a test, and nothing anywhere says which. A
|
|
32
|
+
* hoisted sheet keeps one copy for the document and makes the rule something a
|
|
33
|
+
* test can read.
|
|
34
|
+
*
|
|
35
|
+
* Both cards emit the sheet under the SAME `href`, which is how React 19
|
|
36
|
+
* deduplicates it — a second href carrying the same rule would hoist a second
|
|
37
|
+
* copy for every page showing both card shapes.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/** Lines of title a card draws before it clips. */
|
|
41
|
+
export const TITLE_CLAMP_LINES = 2;
|
|
42
|
+
|
|
43
|
+
/** The class a clamped title carries. */
|
|
44
|
+
export const TITLE_CLAMP_CLASS = "stapel-listing-title-clamp";
|
|
45
|
+
|
|
46
|
+
/** The `href` the hoisted stylesheet is deduplicated by. */
|
|
47
|
+
export const TITLE_CLAMP_STYLE_HREF = "stapel-listings-title-clamp";
|
|
48
|
+
|
|
49
|
+
/** The rule itself, as text, so a test can assert it. */
|
|
50
|
+
export function titleClampCss(): string {
|
|
51
|
+
return (
|
|
52
|
+
`.${TITLE_CLAMP_CLASS}{display:-webkit-box;-webkit-box-orient:vertical;` +
|
|
53
|
+
`-webkit-line-clamp:${String(TITLE_CLAMP_LINES)};overflow:hidden;` +
|
|
54
|
+
`overflow-wrap:normal}`
|
|
55
|
+
);
|
|
56
|
+
}
|