@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/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-AQNn2p5e.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;
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` → an inline compact
1702
- * row. Defaults to `primary`.
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-CrdAtOBl.js";
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.36.0",
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": "^2.4.0",
54
- "@linktr.ee/messaging-taxonomy": "^0.8.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
- * Footer of action buttons. The **first** action's `variant` picks the layout:
29
- * `primary` stacked full-width buttons; `secondary` → an inline compact row.
30
- * Each button still paints in its own emphasis. Returns `null` when there are
31
- * no actions.
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
- }> = ({ surface, actions }) => {
38
+ layout: 'stacked' | 'row'
39
+ }> = ({ surface, actions, layout }) => {
37
40
  if (actions == null || actions.length === 0) return null
38
41
 
39
- const inline = (actions[0].variant ?? 'primary') === 'secondary'
42
+ const stacked = layout === 'stacked'
40
43
 
41
44
  return (
42
45
  <div
43
46
  className={classNames(
44
- 'mt-3 flex gap-2',
45
- inline ? 'flex-row flex-wrap' : 'flex-col'
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={!inline}
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
- return (
94
- <div className="shrink-0 px-4 py-3">
95
- <div className="flex min-w-0 flex-col gap-[2px]">
96
- {hasTitle && (
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
- {hasSubtitle && (
106
- <p
107
- className={classNames(
108
- SUBTITLE_CLASS,
109
- subtitleLines <= 1 && 'truncate'
110
- )}
111
- style={subtitleStyle}
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
- </div>
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
- <RichCardFooter surface={ctaSurface} actions={actions} />
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: [-15, 0, 15],
35
+ 3: [-13, 0, 13],
36
36
  }
37
37
  const FAN_OFFSET_PX: Record<1 | 2 | 3, number[]> = {
38
38
  1: [0],
39
- 2: [-34, 34],
40
- 3: [-58, 0, 58],
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
- // `rounded-[12px]` explicit, NOT `rounded-xl` this preset's `xl`
99
- // radius is 4rem, which rounds a small tile into an oval. A soft drop
100
- // shadow (no border) separates the overlapping tiles.
101
- className="absolute left-1/2 top-1/2 h-[82%] w-[46%] rounded-[12px] object-cover shadow-lg"
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` → an inline compact
26
- * row. Defaults to `primary`.
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
  }