@banbox/chat 1.0.11 → 1.0.13
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/README.md +403 -88
- package/dist/index.cjs +15 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -27
- package/dist/index.d.ts +39 -27
- package/dist/index.js +15 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chat/ChatRoot.tsx +23 -21
- package/src/chat/InboxPopup.tsx +7 -4
- package/src/chat/SinglePopup.tsx +7 -4
- package/src/ui/ChatFooter.tsx +36 -26
package/package.json
CHANGED
package/src/chat/ChatRoot.tsx
CHANGED
|
@@ -11,41 +11,44 @@ import SinglePopup from "./SinglePopup";
|
|
|
11
11
|
import type { ChatTheme } from "./InboxPopup";
|
|
12
12
|
|
|
13
13
|
export type ChatRootProps = {
|
|
14
|
-
/**
|
|
15
|
-
* The unified data adapter — provides all threads, messages, and send logic.
|
|
16
|
-
*/
|
|
14
|
+
/** The unified data adapter — provides all threads, messages, and send logic. */
|
|
17
15
|
adapter: ChatAdapter;
|
|
18
16
|
|
|
17
|
+
/** Optional UI callbacks — toast, navigation, kebab menu. */
|
|
18
|
+
uiCallbacks?: ChatUICallbacks;
|
|
19
|
+
|
|
19
20
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
21
|
+
* Visual theme: "marketplace" | "admin" | { primary, primaryActive, surfaceLow }
|
|
22
|
+
* @example <ChatRoot adapter={adapter} theme="admin" />
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
|
+
theme?: ChatTheme;
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Allow-list of toolbar buttons shown in the **InboxPopup** footer.
|
|
28
|
+
*
|
|
29
|
+
* Keys: "attachment" | "emoji" | "businessCard" | "addressCard" | "translate"
|
|
30
|
+
* Default: ["attachment", "emoji", "translate"]
|
|
30
31
|
*
|
|
31
32
|
* @example
|
|
32
|
-
*
|
|
33
|
+
* // Seller inbox — no cards at all:
|
|
34
|
+
* inboxFooterActions={["attachment", "emoji", "translate"]}
|
|
33
35
|
*/
|
|
34
|
-
|
|
36
|
+
inboxFooterActions?: string[];
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
|
-
*
|
|
39
|
+
* Allow-list of toolbar buttons shown in the **SinglePopup** footer.
|
|
38
40
|
*
|
|
39
|
-
*
|
|
41
|
+
* Keys: "attachment" | "emoji" | "businessCard" | "addressCard" | "translate"
|
|
42
|
+
* Default: ["attachment", "emoji", "translate"]
|
|
40
43
|
*
|
|
41
44
|
* @example
|
|
42
|
-
* //
|
|
43
|
-
*
|
|
45
|
+
* // Single chat with location sharing enabled:
|
|
46
|
+
* singleFooterActions={["attachment", "emoji", "translate", "addressCard"]}
|
|
44
47
|
*/
|
|
45
|
-
|
|
48
|
+
singleFooterActions?: string[];
|
|
46
49
|
};
|
|
47
50
|
|
|
48
|
-
export default function ChatRoot({ adapter, uiCallbacks, theme,
|
|
51
|
+
export default function ChatRoot({ adapter, uiCallbacks, theme, inboxFooterActions, singleFooterActions }: ChatRootProps) {
|
|
49
52
|
const { isOpen, variant } = useChatUI();
|
|
50
53
|
|
|
51
54
|
// Lock page scroll whenever the chat is open
|
|
@@ -56,7 +59,6 @@ export default function ChatRoot({ adapter, uiCallbacks, theme, hiddenActionKeys
|
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
return createPortal(
|
|
59
|
-
// GalleryProvider is scoped to the chat only.
|
|
60
62
|
<GalleryProvider>
|
|
61
63
|
<AnimatePresence mode="wait">
|
|
62
64
|
{isOpen && (
|
|
@@ -66,7 +68,7 @@ export default function ChatRoot({ adapter, uiCallbacks, theme, hiddenActionKeys
|
|
|
66
68
|
adapter={adapter}
|
|
67
69
|
uiCallbacks={uiCallbacks}
|
|
68
70
|
theme={theme}
|
|
69
|
-
|
|
71
|
+
footerActions={inboxFooterActions}
|
|
70
72
|
/>
|
|
71
73
|
) : (
|
|
72
74
|
<SinglePopup
|
|
@@ -74,7 +76,7 @@ export default function ChatRoot({ adapter, uiCallbacks, theme, hiddenActionKeys
|
|
|
74
76
|
adapter={adapter}
|
|
75
77
|
uiCallbacks={uiCallbacks}
|
|
76
78
|
theme={theme}
|
|
77
|
-
|
|
79
|
+
footerActions={singleFooterActions}
|
|
78
80
|
/>
|
|
79
81
|
)
|
|
80
82
|
)}
|
package/src/chat/InboxPopup.tsx
CHANGED
|
@@ -34,8 +34,11 @@ export type InboxPopupProps = {
|
|
|
34
34
|
adapter: ChatAdapter;
|
|
35
35
|
uiCallbacks?: ChatUICallbacks;
|
|
36
36
|
theme?: ChatTheme;
|
|
37
|
-
/**
|
|
38
|
-
|
|
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[];
|
|
39
42
|
};
|
|
40
43
|
|
|
41
44
|
/* ─── Helpers ─── */
|
|
@@ -52,7 +55,7 @@ const avatarBgByInitial: Record<string, string> = {
|
|
|
52
55
|
/* ══════════════════════════════════════════════════
|
|
53
56
|
Component
|
|
54
57
|
══════════════════════════════════════════════════ */
|
|
55
|
-
const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme,
|
|
58
|
+
const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme, footerActions }) => {
|
|
56
59
|
const { close, selectThread, selectedThreadId, reference } = useChatUI();
|
|
57
60
|
const { isOpen: isGalleryOpen, closeGallery } = useGallery();
|
|
58
61
|
|
|
@@ -339,7 +342,7 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme, hi
|
|
|
339
342
|
replyTo={replyTo}
|
|
340
343
|
clearReply={() => setReplyTo(undefined)}
|
|
341
344
|
onAfterSend={() => setRev((v) => v + 1)}
|
|
342
|
-
|
|
345
|
+
enabledActions={footerActions}
|
|
343
346
|
onSend={(payload) => {
|
|
344
347
|
if (activeId) adapter.messages.send(activeId, payload);
|
|
345
348
|
}}
|
package/src/chat/SinglePopup.tsx
CHANGED
|
@@ -55,14 +55,17 @@ export type SinglePopupProps = {
|
|
|
55
55
|
adapter: ChatAdapter;
|
|
56
56
|
uiCallbacks?: ChatUICallbacks;
|
|
57
57
|
theme?: ChatTheme;
|
|
58
|
-
/**
|
|
59
|
-
|
|
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[];
|
|
60
63
|
};
|
|
61
64
|
|
|
62
65
|
/* ══════════════════════════════════════════════════
|
|
63
66
|
Component
|
|
64
67
|
══════════════════════════════════════════════════ */
|
|
65
|
-
const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme,
|
|
68
|
+
const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme, footerActions }) => {
|
|
66
69
|
const { close, reference } = useChatUI();
|
|
67
70
|
const { isOpen: isGalleryOpen, closeGallery } = useGallery();
|
|
68
71
|
|
|
@@ -260,7 +263,7 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme,
|
|
|
260
263
|
replyTo={replyTo}
|
|
261
264
|
clearReply={() => setReplyTo(undefined)}
|
|
262
265
|
onAfterSend={handleAfterSend}
|
|
263
|
-
|
|
266
|
+
enabledActions={footerActions}
|
|
264
267
|
onSend={(payload) => {
|
|
265
268
|
if (activeId) adapter.messages.send(activeId, payload);
|
|
266
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);
|