@linktr.ee/messaging-react 3.37.1 → 3.38.0-rc-1786342246

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.
Files changed (39) hide show
  1. package/dist/{Card-DVMzYquF.js → Card-C40UQcWN.js} +2 -2
  2. package/dist/{Card-DVMzYquF.js.map → Card-C40UQcWN.js.map} +1 -1
  3. package/dist/{Card-Dxk_Ggxw.cjs → Card-C8glaNwz.cjs} +2 -2
  4. package/dist/{Card-Dxk_Ggxw.cjs.map → Card-C8glaNwz.cjs.map} +1 -1
  5. package/dist/assets/index.css +1 -1
  6. package/dist/index-Byt_hX2r.cjs +3 -0
  7. package/dist/index-Byt_hX2r.cjs.map +1 -0
  8. package/dist/{index-BOBIiAuD.js → index-fk5ihkQY.js} +2240 -2277
  9. package/dist/index-fk5ihkQY.js.map +1 -0
  10. package/dist/index.cjs +1 -1
  11. package/dist/index.d.ts +1 -34
  12. package/dist/index.js +27 -29
  13. package/package.json +4 -3
  14. package/src/components/ChannelView.tsx +1 -6
  15. package/src/components/CustomMessage/CustomMessage.test.tsx +0 -12
  16. package/src/components/CustomMessage/MessageTag.test.tsx +9 -18
  17. package/src/components/CustomMessage/MessageTag.tsx +16 -17
  18. package/src/components/CustomMessage/MessageTail.tsx +45 -0
  19. package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +9 -2
  20. package/src/components/CustomMessage/StreamAttachmentMessage.tsx +18 -4
  21. package/src/components/CustomMessage/index.tsx +23 -23
  22. package/src/components/LinkAttachment/components/_shared/BubbleTail.tsx +5 -6
  23. package/src/components/MessageAttachment/_shared/CarouselNav.tsx +5 -6
  24. package/src/components/MessageAttachment/_shared/DownloadAction.tsx +5 -8
  25. package/src/components/MessageAttachment/_shared/ImageViewer.tsx +8 -12
  26. package/src/components/MessageAttachment/_shared/PdfViewer.tsx +1 -1
  27. package/src/components/MessageAttachment/_shared/VideoViewer.tsx +1 -1
  28. package/src/components/MessageAttachment/_shared/ViewerShell.tsx +16 -11
  29. package/src/components/MessageAttachment/_shared/viewerChromeClasses.ts +16 -0
  30. package/src/components/MessagingShell/index.tsx +0 -2
  31. package/src/index.ts +0 -3
  32. package/src/styles.css +56 -377
  33. package/src/types.ts +0 -8
  34. package/dist/index-BOBIiAuD.js.map +0 -1
  35. package/dist/index-D7A8U8GQ.cjs +0 -3
  36. package/dist/index-D7A8U8GQ.cjs.map +0 -1
  37. package/src/components/CustomMessage/MessageVoteButtons.stories.tsx +0 -95
  38. package/src/components/CustomMessage/MessageVoteButtons.tsx +0 -47
  39. package/src/hooks/useMessageVote.ts +0 -77
@@ -1,6 +1,8 @@
1
1
  import { CaretLeftIcon, CaretRightIcon } from '@phosphor-icons/react'
2
2
  import React from 'react'
3
3
 
4
+ import { VIEWER_NAV_CLASS } from './viewerChromeClasses'
5
+
4
6
  export interface CarouselNavProps {
5
7
  onPrev: () => void
6
8
  onNext: () => void
@@ -13,10 +15,7 @@ export interface CarouselNavProps {
13
15
  /**
14
16
  * Prev / next chrome shared by every carousel-aware viewer. Rendered
15
17
  * inside the dialog body so the buttons sit absolutely positioned
16
- * against the viewer (`.mes-media-viewer__nav--{prev,next}`).
17
- *
18
- * Styling lives as plain CSS in `styles.css` so the buttons render
19
- * correctly regardless of whether the consumer has Tailwind wired up.
18
+ * against the viewer, mid-height on each edge.
20
19
  */
21
20
  const CarouselNav: React.FC<CarouselNavProps> = ({
22
21
  onPrev,
@@ -29,7 +28,7 @@ const CarouselNav: React.FC<CarouselNavProps> = ({
29
28
  type="button"
30
29
  onClick={onPrev}
31
30
  aria-label={prevLabel}
32
- className="mes-media-viewer__nav mes-media-viewer__nav--prev"
31
+ className={`${VIEWER_NAV_CLASS} left-4`}
33
32
  >
34
33
  <CaretLeftIcon size={20} weight="bold" aria-hidden />
35
34
  </button>
@@ -37,7 +36,7 @@ const CarouselNav: React.FC<CarouselNavProps> = ({
37
36
  type="button"
38
37
  onClick={onNext}
39
38
  aria-label={nextLabel}
40
- className="mes-media-viewer__nav mes-media-viewer__nav--next"
39
+ className={`${VIEWER_NAV_CLASS} right-4`}
41
40
  >
42
41
  <CaretRightIcon size={20} weight="bold" aria-hidden />
43
42
  </button>
@@ -7,6 +7,7 @@ import classNames from 'classnames'
7
7
  import React, { useCallback, useState } from 'react'
8
8
 
9
9
  import { triggerDownload } from './triggerDownload'
10
+ import { VIEWER_ACTION_CLASS } from './viewerChromeClasses'
10
11
 
11
12
  export type DownloadActionVariant =
12
13
  /** Solid pill button used inside compact / file rows. */
@@ -20,9 +21,7 @@ export type DownloadActionVariant =
20
21
  /**
21
22
  * Round icon button sized to match the close action in the
22
23
  * `ViewerShell` chrome — used by `ImageViewer` for in-viewer
23
- * downloads. Styled with plain CSS (`.mes-media-viewer__action`) so
24
- * it renders correctly without a Tailwind dependency in the
25
- * consumer's app.
24
+ * downloads. Shares `VIEWER_ACTION_CLASS` with the close button.
26
25
  */
27
26
  | 'viewer'
28
27
 
@@ -123,17 +122,15 @@ const DownloadAction: React.FC<DownloadActionProps> = ({
123
122
  }
124
123
 
125
124
  if (variant === 'viewer') {
126
- // Styled entirely through `.mes-media-viewer__action` (plain CSS
127
- // in `styles.css`) so the button renders correctly when consumed
128
- // outside this repo without a Tailwind setup. Sits next to the
129
- // close action inside `ViewerShell`'s top-right chrome.
125
+ // Sits next to the close action inside `ViewerShell`'s top-right chrome
126
+ // and shares its circular-button styling via `VIEWER_ACTION_CLASS`.
130
127
  return (
131
128
  <button
132
129
  type="button"
133
130
  onClick={handleClick}
134
131
  disabled={busy}
135
132
  aria-label={label}
136
- className="mes-media-viewer__action"
133
+ className={VIEWER_ACTION_CLASS}
137
134
  >
138
135
  {busy ? (
139
136
  <CircleNotchIcon size={20} weight="bold" aria-hidden />
@@ -88,19 +88,15 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
88
88
  src={item.src}
89
89
  alt={item.alt ?? filename}
90
90
  draggable={false}
91
- // The user has explicitly opened the viewer, so we want the
92
- // active image to appear immediately rather than fall to the
93
- // browser's lazy-load heuristics. Which is exactly why the
94
- // element is gated on `open`: `ViewerShell` keeps its
95
- // `<dialog>` mounted across open/close so the platform
96
- // transition can play, and an eager `<img>` inside a closed
97
- // dialog made every image bubble in a thread download its
98
- // full-resolution original — note the viewer intentionally
99
- // uses the raw `item.src`, not the CDN-optimised URL the
100
- // bubble tile renders.
101
- loading="eager"
91
+ // Gated on `open` (see the `{open ? : null}` above): `ViewerShell`
92
+ // keeps its `<dialog>` mounted across open/close so the platform
93
+ // transition can play, but a mounted-closed dialog would still hold
94
+ // this `<img>` in the DOM. The viewer uses the raw `item.src` the
95
+ // full-resolution original, not the CDN-optimised URL the bubble tile
96
+ // renders so rendering it unconditionally made every image in a
97
+ // thread prefetch its full-res original. Only mount it while open.
102
98
  decoding="async"
103
- className="mes-media-viewer__image"
99
+ className="block h-auto max-h-[calc(100dvh-128px)] w-auto max-w-[min(96vw,1400px)] select-none object-contain"
104
100
  />
105
101
  ) : null}
106
102
 
@@ -76,7 +76,7 @@ const PdfViewer: React.FC<PdfViewerProps> = ({
76
76
  key={`${index}:${item.src}`}
77
77
  src={iframeSrc}
78
78
  title={filename}
79
- className="mes-media-viewer__iframe"
79
+ className="block h-[calc(100dvh-128px)] w-[min(96vw,1400px)] border-0 bg-white"
80
80
  // Sandbox the iframe to stop the embedded document from
81
81
  // reaching parent context. We intentionally omit
82
82
  // `allow-same-origin`: even when the PDF host happens to share
@@ -92,7 +92,7 @@ const VideoViewer: React.FC<VideoViewerProps> = ({
92
92
  muted
93
93
  playsInline
94
94
  preload={item.preload ?? 'metadata'}
95
- className="mes-media-viewer__video"
95
+ className="block h-auto max-h-[calc(100dvh-128px)] w-auto max-w-[min(96vw,1400px)] bg-black"
96
96
  >
97
97
  {item.mimeType ? <source src={item.src} type={item.mimeType} /> : null}
98
98
  </video>
@@ -1,6 +1,8 @@
1
1
  import { XIcon } from '@phosphor-icons/react'
2
2
  import React, { useEffect, useRef } from 'react'
3
3
 
4
+ import { VIEWER_ACTION_CLASS } from './viewerChromeClasses'
5
+
4
6
  export interface ViewerShellProps {
5
7
  open: boolean
6
8
  onClose: () => void
@@ -20,8 +22,7 @@ export interface ViewerShellProps {
20
22
  counter?: React.ReactNode
21
23
  /**
22
24
  * Extra buttons rendered alongside the close button in the top-right
23
- * chrome (e.g. a download action). They share `.mes-media-viewer__action`
24
- * styling so the row reads as one consistent button group.
25
+ * chrome (e.g. a download action), laid out as one consistent button group.
25
26
  */
26
27
  actions?: React.ReactNode
27
28
  children: React.ReactNode
@@ -40,10 +41,10 @@ export interface ViewerShellProps {
40
41
  * fallback for environments where `showModal` is unavailable
41
42
  * (jsdom under tests).
42
43
  *
43
- * The structural styling lives as plain CSS under `.mes-media-viewer*`
44
- * in `styles.css` so a consumer who imports
45
- * `@linktr.ee/messaging-react/styles.css` gets a working lightbox even
46
- * if their Tailwind pipeline isn't scanning our `dist`.
44
+ * The `<dialog>` root, backdrop, and open/close transitions live in
45
+ * `styles.css` under `.mes-media-viewer` (they use `::backdrop` /
46
+ * `@starting-style`, which Tailwind can't express). The chrome inside is
47
+ * plain Tailwind on the JSX, like the rest of the package.
47
48
  */
48
49
  const ViewerShell: React.FC<ViewerShellProps> = ({
49
50
  open,
@@ -154,24 +155,28 @@ const ViewerShell: React.FC<ViewerShellProps> = ({
154
155
  onClose={handleDialogClose}
155
156
  onClick={handleDialogClick}
156
157
  >
157
- <div className="mes-media-viewer__chrome">
158
+ <div className="pointer-events-none absolute inset-x-3 top-3 z-[1] flex items-center justify-between gap-3">
158
159
  {counter ? (
159
- <span className="mes-media-viewer__counter">{counter}</span>
160
+ <span className="pointer-events-auto inline-flex items-center rounded-full bg-white/[0.12] px-3 py-1.5 text-[13px] font-medium leading-none tabular-nums text-white">
161
+ {counter}
162
+ </span>
160
163
  ) : null}
161
- <div className="mes-media-viewer__actions">
164
+ <div className="pointer-events-auto ml-auto inline-flex items-center gap-2">
162
165
  {actions}
163
166
  <button
164
167
  ref={closeButtonRef}
165
168
  type="button"
166
169
  onClick={onClose}
167
170
  aria-label="Close viewer"
168
- className="mes-media-viewer__action"
171
+ className={VIEWER_ACTION_CLASS}
169
172
  >
170
173
  <XIcon size={20} weight="bold" aria-hidden />
171
174
  </button>
172
175
  </div>
173
176
  </div>
174
- <div className="mes-media-viewer__body">{children}</div>
177
+ <div className="box-border flex h-full w-full items-center justify-center px-6 py-16">
178
+ {children}
179
+ </div>
175
180
  </dialog>
176
181
  )
177
182
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared Tailwind class strings for the media-viewer chrome buttons, reused by
3
+ * `ViewerShell` (close), `DownloadAction`, and `CarouselNav` so every
4
+ * viewer-chrome button stays visually consistent without duplicating the
5
+ * utility list. The lightbox `<dialog>` root itself still lives in
6
+ * `styles.css` (`.mes-media-viewer`) because `::backdrop` / `@starting-style`
7
+ * can't be expressed in Tailwind.
8
+ */
9
+
10
+ /** Circular 40px icon button — close action, download action, etc. */
11
+ export const VIEWER_ACTION_CLASS =
12
+ 'inline-flex size-10 items-center justify-center rounded-full border-0 bg-white/[0.12] p-0 text-white transition-colors duration-150 ease-[ease] hover:bg-white/[0.22] focus-visible:bg-white/[0.22] focus-visible:outline-none disabled:cursor-default disabled:opacity-40'
13
+
14
+ /** Circular 48px carousel prev/next button, mid-height against the dialog. */
15
+ export const VIEWER_NAV_CLASS =
16
+ 'absolute top-1/2 z-[1] inline-flex size-12 -translate-y-1/2 items-center justify-center rounded-full border-0 bg-white/[0.12] p-0 text-white transition-colors duration-150 ease-[ease] hover:bg-white/[0.22] focus-visible:bg-white/[0.22] focus-visible:outline-none'
@@ -32,7 +32,6 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
32
32
  onReportParticipantClick,
33
33
  dmAgentEnabled,
34
34
  onMessageSent,
35
- chatbotVotingEnabled = false,
36
35
  viewerLanguage,
37
36
  renderChannelBanner,
38
37
  customChannelActions,
@@ -260,7 +259,6 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
260
259
  onReportParticipantClick={onReportParticipantClick}
261
260
  dmAgentEnabled={dmAgentEnabled}
262
261
  onMessageSent={onMessageSent}
263
- chatbotVotingEnabled={chatbotVotingEnabled}
264
262
  viewerLanguage={viewerLanguage}
265
263
  customChannelActions={customChannelActions}
266
264
  renderChannelActions={renderChannelActions}
package/src/index.ts CHANGED
@@ -15,7 +15,6 @@ export { default as MessageBubble } from './components/MessageBubble'
15
15
  export { FaqList } from './components/FaqList'
16
16
  export { FaqListItem } from './components/FaqList/FaqListItem'
17
17
  export { ChannelEmptyState } from './components/MessagingShell/ChannelEmptyState'
18
- export { MessageVoteButtons } from './components/CustomMessage/MessageVoteButtons'
19
18
  export {
20
19
  MediaMessage,
21
20
  isLinkAttachment,
@@ -34,7 +33,6 @@ export { CustomMessageProvider } from './components/CustomMessage/context'
34
33
  // Hooks
35
34
  export { useComposerLocked } from './components/CustomMessageInput'
36
35
  export { useMessaging } from './hooks/useMessaging'
37
- export { useMessageVote } from './hooks/useMessageVote'
38
36
  export { useCustomMessage } from './components/CustomMessage/context'
39
37
 
40
38
  // Utils
@@ -136,4 +134,3 @@ export type { CustomMessageRegistry } from './components/CustomMessage/context'
136
134
  export type { AttachmentSourceType } from './components/AttachmentCard/utils/mimeType'
137
135
  export type { Faq, FaqListProps } from './components/FaqList'
138
136
  export type { FaqListItemProps } from './components/FaqList/FaqListItem'
139
- export type { VoteSelection } from './hooks/useMessageVote'