@banbox/chat 1.0.0

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 (52) hide show
  1. package/README.md +215 -0
  2. package/dist/index.cjs +3408 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +556 -0
  5. package/dist/index.d.ts +556 -0
  6. package/dist/index.js +3385 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +83 -0
  9. package/src/adapter/types.ts +146 -0
  10. package/src/chat/ChatImagePreviewModal.tsx +194 -0
  11. package/src/chat/ChatRoot.tsx +67 -0
  12. package/src/chat/InboxPopup.tsx +312 -0
  13. package/src/chat/SinglePopup.tsx +240 -0
  14. package/src/contexts/ChatUIContext.tsx +30 -0
  15. package/src/contexts/ChatUIProvider.tsx +38 -0
  16. package/src/contexts/GalleryContext.tsx +40 -0
  17. package/src/contexts/GalleryProvider.tsx +89 -0
  18. package/src/hooks/useDisableBodyScroll.ts +16 -0
  19. package/src/icons/index.tsx +248 -0
  20. package/src/index.ts +56 -0
  21. package/src/lottie/typingdotanimation2.json +1 -0
  22. package/src/modals/chat/ChatConfirmModal.tsx +104 -0
  23. package/src/modals/chat/ChatTranslateSettingsModal.tsx +180 -0
  24. package/src/types/index.ts +163 -0
  25. package/src/ui/Button.tsx +83 -0
  26. package/src/ui/Portal.tsx +40 -0
  27. package/src/ui/Select.tsx +74 -0
  28. package/src/ui/chat/AttachmentPreviewStrip.tsx +166 -0
  29. package/src/ui/chat/ChatComposerBar.tsx +231 -0
  30. package/src/ui/chat/ChatFooter.tsx +442 -0
  31. package/src/ui/chat/ChatHeader.tsx +24 -0
  32. package/src/ui/chat/ChatIdentity.tsx +145 -0
  33. package/src/ui/chat/ChatInquiryBar.tsx +57 -0
  34. package/src/ui/chat/ChatListHeader.tsx +179 -0
  35. package/src/ui/chat/ChatMessageItem.tsx +214 -0
  36. package/src/ui/chat/ChatScroll.tsx +64 -0
  37. package/src/ui/chat/ChatSpinner.tsx +49 -0
  38. package/src/ui/chat/ChatThreadItem.tsx +140 -0
  39. package/src/ui/chat/MessageHoverActions.tsx +120 -0
  40. package/src/ui/chat/ReplyCard.tsx +217 -0
  41. package/src/ui/chat/TypingIndicator.tsx +93 -0
  42. package/src/ui/chat/drop-up/BusinessCardDropup.tsx +253 -0
  43. package/src/ui/chat/drop-up/EmojiDropup.tsx +132 -0
  44. package/src/ui/chat/message-items/ChatAddressCard.tsx +130 -0
  45. package/src/ui/chat/message-items/ChatBubbleAudio.tsx +209 -0
  46. package/src/ui/chat/message-items/ChatBubbleFiles.tsx +80 -0
  47. package/src/ui/chat/message-items/ChatBubbleImages.tsx +120 -0
  48. package/src/ui/chat/message-items/ChatBubbleText.tsx +16 -0
  49. package/src/ui/chat/message-items/ChatBusinessCard.tsx +95 -0
  50. package/src/ui/chat/scrollToMessage.ts +61 -0
  51. package/src/ui/chat/types.ts +37 -0
  52. package/src/utils/cn.ts +6 -0
@@ -0,0 +1,95 @@
1
+ "use client";
2
+
3
+ import clsx from "clsx";
4
+ import React from "react";
5
+
6
+ import { BusinessInfoIcon, ChatMailIcon, ChatPhoneCallIcon } from "../../../icons";
7
+ import type { BusinessCard } from "../types";
8
+
9
+ // const AVATAR_SIZE = 60; // uncomment if avatar section is re-enabled
10
+
11
+ type Props = {
12
+ mine: boolean;
13
+ card: BusinessCard;
14
+ };
15
+
16
+ const ChatBusinessCard: React.FC<Props> = ({ mine, card }) => {
17
+ // const avatarSrc = card.avatarSrc || "/images/avatar-fallback.png";
18
+
19
+ return (
20
+ <div
21
+ className={clsx(
22
+ "relative h-[208px] w-[355px] overflow-hidden rounded-[12px] bg-white bg-cover bg-no-repeat",
23
+ "shadow-[0_2px_12px_rgba(59,51,51,0.1)]",
24
+ mine ? "ml-auto" : "mr-auto",
25
+ )}
26
+ style={{ backgroundImage: "url('/chat/img/card_bg_raw.svg')" }}
27
+ >
28
+ <div className="flex h-full items-stretch justify-between gap-4 px-6 py-6">
29
+ {/* Left content */}
30
+ <div className="flex min-w-0 flex-1 flex-col justify-between">
31
+ {/* Name + country */}
32
+ <div>
33
+ <h3 className="text-xl font-semibold text-[#004F4F]">{card.name}</h3>
34
+
35
+ <div className="h-px w-[105px] bg-black" />
36
+
37
+ <div className="mt-[6px] flex items-center gap-2">
38
+ <img
39
+ src="https://flagcdn.com/bd.svg"
40
+ alt="Bangladesh flag"
41
+ width={24}
42
+ height={14}
43
+ className="h-[14px] w-6 rounded-xs object-cover"
44
+ loading="lazy"
45
+ />
46
+ <span className="text-xs font-medium text-[#636363]">
47
+ {card.country}
48
+ </span>
49
+ </div>
50
+ </div>
51
+
52
+ {/* Contact details */}
53
+ <div className="mt-4 mb-10 space-y-1.5 text-xs text-black">
54
+ <div className="flex items-center gap-3">
55
+ <div className="flex h-5 w-5 items-center justify-center rounded-full bg-[#FFE9DB]">
56
+ <BusinessInfoIcon className="h-3 w-3 text-[#EA580C]" />
57
+ </div>
58
+ <span className="truncate">{card.company}</span>
59
+ </div>
60
+
61
+ <div className="flex items-center gap-3">
62
+ <div className="flex h-5 w-5 items-center justify-center rounded-full bg-[#FFE9DB]">
63
+ <ChatMailIcon className="h-3 w-3 text-[#EA580C]" />
64
+ </div>
65
+ <span className="truncate">{card.email}</span>
66
+ </div>
67
+
68
+ <div className="flex items-center gap-3">
69
+ <div className="flex h-5 w-5 items-center justify-center rounded-full bg-[#FFE9DB]">
70
+ <ChatPhoneCallIcon className="h-3 w-3 text-[#EA580C]" />
71
+ </div>
72
+ <span className="truncate">{card.phone}</span>
73
+ </div>
74
+ </div>
75
+ </div>
76
+
77
+ {/* Right avatar (kept commented as per original behavior) */}
78
+ {/*
79
+ <div className="flex items-start">
80
+ <img
81
+ src={avatarSrc}
82
+ alt={`${card.name ?? "Contact"} avatar`}
83
+ width={AVATAR_SIZE}
84
+ height={AVATAR_SIZE}
85
+ className="rounded-sm border border-[#f1f1f1] object-cover"
86
+ loading={mine ? "eager" : "lazy"}
87
+ />
88
+ </div>
89
+ */}
90
+ </div>
91
+ </div>
92
+ );
93
+ };
94
+
95
+ export default ChatBusinessCard;
@@ -0,0 +1,61 @@
1
+ // components/chat/ui/chat/scrollToMessage.ts
2
+ export function scrollToMessageById(messageId: string) {
3
+ if (!messageId) {
4
+ return;
5
+ }
6
+
7
+ const target = document.querySelector<HTMLElement>(`[data-msg-id="${messageId}"]`);
8
+ if (!target) {
9
+ return;
10
+ }
11
+
12
+ const scroller = target.closest<HTMLElement>("[data-chat-scroll]");
13
+
14
+ // Smooth center scroll
15
+ if (scroller) {
16
+ const top = target.offsetTop - scroller.clientHeight / 2 + target.clientHeight / 2;
17
+ scroller.scrollTo({ top, behavior: "smooth" });
18
+ } else {
19
+ target.scrollIntoView({ behavior: "smooth", block: "center" });
20
+ }
21
+
22
+ // ── Soft background focus (black @ 5% opacity), no borders ────────────────
23
+ // We inject a transient absolutely-positioned overlay inside the target.
24
+ const cleanupId = `focus-overlay-${Date.now()}`;
25
+ const overlay = document.createElement("div");
26
+ overlay.id = cleanupId;
27
+ Object.assign(overlay.style, {
28
+ position: "absolute",
29
+ inset: "-4px", // tiny spread so it peeks outside rounded corners
30
+ borderRadius: "6px",
31
+ background: "rgba(0,0,0,0.05)", // black / 5% opacity
32
+ pointerEvents: "none",
33
+ zIndex: "1",
34
+ transition: "opacity 220ms ease",
35
+ opacity: "0",
36
+ });
37
+
38
+ // Ensure the target can host an absolutely positioned child
39
+ const prevPos = target.style.position;
40
+ if (getComputedStyle(target).position === "static") {
41
+ target.style.position = "relative";
42
+ }
43
+
44
+ target.appendChild(overlay);
45
+ // fade in
46
+ requestAnimationFrame(() => (overlay.style.opacity = "1"));
47
+
48
+ // Remove highlight after 1.2s with a quick fade-out
49
+ window.setTimeout(() => {
50
+ overlay.style.opacity = "0";
51
+ window.setTimeout(() => {
52
+ overlay.remove();
53
+ // restore inline position if we modified it
54
+ if (!prevPos) {
55
+ target.style.removeProperty("position");
56
+ } else {
57
+ target.style.position = prevPos;
58
+ }
59
+ }, 240);
60
+ }, 1200);
61
+ }
@@ -0,0 +1,37 @@
1
+ // components/chat/ui/chat/types.ts
2
+ import type { ChatAudio, ChatFile } from "./ChatMessageItem";
3
+
4
+ export type MessageRef = {
5
+ id: string;
6
+ author: string | { name: string };
7
+ time?: string;
8
+ text?: string;
9
+ images?: string[];
10
+ files?: ChatFile[];
11
+ audio?: ChatAudio;
12
+ };
13
+
14
+ export type BusinessCard = {
15
+ avatarSrc: string;
16
+ name: string;
17
+ country: string; // e.g. "Bangladesh"
18
+ flag?: string; // emoji or small image URL; default 🏳️ if not given
19
+ company: string;
20
+ email: string;
21
+ phone: string;
22
+ };
23
+
24
+ export type AddressCard = {
25
+ name: string;
26
+ label?: "Home" | "Office";
27
+ businessName?: string;
28
+ mobileNumber: string;
29
+ country: string;
30
+ district?: string | null;
31
+ policeStation?: string | null;
32
+ state?: string | null;
33
+ city?: string | null;
34
+ postalCode?: string;
35
+ addressLine: string;
36
+ landMark?: string | null;
37
+ };
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }