@linktr.ee/messaging-react 2.1.0 → 2.2.0-rc-1778753733
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-CsJvUF_b.js → Card-BdTueeyk.js} +2 -2
- package/dist/{Card-CsJvUF_b.js.map → Card-BdTueeyk.js.map} +1 -1
- package/dist/{Card-DlMSDSdm.js → Card-ChR37pLZ.js} +2 -2
- package/dist/{Card-DlMSDSdm.js.map → Card-ChR37pLZ.js.map} +1 -1
- package/dist/{Card-CFFNq49v.js → Card-EKxCn56j.js} +3 -3
- package/dist/{Card-CFFNq49v.js.map → Card-EKxCn56j.js.map} +1 -1
- package/dist/{LockedThumbnail-DpJx169C.js → LockedThumbnail-B16qP3eH.js} +2 -2
- package/dist/{LockedThumbnail-DpJx169C.js.map → LockedThumbnail-B16qP3eH.js.map} +1 -1
- package/dist/index-Dn7BC9xK.js +4748 -0
- package/dist/index-Dn7BC9xK.js.map +1 -0
- package/dist/index.d.ts +591 -25
- package/dist/index.js +24 -19
- package/package.json +1 -1
- package/src/components/CustomMessage/MessageAttachmentConversations.stories.tsx +841 -0
- package/src/components/LinkAttachment/LinkAttachment.stories.tsx +7 -92
- package/src/components/LinkAttachment/LinkAttachment.test.tsx +69 -0
- package/src/components/LinkAttachment/components/Received/Card.tsx +10 -30
- package/src/components/LinkAttachment/components/_shared/CardShell.tsx +5 -1
- package/src/components/LinkAttachment/index.tsx +24 -50
- package/src/components/LinkAttachment/types.ts +12 -5
- package/src/components/MessageAttachment/Audio/AudioAttachment.stories.tsx +203 -0
- package/src/components/MessageAttachment/Audio/index.tsx +189 -0
- package/src/components/MessageAttachment/File/FileAttachment.stories.tsx +352 -0
- package/src/components/MessageAttachment/File/index.tsx +240 -0
- package/src/components/MessageAttachment/Image/ImageAttachment.stories.tsx +288 -0
- package/src/components/MessageAttachment/Image/index.tsx +257 -0
- package/src/components/MessageAttachment/MessageAttachment.test.tsx +783 -0
- package/src/components/MessageAttachment/Pdf/PdfAttachment.stories.tsx +292 -0
- package/src/components/MessageAttachment/Pdf/index.tsx +228 -0
- package/src/components/MessageAttachment/Video/VideoAttachment.stories.tsx +272 -0
- package/src/components/MessageAttachment/Video/index.tsx +281 -0
- package/src/components/MessageAttachment/_shared/Bubble.tsx +173 -0
- package/src/components/MessageAttachment/_shared/CompactDocumentRow.tsx +152 -0
- package/src/components/MessageAttachment/_shared/DismissButton.tsx +39 -0
- package/src/components/MessageAttachment/_shared/DownloadAction.tsx +175 -0
- package/src/components/MessageAttachment/_shared/ImageViewer.tsx +314 -0
- package/src/components/MessageAttachment/_shared/MediaStackGrid.tsx +139 -0
- package/src/components/MessageAttachment/_shared/PdfViewer.tsx +100 -0
- package/src/components/MessageAttachment/_shared/VideoViewer.tsx +171 -0
- package/src/components/MessageAttachment/_shared/ViewerShell.tsx +159 -0
- package/src/components/MessageAttachment/_shared/fileMeta.test.ts +82 -0
- package/src/components/MessageAttachment/_shared/fileMeta.ts +95 -0
- package/src/components/MessageAttachment/_shared/triggerDownload.ts +54 -0
- package/src/components/MessageAttachment/_shared/useViewer.ts +53 -0
- package/src/components/MessageAttachment/index.tsx +149 -0
- package/src/components/MessageAttachment/stories/StoryTable.tsx +72 -0
- package/src/components/MessageAttachment/types.ts +178 -0
- package/src/index.ts +32 -0
- package/dist/Card-D32U6KfZ.js +0 -85
- package/dist/Card-D32U6KfZ.js.map +0 -1
- package/dist/Card-DlSSJPip.js +0 -60
- package/dist/Card-DlSSJPip.js.map +0 -1
- package/dist/Card-zGbhRBwv.js +0 -48
- package/dist/Card-zGbhRBwv.js.map +0 -1
- package/dist/CardThumbnail-DTBuRQHF.js +0 -239
- package/dist/CardThumbnail-DTBuRQHF.js.map +0 -1
- package/dist/index-DfcRe-Hj.js +0 -3103
- package/dist/index-DfcRe-Hj.js.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import AudioAttachment, {
|
|
2
|
+
type AudioComposerProps,
|
|
3
|
+
type AudioReceivedProps,
|
|
4
|
+
type AudioSentProps,
|
|
5
|
+
} from './Audio'
|
|
6
|
+
import FileAttachment, {
|
|
7
|
+
type FileComposerProps,
|
|
8
|
+
type FileReceivedProps,
|
|
9
|
+
type FileSentProps,
|
|
10
|
+
} from './File'
|
|
11
|
+
import ImageAttachment, {
|
|
12
|
+
type ImageComposerProps,
|
|
13
|
+
type ImageReceivedProps,
|
|
14
|
+
type ImageSentProps,
|
|
15
|
+
} from './Image'
|
|
16
|
+
import PdfAttachment, {
|
|
17
|
+
type PdfComposerProps,
|
|
18
|
+
type PdfReceivedProps,
|
|
19
|
+
type PdfSentProps,
|
|
20
|
+
} from './Pdf'
|
|
21
|
+
import VideoAttachment, {
|
|
22
|
+
type VideoComposerProps,
|
|
23
|
+
type VideoReceivedProps,
|
|
24
|
+
type VideoSentProps,
|
|
25
|
+
} from './Video'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Per-MIME chat attachment cards used inside the messaging stream.
|
|
29
|
+
*
|
|
30
|
+
* Each subnamespace (`Image`, `Video`, `Audio`, `Pdf`, `File`) exposes
|
|
31
|
+
* three states (`Composer`, `Sent`, `Received`) that map to the
|
|
32
|
+
* sender / recipient sides of a chat:
|
|
33
|
+
*
|
|
34
|
+
* - `Composer` — sender draft state with a dismiss `×` affordance.
|
|
35
|
+
* - `Sent` — sender-side state shown after posting.
|
|
36
|
+
* - `Received` — recipient-side state.
|
|
37
|
+
*
|
|
38
|
+
* Sent / Received variants accept an optional `text` prop that renders
|
|
39
|
+
* inside the same bubble as the attachment — mirroring the mobile chat
|
|
40
|
+
* where a sender can pair a caption with an attachment ('Here is the
|
|
41
|
+
* file', 'Here is the image'). Composer prop types intentionally omit
|
|
42
|
+
* `text` (and `items` / `groupPosition`) so the type system enforces
|
|
43
|
+
* the same contract: captions live in the message-input textarea, not
|
|
44
|
+
* inside the draft attachment preview.
|
|
45
|
+
*
|
|
46
|
+
* Stacked attachments — every type except `Composer` accepts an
|
|
47
|
+
* `items={[…]}` prop on Sent / Received that takes precedence over
|
|
48
|
+
* the singular `src`/`filename`/etc. inputs:
|
|
49
|
+
*
|
|
50
|
+
* - Image / Video render a 1 / 2 / 3 / 4-tile grid (5+ collapse
|
|
51
|
+
* into a `+N` overflow tile). Activating any tile opens the
|
|
52
|
+
* built-in `ImageViewer` / `VideoViewer` at that index, with
|
|
53
|
+
* arrow-key navigation between siblings.
|
|
54
|
+
* - Pdf / Audio / File render their compact rows / players in a
|
|
55
|
+
* vertical stack with an 8px gap between rows. Each row keeps
|
|
56
|
+
* its own activation target — Pdf opens the viewer at that
|
|
57
|
+
* index, Audio gets its own native player, and File downloads
|
|
58
|
+
* the asset for that row.
|
|
59
|
+
*
|
|
60
|
+
* The Composer surface accepts only a single attachment at a time,
|
|
61
|
+
* so its props omit `items` / `text`.
|
|
62
|
+
*
|
|
63
|
+
* Image / Video Composer renders bare — just the media with rounded
|
|
64
|
+
* corners and a dismiss overlay, no surrounding bubble — so the draft
|
|
65
|
+
* preview reads as an in-progress attachment instead of a sent
|
|
66
|
+
* message. Sent / Received wrap the media in the shared bubble chrome.
|
|
67
|
+
*
|
|
68
|
+
* Image / Video / Pdf are interactive in every state (Composer, Sent,
|
|
69
|
+
* Received) — clicks open the corresponding native viewer with built-in
|
|
70
|
+
* download. File rows download the asset directly. Audio renders a
|
|
71
|
+
* native `<audio controls>` player which exposes its own download in
|
|
72
|
+
* the kebab menu, so we don't render a separate Download affordance.
|
|
73
|
+
*
|
|
74
|
+
* # Lazy-loading defaults
|
|
75
|
+
*
|
|
76
|
+
* Media attachments bake in network-friendly defaults for long chat
|
|
77
|
+
* histories and expose explicit escape hatches when a caller knows
|
|
78
|
+
* better (e.g. a hero attachment that's already on-screen):
|
|
79
|
+
*
|
|
80
|
+
* - **Image** — every `<img>` rendered on the bubble surface
|
|
81
|
+
* (Composer thumbnail, single + stacked tiles, `+N` overflow)
|
|
82
|
+
* ships with `loading="lazy" decoding="async"`. Override via the
|
|
83
|
+
* attachment's `loading` prop (or `ImageItem.loading` for per-tile
|
|
84
|
+
* control). The opened `ImageViewer` always eager-loads the
|
|
85
|
+
* active image so it appears immediately.
|
|
86
|
+
* - **Video** — the `<video>` rendered inside the `VideoViewer`
|
|
87
|
+
* defaults to `preload="none"`, so no video bytes are fetched
|
|
88
|
+
* until the user opens the viewer. Poster `<img>` thumbnails on
|
|
89
|
+
* the bubble surface are `loading="lazy" decoding="async"`.
|
|
90
|
+
* Override via the attachment's `preload` prop (or
|
|
91
|
+
* `VideoItem.preload` per item). Once the viewer is open the
|
|
92
|
+
* active item bumps to `preload="metadata"` so duration / the
|
|
93
|
+
* first frame appear immediately.
|
|
94
|
+
* - **Audio** — a single audio bubble defaults to
|
|
95
|
+
* `preload="metadata"` so the native player surfaces duration
|
|
96
|
+
* immediately. A stacked audio bubble (`items.length > 1`)
|
|
97
|
+
* defaults to `preload="none"` for every player so a thread of
|
|
98
|
+
* voice memos doesn't fan out N parallel metadata requests on
|
|
99
|
+
* first paint. Override via the attachment's `preload` prop
|
|
100
|
+
* (or `AudioItem.preload` per track).
|
|
101
|
+
* - **Pdf** / **File** — no inline media element to throttle; the
|
|
102
|
+
* bubble renders compact rows that download / open lazily on
|
|
103
|
+
* activation. No `loading` / `preload` props on these surfaces.
|
|
104
|
+
*/
|
|
105
|
+
const MessageAttachment = {
|
|
106
|
+
Image: ImageAttachment,
|
|
107
|
+
Video: VideoAttachment,
|
|
108
|
+
Audio: AudioAttachment,
|
|
109
|
+
Pdf: PdfAttachment,
|
|
110
|
+
File: FileAttachment,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default MessageAttachment
|
|
114
|
+
|
|
115
|
+
export type {
|
|
116
|
+
ImageComposerProps,
|
|
117
|
+
ImageSentProps,
|
|
118
|
+
ImageReceivedProps,
|
|
119
|
+
VideoComposerProps,
|
|
120
|
+
VideoSentProps,
|
|
121
|
+
VideoReceivedProps,
|
|
122
|
+
AudioComposerProps,
|
|
123
|
+
AudioSentProps,
|
|
124
|
+
AudioReceivedProps,
|
|
125
|
+
PdfComposerProps,
|
|
126
|
+
PdfSentProps,
|
|
127
|
+
PdfReceivedProps,
|
|
128
|
+
FileComposerProps,
|
|
129
|
+
FileSentProps,
|
|
130
|
+
FileReceivedProps,
|
|
131
|
+
}
|
|
132
|
+
export type {
|
|
133
|
+
ImageItem,
|
|
134
|
+
ImageLoadingMode,
|
|
135
|
+
VideoItem,
|
|
136
|
+
PdfItem,
|
|
137
|
+
AudioItem,
|
|
138
|
+
FileItem,
|
|
139
|
+
BubbleGroupPosition,
|
|
140
|
+
MediaPreloadMode,
|
|
141
|
+
MessageAttachmentBaseProps,
|
|
142
|
+
MessageAttachmentState,
|
|
143
|
+
} from './types'
|
|
144
|
+
export { bubbleGroupPositionFromStream } from './types'
|
|
145
|
+
export {
|
|
146
|
+
formatFileSize,
|
|
147
|
+
getFileExtensionLabel,
|
|
148
|
+
buildCompactMetaLabel,
|
|
149
|
+
} from './_shared/fileMeta'
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared layout used by every per-type `MessageAttachment` story —
|
|
5
|
+
* three columns (Composer / Sent / Received) on a tinted page so the
|
|
6
|
+
* dark sender bubbles read against a softer surface and the light
|
|
7
|
+
* received bubbles still pop. Mirrors the layout the existing
|
|
8
|
+
* `LinkAttachment` and `MediaMessage` stories use.
|
|
9
|
+
*/
|
|
10
|
+
export const StoryPage: React.FC<{ children: React.ReactNode }> = ({
|
|
11
|
+
children,
|
|
12
|
+
}) => (
|
|
13
|
+
<div className="min-h-screen w-full bg-[#F9F7F4] p-12">{children}</div>
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
export const StoryHeading: React.FC<{
|
|
17
|
+
title: string
|
|
18
|
+
description?: string
|
|
19
|
+
}> = ({ title, description }) => (
|
|
20
|
+
<div className="mb-6">
|
|
21
|
+
<h2 className="text-base font-semibold text-black/80">{title}</h2>
|
|
22
|
+
{description ? (
|
|
23
|
+
<p className="mt-1 max-w-prose text-sm text-black/55">{description}</p>
|
|
24
|
+
) : null}
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export const ColumnHeader: React.FC<{ children: React.ReactNode }> = ({
|
|
29
|
+
children,
|
|
30
|
+
}) => (
|
|
31
|
+
<p className="mb-3 text-xs font-medium uppercase tracking-wide text-black/40">
|
|
32
|
+
{children}
|
|
33
|
+
</p>
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
export interface StoryRowProps {
|
|
37
|
+
label: string
|
|
38
|
+
/** Omit `composer` to leave the column blank — used by stacked
|
|
39
|
+
* Image/Video rows since the composer surface only ever shows a
|
|
40
|
+
* single attachment (no stacked previews in the composer). */
|
|
41
|
+
composer?: React.ReactNode
|
|
42
|
+
sent: React.ReactNode
|
|
43
|
+
received: React.ReactNode
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const StoryRow: React.FC<StoryRowProps> = ({
|
|
47
|
+
label,
|
|
48
|
+
composer,
|
|
49
|
+
sent,
|
|
50
|
+
received,
|
|
51
|
+
}) => (
|
|
52
|
+
<div className="grid grid-cols-[160px_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)] items-start gap-6 py-4">
|
|
53
|
+
<p className="text-xs font-medium text-black/55">{label}</p>
|
|
54
|
+
<div>{composer ?? null}</div>
|
|
55
|
+
<div>{sent}</div>
|
|
56
|
+
<div>{received}</div>
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
export const StoryGrid: React.FC<{ children: React.ReactNode }> = ({
|
|
61
|
+
children,
|
|
62
|
+
}) => (
|
|
63
|
+
<div className="space-y-3 divide-y divide-black/5">
|
|
64
|
+
<div className="grid grid-cols-[160px_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)] gap-6">
|
|
65
|
+
<span />
|
|
66
|
+
<ColumnHeader>Composer</ColumnHeader>
|
|
67
|
+
<ColumnHeader>Sent</ColumnHeader>
|
|
68
|
+
<ColumnHeader>Received</ColumnHeader>
|
|
69
|
+
</div>
|
|
70
|
+
{children}
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type React from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Three messaging states a `MessageAttachment.*` card can render in,
|
|
5
|
+
* mirroring the LinkAttachment / LockedAttachment families.
|
|
6
|
+
*/
|
|
7
|
+
export type MessageAttachmentState = 'composer' | 'sent' | 'received'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Visual variant of the surrounding `Bubble` chrome. Sender-side bubbles
|
|
11
|
+
* (`composer`, `sent`) get the dark treatment; recipient-side bubbles
|
|
12
|
+
* (`received`) get the light treatment.
|
|
13
|
+
*/
|
|
14
|
+
export type BubbleVariant = 'dark' | 'light'
|
|
15
|
+
|
|
16
|
+
/** Maps a state to the bubble chrome it should render with. */
|
|
17
|
+
export const bubbleVariantForState = (
|
|
18
|
+
state: MessageAttachmentState
|
|
19
|
+
): BubbleVariant => (state === 'received' ? 'light' : 'dark')
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Position of a bubble inside a same-author run, mirroring the grouping
|
|
23
|
+
* stream-chat-react applies to consecutive messages from the same user:
|
|
24
|
+
*
|
|
25
|
+
* - `'single'` — standalone message, every corner fully rounded.
|
|
26
|
+
* - `'first'` — first in a 2+ message run; the corner facing the
|
|
27
|
+
* next bubble in the run is flattened.
|
|
28
|
+
* - `'middle'` — both the corner facing the previous bubble and the
|
|
29
|
+
* corner facing the next bubble are flattened.
|
|
30
|
+
* - `'end'` — last in a 2+ message run; the corner facing the
|
|
31
|
+
* previous bubble is flattened (this is also the
|
|
32
|
+
* bubble the avatar attaches to on the receiver side).
|
|
33
|
+
*
|
|
34
|
+
* "Facing" is determined by the bubble's side: sender-aligned bubbles
|
|
35
|
+
* cluster against their right edge, receiver-aligned bubbles against
|
|
36
|
+
* their left edge.
|
|
37
|
+
*/
|
|
38
|
+
export type BubbleGroupPosition = 'single' | 'first' | 'middle' | 'end'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Convenience adapter that derives the `BubbleGroupPosition` from the
|
|
42
|
+
* three booleans `stream-chat-react` forwards into the `Message` HOC.
|
|
43
|
+
* Drop-in for `useMessageContext()` consumers:
|
|
44
|
+
*
|
|
45
|
+
* ```tsx
|
|
46
|
+
* const { firstOfGroup, endOfGroup, groupedByUser } = useMessageContext()
|
|
47
|
+
* <MessageAttachment.Image.Sent
|
|
48
|
+
* {...props}
|
|
49
|
+
* groupPosition={bubbleGroupPositionFromStream({
|
|
50
|
+
* firstOfGroup, endOfGroup, groupedByUser,
|
|
51
|
+
* })}
|
|
52
|
+
* />
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export const bubbleGroupPositionFromStream = ({
|
|
56
|
+
firstOfGroup,
|
|
57
|
+
endOfGroup,
|
|
58
|
+
groupedByUser,
|
|
59
|
+
}: {
|
|
60
|
+
firstOfGroup?: boolean
|
|
61
|
+
endOfGroup?: boolean
|
|
62
|
+
groupedByUser?: boolean
|
|
63
|
+
}): BubbleGroupPosition => {
|
|
64
|
+
if (!groupedByUser) return 'single'
|
|
65
|
+
if (firstOfGroup && endOfGroup) return 'single'
|
|
66
|
+
if (firstOfGroup) return 'first'
|
|
67
|
+
if (endOfGroup) return 'end'
|
|
68
|
+
return 'middle'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Loading hint forwarded to the underlying `<img>` element. */
|
|
72
|
+
export type ImageLoadingMode = 'lazy' | 'eager'
|
|
73
|
+
|
|
74
|
+
/** Preload hint forwarded to the underlying `<video>` / `<audio>` element. */
|
|
75
|
+
export type MediaPreloadMode = 'none' | 'metadata' | 'auto'
|
|
76
|
+
|
|
77
|
+
/** A single image inside an Image attachment (single or stacked). */
|
|
78
|
+
export interface ImageItem {
|
|
79
|
+
src: string
|
|
80
|
+
alt?: string
|
|
81
|
+
/** Optional width/height hint — preserved aspect on cover-fit. */
|
|
82
|
+
width?: number
|
|
83
|
+
height?: number
|
|
84
|
+
/**
|
|
85
|
+
* Per-tile native lazy-load override. Defaults to the parent
|
|
86
|
+
* attachment's `loading` value (which itself defaults to `'lazy'`).
|
|
87
|
+
* Pass `'eager'` for above-the-fold hero tiles.
|
|
88
|
+
*/
|
|
89
|
+
loading?: ImageLoadingMode
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** A single video inside a Video attachment (single or stacked). */
|
|
93
|
+
export interface VideoItem {
|
|
94
|
+
src: string
|
|
95
|
+
/** Poster / thumbnail rendered before playback starts. */
|
|
96
|
+
poster?: string
|
|
97
|
+
/** Optional MIME type (e.g. `video/mp4`). */
|
|
98
|
+
mimeType?: string
|
|
99
|
+
/**
|
|
100
|
+
* Per-item override for the `<video preload>` hint. Defaults to the
|
|
101
|
+
* parent attachment's `preload` value (which itself defaults to
|
|
102
|
+
* `'none'` so the poster carries the visual weight and no video
|
|
103
|
+
* bytes are fetched until playback).
|
|
104
|
+
*/
|
|
105
|
+
preload?: MediaPreloadMode
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** A single PDF inside a Pdf attachment (single or stacked). */
|
|
109
|
+
export interface PdfItem {
|
|
110
|
+
src: string
|
|
111
|
+
/** Filename — drives the title + the meta line + the viewer toolbar. */
|
|
112
|
+
filename?: string
|
|
113
|
+
/** File size in bytes — formatted into the `EXT · SIZE` meta line. */
|
|
114
|
+
fileSize?: number
|
|
115
|
+
/** Override displayed title. Defaults to `filename`. */
|
|
116
|
+
title?: string
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** A single audio file inside an Audio attachment (single or stacked). */
|
|
120
|
+
export interface AudioItem {
|
|
121
|
+
src: string
|
|
122
|
+
/** MIME type hint — typed onto the inline `<source>` element. */
|
|
123
|
+
mimeType?: string
|
|
124
|
+
/**
|
|
125
|
+
* Filename — used as the download default name (consumed by the
|
|
126
|
+
* native player's kebab menu).
|
|
127
|
+
*/
|
|
128
|
+
filename?: string
|
|
129
|
+
/**
|
|
130
|
+
* Per-track override for the `<audio preload>` hint. Defaults to
|
|
131
|
+
* the parent attachment's `preload` value, which itself defaults
|
|
132
|
+
* to `'metadata'` for a single audio (so the native player can
|
|
133
|
+
* surface duration immediately) and `'none'` for a stacked thread
|
|
134
|
+
* (so a fan-out of metadata requests doesn't happen on first paint).
|
|
135
|
+
*/
|
|
136
|
+
preload?: MediaPreloadMode
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** A single generic file inside a File attachment (single or stacked). */
|
|
140
|
+
export interface FileItem {
|
|
141
|
+
src: string
|
|
142
|
+
/** Filename — drives the title + the meta line + the download default name. */
|
|
143
|
+
filename?: string
|
|
144
|
+
/** File size in bytes — formatted into the `EXT · SIZE` meta line. */
|
|
145
|
+
fileSize?: number
|
|
146
|
+
/** MIME type — drives the type icon. Defaults to `application/octet-stream`. */
|
|
147
|
+
mimeType?: string
|
|
148
|
+
/** Override displayed title. Defaults to `filename`. */
|
|
149
|
+
title?: string
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Shared props every `MessageAttachment.*.{Composer|Sent|Received}` accepts. */
|
|
153
|
+
export interface MessageAttachmentBaseProps {
|
|
154
|
+
/**
|
|
155
|
+
* Optional text rendered below the attachment inside the same bubble.
|
|
156
|
+
* Mirrors the chat behavior in the mobile screenshots — `'Here is the
|
|
157
|
+
* file'`, `'Here is the image'`, etc.
|
|
158
|
+
*/
|
|
159
|
+
text?: React.ReactNode
|
|
160
|
+
/**
|
|
161
|
+
* Position of this attachment inside a same-author message run —
|
|
162
|
+
* mirrors the corner-flattening stream-chat-react applies to text
|
|
163
|
+
* bubbles in a clustered conversation. Defaults to `'single'`, so
|
|
164
|
+
* standalone usage keeps every corner rounded. Pass the value
|
|
165
|
+
* derived from `bubbleGroupPositionFromStream({ firstOfGroup,
|
|
166
|
+
* endOfGroup, groupedByUser })` when rendering inside a real
|
|
167
|
+
* `CustomMessage` thread to make the attachment visually merge
|
|
168
|
+
* with adjacent bubbles. Image / Video Composer ignore this — the
|
|
169
|
+
* draft preview renders bare without the surrounding bubble.
|
|
170
|
+
*/
|
|
171
|
+
groupPosition?: BubbleGroupPosition
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Shared props for the `Composer` state of every attachment type. */
|
|
175
|
+
export interface ComposerExtras {
|
|
176
|
+
/** Renders a dismiss `×` button overlaid on the attachment. */
|
|
177
|
+
onDismiss?: () => void
|
|
178
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { ChannelView } from './components/ChannelView'
|
|
|
8
8
|
export { default as ActionButton } from './components/ActionButton'
|
|
9
9
|
export { default as LockedAttachment } from './components/LockedAttachment'
|
|
10
10
|
export { default as LinkAttachment } from './components/LinkAttachment'
|
|
11
|
+
export { default as MessageAttachment } from './components/MessageAttachment'
|
|
11
12
|
export { Avatar } from './components/Avatar'
|
|
12
13
|
export { FaqList } from './components/FaqList'
|
|
13
14
|
export { FaqListItem } from './components/FaqList/FaqListItem'
|
|
@@ -71,6 +72,37 @@ export type {
|
|
|
71
72
|
LinkAttachmentCta,
|
|
72
73
|
LinkAttachmentLayout,
|
|
73
74
|
} from './components/LinkAttachment'
|
|
75
|
+
export type {
|
|
76
|
+
ImageComposerProps as MessageAttachmentImageComposerProps,
|
|
77
|
+
ImageSentProps as MessageAttachmentImageSentProps,
|
|
78
|
+
ImageReceivedProps as MessageAttachmentImageReceivedProps,
|
|
79
|
+
VideoComposerProps as MessageAttachmentVideoComposerProps,
|
|
80
|
+
VideoSentProps as MessageAttachmentVideoSentProps,
|
|
81
|
+
VideoReceivedProps as MessageAttachmentVideoReceivedProps,
|
|
82
|
+
AudioComposerProps as MessageAttachmentAudioComposerProps,
|
|
83
|
+
AudioSentProps as MessageAttachmentAudioSentProps,
|
|
84
|
+
AudioReceivedProps as MessageAttachmentAudioReceivedProps,
|
|
85
|
+
PdfComposerProps as MessageAttachmentPdfComposerProps,
|
|
86
|
+
PdfSentProps as MessageAttachmentPdfSentProps,
|
|
87
|
+
PdfReceivedProps as MessageAttachmentPdfReceivedProps,
|
|
88
|
+
FileComposerProps as MessageAttachmentFileComposerProps,
|
|
89
|
+
FileSentProps as MessageAttachmentFileSentProps,
|
|
90
|
+
FileReceivedProps as MessageAttachmentFileReceivedProps,
|
|
91
|
+
ImageItem as MessageAttachmentImageItem,
|
|
92
|
+
VideoItem as MessageAttachmentVideoItem,
|
|
93
|
+
PdfItem as MessageAttachmentPdfItem,
|
|
94
|
+
AudioItem as MessageAttachmentAudioItem,
|
|
95
|
+
FileItem as MessageAttachmentFileItem,
|
|
96
|
+
BubbleGroupPosition as MessageAttachmentGroupPosition,
|
|
97
|
+
MessageAttachmentBaseProps,
|
|
98
|
+
MessageAttachmentState,
|
|
99
|
+
} from './components/MessageAttachment'
|
|
100
|
+
export {
|
|
101
|
+
formatFileSize,
|
|
102
|
+
getFileExtensionLabel,
|
|
103
|
+
buildCompactMetaLabel,
|
|
104
|
+
bubbleGroupPositionFromStream as messageAttachmentGroupPositionFromStream,
|
|
105
|
+
} from './components/MessageAttachment'
|
|
74
106
|
export type { CustomMessageRegistry } from './components/CustomMessage/context'
|
|
75
107
|
export type { AttachmentSourceType } from './components/AttachmentCard/utils/mimeType'
|
|
76
108
|
export type { Faq, FaqListProps } from './components/FaqList'
|
package/dist/Card-D32U6KfZ.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { jsx as a, jsxs as d } from "react/jsx-runtime";
|
|
2
|
-
import { XIcon as x, PencilSimpleIcon as N } from "@phosphor-icons/react";
|
|
3
|
-
import { i as k, C as o, A as w, a as c, b as A } from "./CardThumbnail-DTBuRQHF.js";
|
|
4
|
-
const B = ({
|
|
5
|
-
title: e,
|
|
6
|
-
placeholderTitle: h,
|
|
7
|
-
description: m,
|
|
8
|
-
url: u,
|
|
9
|
-
mimeType: i,
|
|
10
|
-
thumbnailUrl: b,
|
|
11
|
-
sourceUrl: s,
|
|
12
|
-
layout: f = "featured",
|
|
13
|
-
appIcon: v,
|
|
14
|
-
cta: g,
|
|
15
|
-
onDismiss: r,
|
|
16
|
-
onEditClick: n
|
|
17
|
-
}) => {
|
|
18
|
-
const l = f === "classic", p = k(i, s), t = r ? /* @__PURE__ */ a(
|
|
19
|
-
"button",
|
|
20
|
-
{
|
|
21
|
-
type: "button",
|
|
22
|
-
onClick: r,
|
|
23
|
-
"aria-label": "Dismiss attachment",
|
|
24
|
-
className: "flex size-6 items-center justify-center rounded-full bg-[#121110] text-white",
|
|
25
|
-
children: /* @__PURE__ */ a(x, { className: "size-3", weight: "bold" })
|
|
26
|
-
}
|
|
27
|
-
) : void 0, C = n ? /* @__PURE__ */ a(
|
|
28
|
-
"button",
|
|
29
|
-
{
|
|
30
|
-
type: "button",
|
|
31
|
-
onClick: n,
|
|
32
|
-
"aria-label": "Edit attachment",
|
|
33
|
-
className: "flex size-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/15",
|
|
34
|
-
children: /* @__PURE__ */ a(N, { className: "size-5", weight: "regular" })
|
|
35
|
-
}
|
|
36
|
-
) : void 0;
|
|
37
|
-
return p ? /* @__PURE__ */ a(o, { variant: "dark", bgClassName: w, children: /* @__PURE__ */ d("div", { className: "flex items-center gap-2 pr-3", children: [
|
|
38
|
-
/* @__PURE__ */ a("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ a(
|
|
39
|
-
c,
|
|
40
|
-
{
|
|
41
|
-
variant: "dark",
|
|
42
|
-
sourceUrl: s,
|
|
43
|
-
title: e,
|
|
44
|
-
mimeType: i
|
|
45
|
-
}
|
|
46
|
-
) }),
|
|
47
|
-
t && /* @__PURE__ */ a("div", { className: "shrink-0", children: t })
|
|
48
|
-
] }) }) : /* @__PURE__ */ d(
|
|
49
|
-
o,
|
|
50
|
-
{
|
|
51
|
-
variant: "dark",
|
|
52
|
-
topRight: l ? t : void 0,
|
|
53
|
-
children: [
|
|
54
|
-
!l && /* @__PURE__ */ a(
|
|
55
|
-
c,
|
|
56
|
-
{
|
|
57
|
-
variant: "dark",
|
|
58
|
-
thumbnailUrl: b,
|
|
59
|
-
sourceUrl: s,
|
|
60
|
-
title: e,
|
|
61
|
-
mimeType: i,
|
|
62
|
-
topRight: t
|
|
63
|
-
}
|
|
64
|
-
),
|
|
65
|
-
/* @__PURE__ */ a(
|
|
66
|
-
A,
|
|
67
|
-
{
|
|
68
|
-
variant: "dark",
|
|
69
|
-
title: e,
|
|
70
|
-
placeholderTitle: h,
|
|
71
|
-
description: m,
|
|
72
|
-
url: u,
|
|
73
|
-
appIcon: v,
|
|
74
|
-
cta: g,
|
|
75
|
-
trailingAction: C
|
|
76
|
-
}
|
|
77
|
-
)
|
|
78
|
-
]
|
|
79
|
-
}
|
|
80
|
-
);
|
|
81
|
-
};
|
|
82
|
-
export {
|
|
83
|
-
B as default
|
|
84
|
-
};
|
|
85
|
-
//# sourceMappingURL=Card-D32U6KfZ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Card-D32U6KfZ.js","sources":["../src/components/LinkAttachment/components/Composer/Card.tsx"],"sourcesContent":["import { PencilSimpleIcon, XIcon } from '@phosphor-icons/react'\nimport React from 'react'\n\nimport type { LinkAttachmentBaseProps } from '../../types'\nimport CardBody from '../_shared/CardBody'\nimport CardShell from '../_shared/CardShell'\nimport CardThumbnail, {\n AUDIO_BG_CLASS,\n isPlayableAudio,\n} from '../_shared/CardThumbnail'\n\nexport interface ComposerCardProps extends LinkAttachmentBaseProps {\n /**\n * When provided, renders a dismiss X in the thumbnail corner. Called when\n * the composer clicks it to remove the attachment.\n */\n onDismiss?: () => void\n /**\n * When provided, renders a pencil button to the right of the description\n * that the composer can use to edit the attachment metadata.\n */\n onEditClick?: () => void\n}\n\n/**\n * The card the composer sees while drafting a link attachment.\n * Matches the Composer column of the messaging design system in Figma.\n */\nconst ComposerCard: React.FC<ComposerCardProps> = ({\n title,\n placeholderTitle,\n description,\n url,\n mimeType,\n thumbnailUrl,\n sourceUrl,\n layout = 'featured',\n appIcon,\n cta,\n onDismiss,\n onEditClick,\n}) => {\n const isClassic = layout === 'classic'\n const isAudio = isPlayableAudio(mimeType, sourceUrl)\n const dismissButton = onDismiss ? (\n <button\n type=\"button\"\n onClick={onDismiss}\n aria-label=\"Dismiss attachment\"\n className=\"flex size-6 items-center justify-center rounded-full bg-[#121110] text-white\"\n >\n <XIcon className=\"size-3\" weight=\"bold\" />\n </button>\n ) : undefined\n\n const editButton = onEditClick ? (\n <button\n type=\"button\"\n onClick={onEditClick}\n aria-label=\"Edit attachment\"\n className=\"flex size-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/15\"\n >\n <PencilSimpleIcon className=\"size-5\" weight=\"regular\" />\n </button>\n ) : undefined\n\n // Audio cards collapse to just the native player — render the dismiss\n // button as an inline sibling so it always has its own space and never\n // overlaps the audio control's volume/menu buttons.\n if (isAudio) {\n return (\n <CardShell variant=\"dark\" bgClassName={AUDIO_BG_CLASS}>\n <div className=\"flex items-center gap-2 pr-3\">\n <div className=\"min-w-0 flex-1\">\n <CardThumbnail\n variant=\"dark\"\n sourceUrl={sourceUrl}\n title={title}\n mimeType={mimeType}\n />\n </div>\n {dismissButton && <div className=\"shrink-0\">{dismissButton}</div>}\n </div>\n </CardShell>\n )\n }\n\n return (\n <CardShell\n variant=\"dark\"\n topRight={isClassic ? dismissButton : undefined}\n >\n {!isClassic && (\n <CardThumbnail\n variant=\"dark\"\n thumbnailUrl={thumbnailUrl}\n sourceUrl={sourceUrl}\n title={title}\n mimeType={mimeType}\n topRight={dismissButton}\n />\n )}\n <CardBody\n variant=\"dark\"\n title={title}\n placeholderTitle={placeholderTitle}\n description={description}\n url={url}\n appIcon={appIcon}\n cta={cta}\n trailingAction={editButton}\n />\n </CardShell>\n )\n}\n\nexport default ComposerCard\n"],"names":["ComposerCard","title","placeholderTitle","description","url","mimeType","thumbnailUrl","sourceUrl","layout","appIcon","cta","onDismiss","onEditClick","isClassic","isAudio","isPlayableAudio","dismissButton","jsx","XIcon","editButton","PencilSimpleIcon","CardShell","AUDIO_BG_CLASS","jsxs","CardThumbnail","CardBody"],"mappings":";;;AA4BA,MAAMA,IAA4C,CAAC;AAAA,EACjD,OAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,aAAAC;AAAA,EACA,KAAAC;AAAA,EACA,UAAAC;AAAA,EACA,cAAAC;AAAA,EACA,WAAAC;AAAA,EACA,QAAAC,IAAS;AAAA,EACT,SAAAC;AAAA,EACA,KAAAC;AAAA,EACA,WAAAC;AAAA,EACA,aAAAC;AACF,MAAM;AACJ,QAAMC,IAAYL,MAAW,WACvBM,IAAUC,EAAgBV,GAAUE,CAAS,GAC7CS,IAAgBL,IACpB,gBAAAM;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,SAASN;AAAA,MACT,cAAW;AAAA,MACX,WAAU;AAAA,MAEV,UAAA,gBAAAM,EAACC,GAAA,EAAM,WAAU,UAAS,QAAO,OAAA,CAAO;AAAA,IAAA;AAAA,EAAA,IAExC,QAEEC,IAAaP,IACjB,gBAAAK;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,SAASL;AAAA,MACT,cAAW;AAAA,MACX,WAAU;AAAA,MAEV,UAAA,gBAAAK,EAACG,GAAA,EAAiB,WAAU,UAAS,QAAO,UAAA,CAAU;AAAA,IAAA;AAAA,EAAA,IAEtD;AAKJ,SAAIN,IAEA,gBAAAG,EAACI,KAAU,SAAQ,QAAO,aAAaC,GACrC,UAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,gCACb,UAAA;AAAA,IAAA,gBAAAN,EAAC,OAAA,EAAI,WAAU,kBACb,UAAA,gBAAAA;AAAA,MAACO;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,WAAAjB;AAAA,QACA,OAAAN;AAAA,QACA,UAAAI;AAAA,MAAA;AAAA,IAAA,GAEJ;AAAA,IACCW,KAAiB,gBAAAC,EAAC,OAAA,EAAI,WAAU,YAAY,UAAAD,EAAA,CAAc;AAAA,EAAA,EAAA,CAC7D,EAAA,CACF,IAKF,gBAAAO;AAAA,IAACF;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,UAAUR,IAAYG,IAAgB;AAAA,MAErC,UAAA;AAAA,QAAA,CAACH,KACA,gBAAAI;AAAA,UAACO;AAAA,UAAA;AAAA,YACC,SAAQ;AAAA,YACR,cAAAlB;AAAA,YACA,WAAAC;AAAA,YACA,OAAAN;AAAA,YACA,UAAAI;AAAA,YACA,UAAUW;AAAA,UAAA;AAAA,QAAA;AAAA,QAGd,gBAAAC;AAAA,UAACQ;AAAA,UAAA;AAAA,YACC,SAAQ;AAAA,YACR,OAAAxB;AAAA,YACA,kBAAAC;AAAA,YACA,aAAAC;AAAA,YACA,KAAAC;AAAA,YACA,SAAAK;AAAA,YACA,KAAAC;AAAA,YACA,gBAAgBS;AAAA,UAAA;AAAA,QAAA;AAAA,MAClB;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|
package/dist/Card-DlSSJPip.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { jsxs as p, jsx as t } from "react/jsx-runtime";
|
|
2
|
-
import { c, n as x, i as A, A as k, C as B, a as O, b as P } from "./CardThumbnail-DTBuRQHF.js";
|
|
3
|
-
const w = ({
|
|
4
|
-
title: i,
|
|
5
|
-
description: h,
|
|
6
|
-
url: s,
|
|
7
|
-
mimeType: l,
|
|
8
|
-
thumbnailUrl: C,
|
|
9
|
-
sourceUrl: n,
|
|
10
|
-
layout: f = "featured",
|
|
11
|
-
appIcon: v,
|
|
12
|
-
cta: a,
|
|
13
|
-
onClick: e
|
|
14
|
-
}) => {
|
|
15
|
-
const r = c(l, n), d = x(s), u = a == null && d != null && !r ? d : void 0, b = a == null && !r ? e : void 0, g = A(l, n) ? k : void 0, m = a && e ? {
|
|
16
|
-
...a,
|
|
17
|
-
onClick: () => {
|
|
18
|
-
var o;
|
|
19
|
-
e(), (o = a.onClick) == null || o.call(a);
|
|
20
|
-
}
|
|
21
|
-
} : a;
|
|
22
|
-
return /* @__PURE__ */ p(
|
|
23
|
-
B,
|
|
24
|
-
{
|
|
25
|
-
variant: "light",
|
|
26
|
-
href: u,
|
|
27
|
-
onClick: b,
|
|
28
|
-
ariaLabel: i ?? "Open attachment preview",
|
|
29
|
-
bgClassName: g,
|
|
30
|
-
"data-testid": "link-attachment",
|
|
31
|
-
children: [
|
|
32
|
-
f === "featured" && /* @__PURE__ */ t(
|
|
33
|
-
O,
|
|
34
|
-
{
|
|
35
|
-
variant: "light",
|
|
36
|
-
thumbnailUrl: C,
|
|
37
|
-
sourceUrl: n,
|
|
38
|
-
title: i,
|
|
39
|
-
mimeType: l
|
|
40
|
-
}
|
|
41
|
-
),
|
|
42
|
-
/* @__PURE__ */ t(
|
|
43
|
-
P,
|
|
44
|
-
{
|
|
45
|
-
variant: "light",
|
|
46
|
-
title: i,
|
|
47
|
-
description: h,
|
|
48
|
-
url: s,
|
|
49
|
-
appIcon: v,
|
|
50
|
-
cta: m
|
|
51
|
-
}
|
|
52
|
-
)
|
|
53
|
-
]
|
|
54
|
-
}
|
|
55
|
-
);
|
|
56
|
-
};
|
|
57
|
-
export {
|
|
58
|
-
w as default
|
|
59
|
-
};
|
|
60
|
-
//# sourceMappingURL=Card-DlSSJPip.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Card-DlSSJPip.js","sources":["../src/components/LinkAttachment/components/Received/Card.tsx"],"sourcesContent":["import React from 'react'\n\nimport type { LinkAttachmentBaseProps } from '../../types'\nimport CardBody from '../_shared/CardBody'\nimport CardShell from '../_shared/CardShell'\nimport CardThumbnail, {\n AUDIO_BG_CLASS,\n isPlayableAudio,\n isPlayableMedia,\n} from '../_shared/CardThumbnail'\nimport { normalizeExternalHref } from '../_shared/normalizeExternalHref'\n\nexport interface ReceivedCardProps extends LinkAttachmentBaseProps {\n /**\n * Fired when the recipient activates the card. Behavior depends on how\n * the card is configured:\n * - **Link app with a CTA** (FAQ / Form): the CTA owns navigation;\n * `onClick` fires when the recipient taps the CTA itself, alongside\n * `cta.onClick` (use for analytics).\n * - **Link app with a URL** (Spotify / TikTok / generic link): the card\n * chrome is an `<a target=\"_blank\">` opening `url` — `onClick` fires\n * alongside the navigation (use for analytics).\n * - **Image / file / placeholder attachment**: the card has no URL, so\n * it renders as a button. `onClick` is the consumer's hook for\n * opening an image / file preview (lightbox).\n * - **Video / audio attachment**: the shell stays non-interactive so\n * the native media controls remain operable — `onClick` is ignored\n * in this configuration.\n */\n onClick?: () => void\n}\n\n/**\n * The card the recipient sees in chat for a link attachment. Matches the\n * Received column of the messaging design system in Figma.\n *\n * The chrome adapts to its content:\n * - Link previews / link apps render the light card body and either\n * navigate via `url` or surface a CTA button when `cta` is set.\n * - Image / file attachments render as media-only cards — the 180px\n * thumbnail (or type-icon placeholder) fills the card with no title /\n * description body. `onClick` fires when the recipient taps the card\n * so consumers can open an image preview / lightbox.\n */\nconst ReceivedCard: React.FC<ReceivedCardProps> = ({\n title,\n description,\n url,\n mimeType,\n thumbnailUrl,\n sourceUrl,\n layout = 'featured',\n appIcon,\n cta,\n onClick,\n}) => {\n // The Received card is opened by either the CTA (FAQ / Form), the URL\n // (link previews / Spotify / TikTok), or — for media-only attachments —\n // by tapping the entire card chrome to open a preview. We hand the\n // anchor behavior off to CardShell only when there's no CTA so we don't\n // end up with nested anchors when a CTA is present.\n //\n // Video / audio attachments are an exception: wrapping the shell in\n // either a `<button>` or an `<a>` around `<video controls>` /\n // `<audio controls>` creates nested interactive elements, and clicks on\n // the native media controls can bubble up to fire the outer card\n // action (preview / link navigation). For those, we render a plain\n // non-interactive shell and let the media element own clicks — both\n // `shellHref` and `shellOnClick` are suppressed even when `url` is set.\n const isPlayingMedia = isPlayableMedia(mimeType, sourceUrl)\n // Normalize the URL so a bare hostname like `tr.ee/briemix` (used in\n // our own docs / stories) is treated as an external link instead of a\n // relative path. Returns `undefined` for empty / whitespace-only\n // values, so those fall through to the media-preview path instead of\n // producing an empty `href` on the shell anchor.\n const normalizedUrl = normalizeExternalHref(url)\n const shellHref =\n cta == null && normalizedUrl != null && !isPlayingMedia\n ? normalizedUrl\n : undefined\n const shellOnClick =\n cta == null && !isPlayingMedia ? onClick : undefined\n const audioBg = isPlayableAudio(mimeType, sourceUrl)\n ? AUDIO_BG_CLASS\n : undefined\n\n // When a CTA is set the shell isn't interactive — the CTA owns the\n // click target. Forward the card-level `onClick` to the CTA so\n // analytics / side-effect consumers still fire on activation while\n // preserving the CTA's own `onClick` callback.\n const wrappedCta =\n cta && onClick\n ? {\n ...cta,\n onClick: () => {\n onClick()\n cta.onClick?.()\n },\n }\n : cta\n\n return (\n <CardShell\n variant=\"light\"\n href={shellHref}\n onClick={shellOnClick}\n ariaLabel={title ?? 'Open attachment preview'}\n bgClassName={audioBg}\n data-testid=\"link-attachment\"\n >\n {layout === 'featured' && (\n <CardThumbnail\n variant=\"light\"\n thumbnailUrl={thumbnailUrl}\n sourceUrl={sourceUrl}\n title={title}\n mimeType={mimeType}\n />\n )}\n <CardBody\n variant=\"light\"\n title={title}\n description={description}\n url={url}\n appIcon={appIcon}\n cta={wrappedCta}\n />\n </CardShell>\n )\n}\n\nexport default ReceivedCard\n"],"names":["ReceivedCard","title","description","url","mimeType","thumbnailUrl","sourceUrl","layout","appIcon","cta","onClick","isPlayingMedia","isPlayableMedia","normalizedUrl","normalizeExternalHref","shellHref","shellOnClick","audioBg","isPlayableAudio","AUDIO_BG_CLASS","wrappedCta","_a","jsxs","CardShell","jsx","CardThumbnail","CardBody"],"mappings":";;AA4CA,MAAMA,IAA4C,CAAC;AAAA,EACjD,OAAAC;AAAA,EACA,aAAAC;AAAA,EACA,KAAAC;AAAA,EACA,UAAAC;AAAA,EACA,cAAAC;AAAA,EACA,WAAAC;AAAA,EACA,QAAAC,IAAS;AAAA,EACT,SAAAC;AAAA,EACA,KAAAC;AAAA,EACA,SAAAC;AACF,MAAM;AAcJ,QAAMC,IAAiBC,EAAgBR,GAAUE,CAAS,GAMpDO,IAAgBC,EAAsBX,CAAG,GACzCY,IACJN,KAAO,QAAQI,KAAiB,QAAQ,CAACF,IACrCE,IACA,QACAG,IACJP,KAAO,QAAQ,CAACE,IAAiBD,IAAU,QACvCO,IAAUC,EAAgBd,GAAUE,CAAS,IAC/Ca,IACA,QAMEC,IACJX,KAAOC,IACH;AAAA,IACE,GAAGD;AAAA,IACH,SAAS,MAAM;;AACb,MAAAC,EAAA,IACAW,IAAAZ,EAAI,YAAJ,QAAAY,EAAA,KAAAZ;AAAA,IACF;AAAA,EAAA,IAEFA;AAEN,SACE,gBAAAa;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,MAAMR;AAAA,MACN,SAASC;AAAA,MACT,WAAWf,KAAS;AAAA,MACpB,aAAagB;AAAA,MACb,eAAY;AAAA,MAEX,UAAA;AAAA,QAAAV,MAAW,cACV,gBAAAiB;AAAA,UAACC;AAAA,UAAA;AAAA,YACC,SAAQ;AAAA,YACR,cAAApB;AAAA,YACA,WAAAC;AAAA,YACA,OAAAL;AAAA,YACA,UAAAG;AAAA,UAAA;AAAA,QAAA;AAAA,QAGJ,gBAAAoB;AAAA,UAACE;AAAA,UAAA;AAAA,YACC,SAAQ;AAAA,YACR,OAAAzB;AAAA,YACA,aAAAC;AAAA,YACA,KAAAC;AAAA,YACA,SAAAK;AAAA,YACA,KAAKY;AAAA,UAAA;AAAA,QAAA;AAAA,MACP;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|
package/dist/Card-zGbhRBwv.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { jsxs as f, jsx as s } from "react/jsx-runtime";
|
|
2
|
-
import { C as b, A as m, a as u, b as v, i as A } from "./CardThumbnail-DTBuRQHF.js";
|
|
3
|
-
const k = ({
|
|
4
|
-
title: a,
|
|
5
|
-
placeholderTitle: i,
|
|
6
|
-
description: t,
|
|
7
|
-
url: o,
|
|
8
|
-
mimeType: r,
|
|
9
|
-
thumbnailUrl: e,
|
|
10
|
-
sourceUrl: d,
|
|
11
|
-
layout: n = "featured",
|
|
12
|
-
appIcon: C,
|
|
13
|
-
cta: l
|
|
14
|
-
}) => /* @__PURE__ */ f(
|
|
15
|
-
b,
|
|
16
|
-
{
|
|
17
|
-
variant: "dark",
|
|
18
|
-
bgClassName: A(r, d) ? m : void 0,
|
|
19
|
-
children: [
|
|
20
|
-
n === "featured" && /* @__PURE__ */ s(
|
|
21
|
-
u,
|
|
22
|
-
{
|
|
23
|
-
variant: "dark",
|
|
24
|
-
thumbnailUrl: e,
|
|
25
|
-
sourceUrl: d,
|
|
26
|
-
title: a,
|
|
27
|
-
mimeType: r
|
|
28
|
-
}
|
|
29
|
-
),
|
|
30
|
-
/* @__PURE__ */ s(
|
|
31
|
-
v,
|
|
32
|
-
{
|
|
33
|
-
variant: "dark",
|
|
34
|
-
title: a,
|
|
35
|
-
placeholderTitle: i,
|
|
36
|
-
description: t,
|
|
37
|
-
url: o,
|
|
38
|
-
appIcon: C,
|
|
39
|
-
cta: l
|
|
40
|
-
}
|
|
41
|
-
)
|
|
42
|
-
]
|
|
43
|
-
}
|
|
44
|
-
);
|
|
45
|
-
export {
|
|
46
|
-
k as default
|
|
47
|
-
};
|
|
48
|
-
//# sourceMappingURL=Card-zGbhRBwv.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Card-zGbhRBwv.js","sources":["../src/components/LinkAttachment/components/Sent/Card.tsx"],"sourcesContent":["import React from 'react'\n\nimport type { LinkAttachmentBaseProps } from '../../types'\nimport CardBody from '../_shared/CardBody'\nimport CardShell from '../_shared/CardShell'\nimport CardThumbnail, {\n AUDIO_BG_CLASS,\n isPlayableAudio,\n} from '../_shared/CardThumbnail'\n\nexport interface SentCardProps extends LinkAttachmentBaseProps {}\n\n/**\n * The card the sender sees in chat after a link attachment has been posted.\n * Matches the Sent column of the messaging design system in Figma — same\n * dark chrome as the Composer card minus the dismiss / edit affordances.\n */\nconst SentCard: React.FC<SentCardProps> = ({\n title,\n placeholderTitle,\n description,\n url,\n mimeType,\n thumbnailUrl,\n sourceUrl,\n layout = 'featured',\n appIcon,\n cta,\n}) => (\n <CardShell\n variant=\"dark\"\n bgClassName={\n isPlayableAudio(mimeType, sourceUrl) ? AUDIO_BG_CLASS : undefined\n }\n >\n {layout === 'featured' && (\n <CardThumbnail\n variant=\"dark\"\n thumbnailUrl={thumbnailUrl}\n sourceUrl={sourceUrl}\n title={title}\n mimeType={mimeType}\n />\n )}\n <CardBody\n variant=\"dark\"\n title={title}\n placeholderTitle={placeholderTitle}\n description={description}\n url={url}\n appIcon={appIcon}\n cta={cta}\n />\n </CardShell>\n)\n\nexport default SentCard\n"],"names":["SentCard","title","placeholderTitle","description","url","mimeType","thumbnailUrl","sourceUrl","layout","appIcon","cta","jsxs","CardShell","isPlayableAudio","AUDIO_BG_CLASS","jsx","CardThumbnail","CardBody"],"mappings":";;AAiBA,MAAMA,IAAoC,CAAC;AAAA,EACzC,OAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,aAAAC;AAAA,EACA,KAAAC;AAAA,EACA,UAAAC;AAAA,EACA,cAAAC;AAAA,EACA,WAAAC;AAAA,EACA,QAAAC,IAAS;AAAA,EACT,SAAAC;AAAA,EACA,KAAAC;AACF,MACE,gBAAAC;AAAA,EAACC;AAAA,EAAA;AAAA,IACC,SAAQ;AAAA,IACR,aACEC,EAAgBR,GAAUE,CAAS,IAAIO,IAAiB;AAAA,IAGzD,UAAA;AAAA,MAAAN,MAAW,cACV,gBAAAO;AAAA,QAACC;AAAA,QAAA;AAAA,UACC,SAAQ;AAAA,UACR,cAAAV;AAAA,UACA,WAAAC;AAAA,UACA,OAAAN;AAAA,UACA,UAAAI;AAAA,QAAA;AAAA,MAAA;AAAA,MAGJ,gBAAAU;AAAA,QAACE;AAAA,QAAA;AAAA,UACC,SAAQ;AAAA,UACR,OAAAhB;AAAA,UACA,kBAAAC;AAAA,UACA,aAAAC;AAAA,UACA,KAAAC;AAAA,UACA,SAAAK;AAAA,UACA,KAAAC;AAAA,QAAA;AAAA,MAAA;AAAA,IACF;AAAA,EAAA;AACF;"}
|