@banbox/chat 1.0.10 → 1.0.12
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 +17 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -21
- package/dist/index.d.ts +43 -21
- package/dist/index.js +17 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chat/ChatRoot.tsx +28 -13
- package/src/chat/InboxPopup.tsx +7 -1
- package/src/chat/SinglePopup.tsx +7 -1
- package/src/ui/ChatFooter.tsx +36 -26
package/package.json
CHANGED
package/src/chat/ChatRoot.tsx
CHANGED
|
@@ -11,30 +11,44 @@ import SinglePopup from "./SinglePopup";
|
|
|
11
11
|
import type { ChatTheme } from "./InboxPopup";
|
|
12
12
|
|
|
13
13
|
export type ChatRootProps = {
|
|
14
|
+
/** The unified data adapter — provides all threads, messages, and send logic. */
|
|
15
|
+
adapter: ChatAdapter;
|
|
16
|
+
|
|
17
|
+
/** Optional UI callbacks — toast, navigation, kebab menu. */
|
|
18
|
+
uiCallbacks?: ChatUICallbacks;
|
|
19
|
+
|
|
14
20
|
/**
|
|
15
|
-
*
|
|
21
|
+
* Visual theme: "marketplace" | "admin" | { primary, primaryActive, surfaceLow }
|
|
22
|
+
* @example <ChatRoot adapter={adapter} theme="admin" />
|
|
16
23
|
*/
|
|
17
|
-
|
|
24
|
+
theme?: ChatTheme;
|
|
18
25
|
|
|
19
26
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
27
|
+
* Allow-list of toolbar buttons shown in the **InboxPopup** footer.
|
|
28
|
+
*
|
|
29
|
+
* Keys: "attachment" | "emoji" | "businessCard" | "addressCard" | "translate"
|
|
30
|
+
* Default: ["attachment", "emoji", "translate"]
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // Seller inbox — no cards at all:
|
|
34
|
+
* inboxFooterActions={["attachment", "emoji", "translate"]}
|
|
22
35
|
*/
|
|
23
|
-
|
|
36
|
+
inboxFooterActions?: string[];
|
|
24
37
|
|
|
25
38
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
39
|
+
* Allow-list of toolbar buttons shown in the **SinglePopup** footer.
|
|
40
|
+
*
|
|
41
|
+
* Keys: "attachment" | "emoji" | "businessCard" | "addressCard" | "translate"
|
|
42
|
+
* Default: ["attachment", "emoji", "translate"]
|
|
30
43
|
*
|
|
31
44
|
* @example
|
|
32
|
-
*
|
|
45
|
+
* // Single chat with location sharing enabled:
|
|
46
|
+
* singleFooterActions={["attachment", "emoji", "translate", "addressCard"]}
|
|
33
47
|
*/
|
|
34
|
-
|
|
48
|
+
singleFooterActions?: string[];
|
|
35
49
|
};
|
|
36
50
|
|
|
37
|
-
export default function ChatRoot({ adapter, uiCallbacks, theme }: ChatRootProps) {
|
|
51
|
+
export default function ChatRoot({ adapter, uiCallbacks, theme, inboxFooterActions, singleFooterActions }: ChatRootProps) {
|
|
38
52
|
const { isOpen, variant } = useChatUI();
|
|
39
53
|
|
|
40
54
|
// Lock page scroll whenever the chat is open
|
|
@@ -45,7 +59,6 @@ export default function ChatRoot({ adapter, uiCallbacks, theme }: ChatRootProps)
|
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
return createPortal(
|
|
48
|
-
// GalleryProvider is scoped to the chat only.
|
|
49
62
|
<GalleryProvider>
|
|
50
63
|
<AnimatePresence mode="wait">
|
|
51
64
|
{isOpen && (
|
|
@@ -55,6 +68,7 @@ export default function ChatRoot({ adapter, uiCallbacks, theme }: ChatRootProps)
|
|
|
55
68
|
adapter={adapter}
|
|
56
69
|
uiCallbacks={uiCallbacks}
|
|
57
70
|
theme={theme}
|
|
71
|
+
footerActions={inboxFooterActions}
|
|
58
72
|
/>
|
|
59
73
|
) : (
|
|
60
74
|
<SinglePopup
|
|
@@ -62,6 +76,7 @@ export default function ChatRoot({ adapter, uiCallbacks, theme }: ChatRootProps)
|
|
|
62
76
|
adapter={adapter}
|
|
63
77
|
uiCallbacks={uiCallbacks}
|
|
64
78
|
theme={theme}
|
|
79
|
+
footerActions={singleFooterActions}
|
|
65
80
|
/>
|
|
66
81
|
)
|
|
67
82
|
)}
|
package/src/chat/InboxPopup.tsx
CHANGED
|
@@ -34,6 +34,11 @@ export type InboxPopupProps = {
|
|
|
34
34
|
adapter: ChatAdapter;
|
|
35
35
|
uiCallbacks?: ChatUICallbacks;
|
|
36
36
|
theme?: ChatTheme;
|
|
37
|
+
/**
|
|
38
|
+
* Allow-list of footer toolbar keys. Defaults to ["attachment", "emoji", "translate"].
|
|
39
|
+
* Pass e.g. ["attachment", "emoji", "translate", "businessCard"] to opt-in to more.
|
|
40
|
+
*/
|
|
41
|
+
footerActions?: string[];
|
|
37
42
|
};
|
|
38
43
|
|
|
39
44
|
/* ─── Helpers ─── */
|
|
@@ -50,7 +55,7 @@ const avatarBgByInitial: Record<string, string> = {
|
|
|
50
55
|
/* ══════════════════════════════════════════════════
|
|
51
56
|
Component
|
|
52
57
|
══════════════════════════════════════════════════ */
|
|
53
|
-
const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme }) => {
|
|
58
|
+
const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme, footerActions }) => {
|
|
54
59
|
const { close, selectThread, selectedThreadId, reference } = useChatUI();
|
|
55
60
|
const { isOpen: isGalleryOpen, closeGallery } = useGallery();
|
|
56
61
|
|
|
@@ -337,6 +342,7 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
|
|
|
337
342
|
replyTo={replyTo}
|
|
338
343
|
clearReply={() => setReplyTo(undefined)}
|
|
339
344
|
onAfterSend={() => setRev((v) => v + 1)}
|
|
345
|
+
enabledActions={footerActions}
|
|
340
346
|
onSend={(payload) => {
|
|
341
347
|
if (activeId) adapter.messages.send(activeId, payload);
|
|
342
348
|
}}
|
package/src/chat/SinglePopup.tsx
CHANGED
|
@@ -55,12 +55,17 @@ export type SinglePopupProps = {
|
|
|
55
55
|
adapter: ChatAdapter;
|
|
56
56
|
uiCallbacks?: ChatUICallbacks;
|
|
57
57
|
theme?: ChatTheme;
|
|
58
|
+
/**
|
|
59
|
+
* Allow-list of footer toolbar keys. Defaults to ["attachment", "emoji", "translate"].
|
|
60
|
+
* Pass e.g. ["attachment", "emoji", "translate", "addressCard"] to add location sharing.
|
|
61
|
+
*/
|
|
62
|
+
footerActions?: string[];
|
|
58
63
|
};
|
|
59
64
|
|
|
60
65
|
/* ══════════════════════════════════════════════════
|
|
61
66
|
Component
|
|
62
67
|
══════════════════════════════════════════════════ */
|
|
63
|
-
const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme }) => {
|
|
68
|
+
const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme, footerActions }) => {
|
|
64
69
|
const { close, reference } = useChatUI();
|
|
65
70
|
const { isOpen: isGalleryOpen, closeGallery } = useGallery();
|
|
66
71
|
|
|
@@ -258,6 +263,7 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme }
|
|
|
258
263
|
replyTo={replyTo}
|
|
259
264
|
clearReply={() => setReplyTo(undefined)}
|
|
260
265
|
onAfterSend={handleAfterSend}
|
|
266
|
+
enabledActions={footerActions}
|
|
261
267
|
onSend={(payload) => {
|
|
262
268
|
if (activeId) adapter.messages.send(activeId, payload);
|
|
263
269
|
}}
|
package/src/ui/ChatFooter.tsx
CHANGED
|
@@ -39,9 +39,26 @@ type Props = {
|
|
|
39
39
|
/** Called after send completes — triggers parent re-render / scroll */
|
|
40
40
|
onAfterSend?: () => void;
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Allow-list of toolbar action keys to show.
|
|
44
|
+
* If omitted, defaults to ["attachment", "emoji", "translate"].
|
|
45
|
+
*
|
|
46
|
+
* Available keys:
|
|
47
|
+
* "attachment" — 📎 Attach file / image
|
|
48
|
+
* "emoji" — 😊 Emoji picker
|
|
49
|
+
* "businessCard" — 👤 Share business card (opt-in)
|
|
50
|
+
* "addressCard" — 📍 Share address card (opt-in)
|
|
51
|
+
* "translate" — 🌐 Translation settings
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // InboxPopup: no location, no business card
|
|
55
|
+
* enabledActions={["attachment", "emoji", "translate"]}
|
|
56
|
+
*
|
|
57
|
+
* // SinglePopup: add location button
|
|
58
|
+
* enabledActions={["attachment", "emoji", "translate", "addressCard"]}
|
|
59
|
+
*/
|
|
60
|
+
enabledActions?: string[];
|
|
61
|
+
|
|
45
62
|
className?: string;
|
|
46
63
|
maxRows?: number;
|
|
47
64
|
|
|
@@ -50,6 +67,13 @@ type Props = {
|
|
|
50
67
|
clearReply?: () => void;
|
|
51
68
|
};
|
|
52
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Default toolbar actions when `enabledActions` is not specified.
|
|
72
|
+
* businessCard and addressCard are opt-in (not shown by default).
|
|
73
|
+
*/
|
|
74
|
+
export const DEFAULT_FOOTER_ACTIONS: string[] = ["attachment", "emoji", "translate"];
|
|
75
|
+
|
|
76
|
+
|
|
53
77
|
/* ────────────────────────── Utilities ─────────────────────────── */
|
|
54
78
|
|
|
55
79
|
const fmtTime = (s: number) => `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`;
|
|
@@ -69,36 +93,22 @@ const ChatFooter: React.FC<Props> = ({
|
|
|
69
93
|
onAfterSend,
|
|
70
94
|
className,
|
|
71
95
|
maxRows = 4,
|
|
72
|
-
|
|
73
|
-
actions: _actions,
|
|
74
|
-
hiddenActionKeys = [],
|
|
96
|
+
enabledActions,
|
|
75
97
|
replyTo,
|
|
76
98
|
clearReply,
|
|
77
99
|
}) => {
|
|
78
100
|
const allActions: FooterAction[] = [
|
|
79
101
|
{ key: "attachment", title: "Attach file", icon: <AttachIcon className="h-4 w-4" /> },
|
|
80
|
-
{ key: "emoji",
|
|
81
|
-
{
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
icon: <ProfileCardIcon className="h-4 w-4" />,
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
key: "addressCard",
|
|
88
|
-
title: "Share address",
|
|
89
|
-
icon: <MapIcon2 className="h-4 w-4" />,
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
key: "translate",
|
|
93
|
-
title: "Translation settings",
|
|
94
|
-
icon: <NewLanguageIcon className="h-4 w-4" />,
|
|
95
|
-
},
|
|
102
|
+
{ key: "emoji", title: "Add emoji", icon: <SmileIcon className="h-4 w-4" /> },
|
|
103
|
+
{ key: "businessCard", title: "Share business card", icon: <ProfileCardIcon className="h-4 w-4" /> },
|
|
104
|
+
{ key: "addressCard", title: "Share address", icon: <MapIcon2 className="h-4 w-4" /> },
|
|
105
|
+
{ key: "translate", title: "Translation settings", icon: <NewLanguageIcon className="h-4 w-4" /> },
|
|
96
106
|
];
|
|
97
107
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
// Allow-list filter: show only explicitly requested keys.
|
|
109
|
+
// Falls back to DEFAULT_FOOTER_ACTIONS when enabledActions is not provided.
|
|
110
|
+
const allowed = enabledActions ?? DEFAULT_FOOTER_ACTIONS;
|
|
111
|
+
const actionData = allActions.filter((a) => allowed.includes(a.key));
|
|
102
112
|
|
|
103
113
|
const textRef = useRef<HTMLTextAreaElement>(null);
|
|
104
114
|
const emojiBtnRef = useRef<HTMLButtonElement | null>(null);
|