@banbox/chat 1.0.11 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@banbox/chat",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Banbox Chat UI components — reusable across all Banbox React/Next.js projects",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -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
- * Optional UI callbacks controls toast notifications, navigation,
21
- * and the kebab (⋮) menu renderer.
21
+ * Visual theme: "marketplace" | "admin" | { primary, primaryActive, surfaceLow }
22
+ * @example <ChatRoot adapter={adapter} theme="admin" />
22
23
  */
23
- uiCallbacks?: ChatUICallbacks;
24
+ theme?: ChatTheme;
24
25
 
25
26
  /**
26
- * Visual theme:
27
- * - "marketplace" (default) — orange primary (#ff5300)
28
- * - "admin" black primary (#1a1a1a)
29
- * - custom object — { primary, primaryActive, surfaceLow }
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
- * <ChatRoot adapter={adapter} theme="admin" />
33
+ * // Seller inbox — no cards at all:
34
+ * inboxFooterActions={["attachment", "emoji", "translate"]}
33
35
  */
34
- theme?: ChatTheme;
36
+ inboxFooterActions?: string[];
35
37
 
36
38
  /**
37
- * Keys of footer toolbar actions to hide.
39
+ * Allow-list of toolbar buttons shown in the **SinglePopup** footer.
38
40
  *
39
- * Available keys: "attachment" | "emoji" | "businessCard" | "addressCard" | "translate"
41
+ * Keys: "attachment" | "emoji" | "businessCard" | "addressCard" | "translate"
42
+ * Default: ["attachment", "emoji", "translate"]
40
43
  *
41
44
  * @example
42
- * // Hide the Delivery Address button in the seller app:
43
- * <ChatRoot hiddenActionKeys={["addressCard"]} ... />
45
+ * // Single chat with location sharing enabled:
46
+ * singleFooterActions={["attachment", "emoji", "translate", "addressCard"]}
44
47
  */
45
- hiddenActionKeys?: string[];
48
+ singleFooterActions?: string[];
46
49
  };
47
50
 
48
- export default function ChatRoot({ adapter, uiCallbacks, theme, hiddenActionKeys }: ChatRootProps) {
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
- hiddenActionKeys={hiddenActionKeys}
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
- hiddenActionKeys={hiddenActionKeys}
79
+ footerActions={singleFooterActions}
78
80
  />
79
81
  )
80
82
  )}
@@ -34,8 +34,11 @@ export type InboxPopupProps = {
34
34
  adapter: ChatAdapter;
35
35
  uiCallbacks?: ChatUICallbacks;
36
36
  theme?: ChatTheme;
37
- /** Keys of footer toolbar actions to hide. e.g. ["addressCard", "translate"] */
38
- hiddenActionKeys?: string[];
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, hiddenActionKeys }) => {
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
- hiddenActionKeys={hiddenActionKeys}
345
+ enabledActions={footerActions}
343
346
  onSend={(payload) => {
344
347
  if (activeId) adapter.messages.send(activeId, payload);
345
348
  }}
@@ -55,14 +55,17 @@ export type SinglePopupProps = {
55
55
  adapter: ChatAdapter;
56
56
  uiCallbacks?: ChatUICallbacks;
57
57
  theme?: ChatTheme;
58
- /** Keys of footer toolbar actions to hide. e.g. ["addressCard", "translate"] */
59
- hiddenActionKeys?: string[];
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, hiddenActionKeys }) => {
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
- hiddenActionKeys={hiddenActionKeys}
266
+ enabledActions={footerActions}
264
267
  onSend={(payload) => {
265
268
  if (activeId) adapter.messages.send(activeId, payload);
266
269
  }}
@@ -39,9 +39,26 @@ type Props = {
39
39
  /** Called after send completes — triggers parent re-render / scroll */
40
40
  onAfterSend?: () => void;
41
41
 
42
- actions?: FooterAction[];
43
- /** Keys of actions to hide from the toolbar, e.g. ["businessCard", "addressCard"] */
44
- hiddenActionKeys?: string[];
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
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
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", title: "Add emoji", icon: <SmileIcon className="h-4 w-4" /> },
81
- {
82
- key: "businessCard",
83
- title: "Share business card",
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
- // Filter hidden actions
99
- const actionData = hiddenActionKeys.length
100
- ? allActions.filter((a) => !hiddenActionKeys.includes(a.key))
101
- : allActions;
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);