@linktr.ee/messaging-react 3.36.0 → 3.37.0-rc-1786156011
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/dist/{Card-CjM2b_y5.cjs → Card-CznUgezA.cjs} +2 -2
- package/dist/{Card-CjM2b_y5.cjs.map → Card-CznUgezA.cjs.map} +1 -1
- package/dist/{Card-B4qcC_0B.js → Card-DruKqEx_.js} +2 -2
- package/dist/{Card-B4qcC_0B.js.map → Card-DruKqEx_.js.map} +1 -1
- package/dist/{index-CrdAtOBl.js → index-B24ijhqy.js} +922 -910
- package/dist/{index-CrdAtOBl.js.map → index-B24ijhqy.js.map} +1 -1
- package/dist/{index-AQNn2p5e.cjs → index-DD615yi4.cjs} +3 -3
- package/dist/{index-AQNn2p5e.cjs.map → index-DD615yi4.cjs.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/components/RichCard/RichCard.test.tsx +26 -0
- package/src/components/RichCard/RichCardBody.tsx +52 -32
- package/src/components/RichCard/RichCardImages.tsx +30 -9
- package/src/components/RichCard/types.ts +3 -2
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-DD615yi4.cjs");exports.ActionButton=e.ActionButton;exports.Avatar=e.Avatar;exports.ChannelEmptyState=e.ChannelEmptyState;exports.ChannelList=e.ChannelList;exports.ChannelView=e.ChannelView;exports.CustomMessageProvider=e.CustomMessageProvider;exports.FaqList=e.FaqList;exports.FaqListItem=e.FaqListItem;exports.LinkAttachment=e.LinkAttachment;exports.LockedAttachment=e.LockedAttachment;exports.MediaMessage=e.MediaMessage;exports.MessageAttachment=e.MessageAttachment;exports.MessageBubble=e.MessageBubble;exports.MessageVoteButtons=e.MessageVoteButtons;exports.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.RichCard=e.RichCard;exports.buildCompactMetaLabel=e.buildCompactMetaLabel;exports.formatFileSize=e.formatFileSize;exports.formatRelativeTime=e.formatRelativeTime;exports.getFileExtensionLabel=e.getFileExtensionLabel;exports.getMessageDisplayText=e.getMessageDisplayText;exports.isLinkAttachment=e.isLinkAttachment;exports.isUuidLike=e.isUuidLike;exports.messageAttachmentGroupPositionFromStream=e.bubbleGroupPositionFromStream;exports.normalizeLanguageCode=e.normalizeLanguageCode;exports.optimizeMessagingAttachmentUrl=e.optimizeMessagingAttachmentUrl;exports.resolveConversationParticipant=e.resolveConversationParticipant;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useComposerLocked=e.useComposerLocked;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1698,8 +1698,9 @@ export declare interface RichCardActionButton {
|
|
|
1698
1698
|
onClick?: () => void;
|
|
1699
1699
|
/**
|
|
1700
1700
|
* Button emphasis. The **first** action's variant drives the footer layout:
|
|
1701
|
-
* `primary` → a stacked full-width button; `secondary` →
|
|
1702
|
-
*
|
|
1701
|
+
* `primary` → a stacked full-width button below the copy; `secondary` → a row
|
|
1702
|
+
* in the chin with the copy on the left and the action pinned right. Defaults
|
|
1703
|
+
* to `primary`.
|
|
1703
1704
|
*/
|
|
1704
1705
|
variant?: 'primary' | 'secondary';
|
|
1705
1706
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as h, k as L, l as d, m as p, R as C, n as v, p as A, q as k, s as F, t as b, u as P, v as f, w as x, x as y, o as z, y as B, z as S, B as q, D, E, G as R, H as V, I as w } from "./index-
|
|
1
|
+
import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as h, k as L, l as d, m as p, R as C, n as v, p as A, q as k, s as F, t as b, u as P, v as f, w as x, x as y, o as z, y as B, z as S, B as q, D, E, G as R, H as V, I as w } from "./index-B24ijhqy.js";
|
|
2
2
|
export {
|
|
3
3
|
e as ActionButton,
|
|
4
4
|
t as Avatar,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/messaging-react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.37.0-rc-1786156011",
|
|
4
4
|
"description": "React messaging components built on messaging-core for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@linktr.ee/component-library": "11.8.6",
|
|
53
|
-
"@linktr.ee/messaging-core": "
|
|
54
|
-
"@linktr.ee/messaging-taxonomy": "^0.
|
|
53
|
+
"@linktr.ee/messaging-core": "2.4.0-rc-1786156011",
|
|
54
|
+
"@linktr.ee/messaging-taxonomy": "^0.9.0",
|
|
55
55
|
"@phosphor-icons/react": "^2.1.10"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
@@ -193,4 +193,30 @@ describe('RichCard', () => {
|
|
|
193
193
|
// Secondary → inline, content-width button (no w-full).
|
|
194
194
|
expect(screen.getByText('Shop now')).not.toHaveClass('w-full')
|
|
195
195
|
})
|
|
196
|
+
|
|
197
|
+
it('lays a secondary chin out as a row: copy left, action right', async () => {
|
|
198
|
+
const { rerender } = renderWithProviders(
|
|
199
|
+
<RichCard.Received
|
|
200
|
+
title="Test collection"
|
|
201
|
+
subtitle="Collection of 3 links"
|
|
202
|
+
images={[HERO, IMG_2, IMG_3]}
|
|
203
|
+
actions={[{ label: 'Open', href: 'tr.ee/c', variant: 'secondary' }]}
|
|
204
|
+
/>
|
|
205
|
+
)
|
|
206
|
+
// The action shares one justify-between row with the title/subtitle copy.
|
|
207
|
+
const link = await screen.findByText('Open')
|
|
208
|
+
const row = link.closest('.justify-between')
|
|
209
|
+
expect(row).not.toBeNull()
|
|
210
|
+
expect(row?.textContent).toContain('Test collection')
|
|
211
|
+
|
|
212
|
+
// Primary stays stacked below the copy — no chin row.
|
|
213
|
+
rerender(
|
|
214
|
+
<RichCard.Received
|
|
215
|
+
title="Welcome"
|
|
216
|
+
images={[HERO]}
|
|
217
|
+
actions={[{ label: 'Set up', variant: 'primary' }]}
|
|
218
|
+
/>
|
|
219
|
+
)
|
|
220
|
+
expect(screen.getByText('Set up').closest('.justify-between')).toBeNull()
|
|
221
|
+
})
|
|
196
222
|
})
|
|
@@ -25,24 +25,27 @@ const TITLE_CLASS = 'line-clamp-2 text-[14px] font-medium leading-5 tracking-[0.
|
|
|
25
25
|
const SUBTITLE_CLASS = 'text-[12px] leading-4 tracking-[0.24px]'
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* The action footer. `layout` (chosen by the first action's `variant` in
|
|
29
|
+
* {@link RichCardBody}) picks the presentation: `stacked` (`primary`) →
|
|
30
|
+
* full-width buttons stacked below the copy; `row` (`secondary`) → content-width
|
|
31
|
+
* buttons pinned to the right of the chin row (`shrink-0`, so they never
|
|
32
|
+
* compress the title/subtitle). Each button still paints in its own emphasis.
|
|
33
|
+
* Returns `null` when there are no actions.
|
|
32
34
|
*/
|
|
33
35
|
const RichCardFooter: React.FC<{
|
|
34
36
|
surface: LinkAttachmentVariant
|
|
35
37
|
actions?: RichCardActionButton[]
|
|
36
|
-
|
|
38
|
+
layout: 'stacked' | 'row'
|
|
39
|
+
}> = ({ surface, actions, layout }) => {
|
|
37
40
|
if (actions == null || actions.length === 0) return null
|
|
38
41
|
|
|
39
|
-
const
|
|
42
|
+
const stacked = layout === 'stacked'
|
|
40
43
|
|
|
41
44
|
return (
|
|
42
45
|
<div
|
|
43
46
|
className={classNames(
|
|
44
|
-
'
|
|
45
|
-
|
|
47
|
+
'flex gap-2',
|
|
48
|
+
stacked ? 'mt-3 flex-col' : 'shrink-0 flex-row flex-wrap justify-end'
|
|
46
49
|
)}
|
|
47
50
|
>
|
|
48
51
|
{actions.map((action, index) => (
|
|
@@ -50,7 +53,7 @@ const RichCardFooter: React.FC<{
|
|
|
50
53
|
key={`${action.label}-${index}`}
|
|
51
54
|
variant={surface}
|
|
52
55
|
cta={action}
|
|
53
|
-
fullWidth={
|
|
56
|
+
fullWidth={stacked}
|
|
54
57
|
standalone
|
|
55
58
|
/>
|
|
56
59
|
))}
|
|
@@ -90,32 +93,49 @@ const RichCardBody: React.FC<RichCardBodyProps> = ({
|
|
|
90
93
|
}
|
|
91
94
|
: chinTextStyle
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<div className="flex min-w-0 items-center gap-2">
|
|
98
|
-
{appIcon ? <span className="shrink-0">{appIcon}</span> : null}
|
|
99
|
-
<p className={classNames('min-w-0', TITLE_CLASS)} style={chinTextStyle}>
|
|
100
|
-
{title}
|
|
101
|
-
</p>
|
|
102
|
-
</div>
|
|
103
|
-
)}
|
|
96
|
+
// A secondary primary-action lays out as a row (copy left, action right);
|
|
97
|
+
// primary stacks the button full-width below the copy.
|
|
98
|
+
const inline =
|
|
99
|
+
hasActions && (actions?.[0]?.variant ?? 'primary') === 'secondary'
|
|
104
100
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
>
|
|
113
|
-
{subtitle}
|
|
101
|
+
const textBlock = (
|
|
102
|
+
<div className="flex min-w-0 flex-col gap-[2px]">
|
|
103
|
+
{hasTitle && (
|
|
104
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
105
|
+
{appIcon ? <span className="shrink-0">{appIcon}</span> : null}
|
|
106
|
+
<p className={classNames('min-w-0', TITLE_CLASS)} style={chinTextStyle}>
|
|
107
|
+
{title}
|
|
114
108
|
</p>
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
|
|
112
|
+
{hasSubtitle && (
|
|
113
|
+
<p
|
|
114
|
+
className={classNames(
|
|
115
|
+
SUBTITLE_CLASS,
|
|
116
|
+
subtitleLines <= 1 && 'truncate'
|
|
117
|
+
)}
|
|
118
|
+
style={subtitleStyle}
|
|
119
|
+
>
|
|
120
|
+
{subtitle}
|
|
121
|
+
</p>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
)
|
|
117
125
|
|
|
118
|
-
|
|
126
|
+
return (
|
|
127
|
+
<div className="shrink-0 px-4 py-3">
|
|
128
|
+
{inline ? (
|
|
129
|
+
<div className="flex items-center justify-between gap-3">
|
|
130
|
+
{textBlock}
|
|
131
|
+
<RichCardFooter surface={ctaSurface} actions={actions} layout="row" />
|
|
132
|
+
</div>
|
|
133
|
+
) : (
|
|
134
|
+
<>
|
|
135
|
+
{textBlock}
|
|
136
|
+
<RichCardFooter surface={ctaSurface} actions={actions} layout="stacked" />
|
|
137
|
+
</>
|
|
138
|
+
)}
|
|
119
139
|
</div>
|
|
120
140
|
)
|
|
121
141
|
}
|
|
@@ -32,12 +32,27 @@ const FAN_BG: Record<LinkAttachmentVariant, string> = {
|
|
|
32
32
|
const FAN_ROTATION: Record<1 | 2 | 3, number[]> = {
|
|
33
33
|
1: [0],
|
|
34
34
|
2: [-10, 10],
|
|
35
|
-
3: [-
|
|
35
|
+
3: [-13, 0, 13],
|
|
36
36
|
}
|
|
37
37
|
const FAN_OFFSET_PX: Record<1 | 2 | 3, number[]> = {
|
|
38
38
|
1: [0],
|
|
39
|
-
2: [-
|
|
40
|
-
3: [-
|
|
39
|
+
2: [-30, 30],
|
|
40
|
+
3: [-44, 0, 44],
|
|
41
|
+
}
|
|
42
|
+
// Paint z-order per slot so the **centre** card sits on top. Survivors render
|
|
43
|
+
// left→right, so with the naive `zIndex: position` the right card overlaps the
|
|
44
|
+
// middle; here the centre (slot 1 of a 3-fan) gets the highest layer.
|
|
45
|
+
const FAN_ZORDER: Record<1 | 2 | 3, number[]> = {
|
|
46
|
+
1: [0],
|
|
47
|
+
2: [0, 1],
|
|
48
|
+
3: [1, 3, 2],
|
|
49
|
+
}
|
|
50
|
+
// The centre card of a 3-fan is nudged larger so it reads as the top of the
|
|
51
|
+
// stack rather than one of three equals (mirrors the collection design).
|
|
52
|
+
const FAN_SCALE: Record<1 | 2 | 3, number[]> = {
|
|
53
|
+
1: [1],
|
|
54
|
+
2: [1, 1],
|
|
55
|
+
3: [1, 1.08, 1],
|
|
41
56
|
}
|
|
42
57
|
|
|
43
58
|
/**
|
|
@@ -75,6 +90,8 @@ const RichCardFan: React.FC<{
|
|
|
75
90
|
) as 1 | 2 | 3
|
|
76
91
|
const rotations = FAN_ROTATION[count]
|
|
77
92
|
const offsets = FAN_OFFSET_PX[count]
|
|
93
|
+
const zorder = FAN_ZORDER[count]
|
|
94
|
+
const scales = FAN_SCALE[count]
|
|
78
95
|
|
|
79
96
|
return (
|
|
80
97
|
<div
|
|
@@ -95,13 +112,17 @@ const RichCardFan: React.FC<{
|
|
|
95
112
|
loading="lazy"
|
|
96
113
|
decoding="async"
|
|
97
114
|
onError={() => setFailed((prev) => ({ ...prev, [index]: true }))}
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
115
|
+
// A uniform, slightly-landscape tile (`h-74% w-66%`) with `object-cover`
|
|
116
|
+
// so the fan stays tidy whatever the source thumbnails are (OG scrape,
|
|
117
|
+
// upload, video frame) — the tiles are decorative, so an even cover
|
|
118
|
+
// crop reads better than per-image aspect. `rounded-[12px]` explicit,
|
|
119
|
+
// NOT `rounded-xl` — this preset's `xl` radius is 4rem, which rounds a
|
|
120
|
+
// tile into an oval. A soft drop shadow (no border) separates the
|
|
121
|
+
// overlapping tiles.
|
|
122
|
+
className="absolute left-1/2 top-1/2 h-[74%] w-[66%] rounded-[12px] object-cover shadow-lg"
|
|
102
123
|
style={{
|
|
103
|
-
transform: `translate(-50%, -50%) translateX(${offsets[position]}px) rotate(${rotations[position]}deg)`,
|
|
104
|
-
zIndex: position,
|
|
124
|
+
transform: `translate(-50%, -50%) translateX(${offsets[position]}px) rotate(${rotations[position]}deg) scale(${scales[position]})`,
|
|
125
|
+
zIndex: zorder[position],
|
|
105
126
|
}}
|
|
106
127
|
/>
|
|
107
128
|
))}
|
|
@@ -22,8 +22,9 @@ export interface RichCardActionButton {
|
|
|
22
22
|
onClick?: () => void
|
|
23
23
|
/**
|
|
24
24
|
* Button emphasis. The **first** action's variant drives the footer layout:
|
|
25
|
-
* `primary` → a stacked full-width button; `secondary` →
|
|
26
|
-
*
|
|
25
|
+
* `primary` → a stacked full-width button below the copy; `secondary` → a row
|
|
26
|
+
* in the chin with the copy on the left and the action pinned right. Defaults
|
|
27
|
+
* to `primary`.
|
|
27
28
|
*/
|
|
28
29
|
variant?: 'primary' | 'secondary'
|
|
29
30
|
}
|