@better-zap/react 0.2.0 → 0.2.2

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 (50) hide show
  1. package/dist/bubble.cjs +93 -0
  2. package/dist/bubble.d.cts +61 -0
  3. package/dist/bubble.d.mts +61 -0
  4. package/dist/bubble.mjs +89 -0
  5. package/dist/composer.cjs +220 -0
  6. package/dist/composer.d.cts +96 -0
  7. package/dist/composer.d.mts +96 -0
  8. package/dist/composer.mjs +211 -0
  9. package/dist/conversation-list-BlhJhDsc.d.cts +100 -0
  10. package/dist/conversation-list-BwQ4oK2J.d.mts +100 -0
  11. package/dist/conversation-list-C0EmReS-.mjs +293 -0
  12. package/dist/conversation-list-CVWdddJh.cjs +311 -0
  13. package/dist/conversation-list.cjs +6 -0
  14. package/dist/conversation-list.d.cts +2 -0
  15. package/dist/conversation-list.d.mts +2 -0
  16. package/dist/conversation-list.mjs +5 -0
  17. package/dist/index.cjs +49 -0
  18. package/dist/index.d.cts +19 -0
  19. package/dist/index.d.mts +19 -0
  20. package/dist/index.mjs +11 -0
  21. package/dist/message-bubble.cjs +116 -0
  22. package/dist/message-bubble.d.cts +38 -0
  23. package/dist/message-bubble.d.mts +38 -0
  24. package/dist/message-bubble.mjs +114 -0
  25. package/dist/message-input-CkaL6fb4.mjs +173 -0
  26. package/dist/message-input-DcEzHQ9t.cjs +191 -0
  27. package/dist/message-input.cjs +6 -0
  28. package/dist/message-input.d.cts +51 -0
  29. package/dist/message-input.d.mts +51 -0
  30. package/dist/message-input.mjs +5 -0
  31. package/dist/message-view.cjs +273 -0
  32. package/dist/message-view.d.cts +109 -0
  33. package/dist/message-view.d.mts +109 -0
  34. package/dist/message-view.mjs +266 -0
  35. package/dist/message.cjs +50 -0
  36. package/dist/message.d.cts +40 -0
  37. package/dist/message.d.mts +40 -0
  38. package/dist/message.mjs +43 -0
  39. package/dist/tailwind.css +33 -0
  40. package/dist/utils-Bp9IahGG.cjs +91 -0
  41. package/dist/utils.cjs +5 -0
  42. package/dist/utils.d.cts +20 -0
  43. package/dist/utils.d.mts +20 -0
  44. package/dist/utils.mjs +45 -0
  45. package/dist/whatsapp-dashboard.cjs +64 -0
  46. package/dist/whatsapp-dashboard.d.cts +33 -0
  47. package/dist/whatsapp-dashboard.d.mts +33 -0
  48. package/dist/whatsapp-dashboard.mjs +60 -0
  49. package/dist/wpp-bg.webp +0 -0
  50. package/package.json +1 -1
@@ -0,0 +1,191 @@
1
+ const require_utils = require("./utils-Bp9IahGG.cjs");
2
+ const require_composer = require("./composer.cjs");
3
+ let react = require("react");
4
+ react = require_utils.__toESM(react);
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+ let _hugeicons_react = require("@hugeicons/react");
7
+ let _hugeicons_core_free_icons = require("@hugeicons/core-free-icons");
8
+ let better_zap = require("better-zap");
9
+ //#region src/use-freeform-message-window.ts
10
+ const CLOSED_WINDOW = {
11
+ isOpen: false,
12
+ lastIncomingMessageAt: null,
13
+ expiresAt: null
14
+ };
15
+ const MAX_TIMEOUT_MS = 2 ** 31 - 1;
16
+ function useFreeformMessageWindow(conversation, messages) {
17
+ const [now, setNow] = (0, react.useState)(() => /* @__PURE__ */ new Date());
18
+ const freeformWindow = conversation ? (0, better_zap.resolveConversationFreeformMessageWindow)(conversation, messages, now) : CLOSED_WINDOW;
19
+ const revalidate = (0, react.useCallback)(() => {
20
+ const freshNow = /* @__PURE__ */ new Date();
21
+ setNow(freshNow);
22
+ if (!conversation) return CLOSED_WINDOW;
23
+ return (0, better_zap.resolveConversationFreeformMessageWindow)(conversation, messages, freshNow);
24
+ }, [conversation, messages]);
25
+ (0, react.useEffect)(() => {
26
+ if (!conversation || !freeformWindow.isOpen || !freeformWindow.expiresAt) return;
27
+ const expiresAtMs = new Date(freeformWindow.expiresAt).getTime();
28
+ if (!Number.isFinite(expiresAtMs)) return;
29
+ const delay = Math.min(Math.max(0, expiresAtMs - Date.now()), MAX_TIMEOUT_MS);
30
+ const timeoutId = globalThis.setTimeout(() => {
31
+ setNow(/* @__PURE__ */ new Date());
32
+ }, delay);
33
+ return () => {
34
+ globalThis.clearTimeout(timeoutId);
35
+ };
36
+ }, [
37
+ conversation,
38
+ freeformWindow.isOpen,
39
+ freeformWindow.expiresAt,
40
+ now
41
+ ]);
42
+ return {
43
+ ...freeformWindow,
44
+ revalidate
45
+ };
46
+ }
47
+ //#endregion
48
+ //#region src/message-input.tsx
49
+ var FreeformWindowClosedError = class extends Error {
50
+ window;
51
+ constructor(freeformWindow) {
52
+ super("Freeform messaging window is closed");
53
+ this.name = "FreeformWindowClosedError";
54
+ this.window = freeformWindow;
55
+ }
56
+ };
57
+ const DEFAULT_LABELS = {
58
+ sending: "Enviando...",
59
+ expired: "A janela de 24h expirou. Mensagens de texto livre só podem ser enviadas dentro de 24h após a última mensagem do contato.",
60
+ emoji: "Emojis",
61
+ attach: "Anexar arquivo",
62
+ send: "Enviar",
63
+ mic: "Gravar áudio",
64
+ textarea: "Mensagem",
65
+ sendFailed: "Falha ao enviar. Tente novamente."
66
+ };
67
+ function MessageInputBody({ labels, placeholder, onEmojiClick, onAttachClick, onMicClick, showDefaultError }) {
68
+ const { isSending, hasText } = require_composer.useComposerState();
69
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
70
+ (onEmojiClick || onAttachClick) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
71
+ className: "flex items-center gap-1 text-[#54656f] shrink-0",
72
+ children: [onEmojiClick && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerButton, {
73
+ "aria-label": labels.emoji,
74
+ title: labels.emoji,
75
+ onClick: onEmojiClick,
76
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
77
+ icon: _hugeicons_core_free_icons.SmileIcon,
78
+ size: 24
79
+ })
80
+ }), onAttachClick && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerButton, {
81
+ "aria-label": labels.attach,
82
+ title: "Anexar",
83
+ onClick: onAttachClick,
84
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
85
+ icon: _hugeicons_core_free_icons.Add01Icon,
86
+ size: 24
87
+ })
88
+ })]
89
+ }),
90
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
91
+ className: "flex-1 flex items-center min-h-[40px] py-2 px-2",
92
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerTextarea, {
93
+ "aria-label": labels.textarea,
94
+ placeholder: isSending ? labels.sending : placeholder
95
+ })
96
+ }),
97
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
98
+ className: "flex items-center pr-1 shrink-0",
99
+ children: hasText ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerSend, { "aria-label": labels.send }) : onMicClick ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerButton, {
100
+ "aria-label": labels.mic,
101
+ className: "text-[#54656f]",
102
+ onClick: onMicClick,
103
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
104
+ icon: _hugeicons_core_free_icons.Mic01Icon,
105
+ size: 24
106
+ })
107
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerSend, { "aria-label": labels.send })
108
+ }),
109
+ showDefaultError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.ComposerError, {
110
+ className: "px-2 pb-1 text-[13px] leading-[18px] text-red-600",
111
+ children: labels.sendFailed
112
+ })
113
+ ] });
114
+ }
115
+ function MessageInput({ onSend, conversation, messages, disabled, placeholder = "Digite uma mensagem", className, contextWindowOpen = true, onSendError, onEmojiClick, onAttachClick, onMicClick, labels: labelsProp }) {
116
+ const labels = {
117
+ ...DEFAULT_LABELS,
118
+ ...labelsProp
119
+ };
120
+ const freeformFromHook = useFreeformMessageWindow(conversation, messages);
121
+ const freeformMessageWindow = conversation ? freeformFromHook : {
122
+ isOpen: contextWindowOpen,
123
+ lastIncomingMessageAt: null,
124
+ expiresAt: null,
125
+ revalidate: () => ({
126
+ isOpen: contextWindowOpen,
127
+ lastIncomingMessageAt: null,
128
+ expiresAt: null
129
+ })
130
+ };
131
+ const isContextWindowOpen = freeformMessageWindow.isOpen;
132
+ const revalidate = freeformMessageWindow.revalidate;
133
+ const handleSubmit = (0, react.useCallback)(async (text) => {
134
+ const windowState = revalidate();
135
+ if (!windowState.isOpen) throw new FreeformWindowClosedError(windowState);
136
+ return onSend(text);
137
+ }, [onSend, revalidate]);
138
+ const handleError = (0, react.useCallback)((error, context) => {
139
+ onSendError?.(error, context);
140
+ }, [onSendError]);
141
+ const showDefaultError = !(onSendError != null && labelsProp?.sendFailed === "");
142
+ if (!isContextWindowOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
143
+ className: require_utils.cn("w-full flex flex-col px-4 py-3 z-10 shrink-0", className),
144
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
145
+ className: "flex items-center gap-3 px-4 py-3 min-h-[52px] bg-[#ffeecd] rounded-[7.5px] shadow-[0_1px_0.5px_rgba(11,20,26,0.13)]",
146
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
147
+ icon: _hugeicons_core_free_icons.Clock01Icon,
148
+ size: 20,
149
+ className: "text-[#54656f] shrink-0"
150
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
151
+ className: "text-[13px] leading-[18px] text-[#54656f]",
152
+ children: labels.expired
153
+ })]
154
+ })
155
+ });
156
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
157
+ className: require_utils.cn("w-full flex flex-col px-4 py-3 z-10 shrink-0", className),
158
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_composer.Composer, {
159
+ onSubmit: handleSubmit,
160
+ onError: handleError,
161
+ disabled,
162
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageInputBody, {
163
+ labels,
164
+ placeholder,
165
+ onEmojiClick,
166
+ onAttachClick,
167
+ onMicClick,
168
+ showDefaultError
169
+ })
170
+ })
171
+ });
172
+ }
173
+ //#endregion
174
+ Object.defineProperty(exports, "FreeformWindowClosedError", {
175
+ enumerable: true,
176
+ get: function() {
177
+ return FreeformWindowClosedError;
178
+ }
179
+ });
180
+ Object.defineProperty(exports, "MessageInput", {
181
+ enumerable: true,
182
+ get: function() {
183
+ return MessageInput;
184
+ }
185
+ });
186
+ Object.defineProperty(exports, "useFreeformMessageWindow", {
187
+ enumerable: true,
188
+ get: function() {
189
+ return useFreeformMessageWindow;
190
+ }
191
+ });
@@ -0,0 +1,6 @@
1
+ "use client";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ require("./composer.cjs");
4
+ const require_message_input = require("./message-input-DcEzHQ9t.cjs");
5
+ exports.FreeformWindowClosedError = require_message_input.FreeformWindowClosedError;
6
+ exports.MessageInput = require_message_input.MessageInput;
@@ -0,0 +1,51 @@
1
+ import React from "react";
2
+ import { Conversation, FreeformMessageWindow, UIMessage } from "better-zap";
3
+
4
+ //#region src/message-input.d.ts
5
+ declare class FreeformWindowClosedError extends Error {
6
+ readonly window: FreeformMessageWindow;
7
+ constructor(freeformWindow: FreeformMessageWindow);
8
+ }
9
+ interface MessageInputLabels {
10
+ sending: string;
11
+ expired: string;
12
+ emoji: string;
13
+ attach: string;
14
+ send: string;
15
+ mic: string;
16
+ textarea: string;
17
+ sendFailed: string;
18
+ }
19
+ interface MessageInputProps {
20
+ onSend: (text: string) => void | Promise<void>;
21
+ conversation?: Conversation | null;
22
+ messages?: UIMessage[];
23
+ disabled?: boolean;
24
+ placeholder?: string;
25
+ className?: string;
26
+ /** @deprecated Prefer `conversation` (and optional `messages`) so the library owns window state. */
27
+ contextWindowOpen?: boolean;
28
+ onSendError?: (error: unknown, context: {
29
+ text: string;
30
+ }) => void;
31
+ onEmojiClick?: () => void;
32
+ onAttachClick?: () => void;
33
+ onMicClick?: () => void;
34
+ labels?: Partial<MessageInputLabels>;
35
+ }
36
+ declare function MessageInput({
37
+ onSend,
38
+ conversation,
39
+ messages,
40
+ disabled,
41
+ placeholder,
42
+ className,
43
+ contextWindowOpen,
44
+ onSendError,
45
+ onEmojiClick,
46
+ onAttachClick,
47
+ onMicClick,
48
+ labels: labelsProp
49
+ }: MessageInputProps): React.JSX.Element;
50
+ //#endregion
51
+ export { FreeformWindowClosedError, MessageInput, MessageInputLabels, MessageInputProps };
@@ -0,0 +1,51 @@
1
+ import React from "react";
2
+ import { Conversation, FreeformMessageWindow, UIMessage } from "better-zap";
3
+
4
+ //#region src/message-input.d.ts
5
+ declare class FreeformWindowClosedError extends Error {
6
+ readonly window: FreeformMessageWindow;
7
+ constructor(freeformWindow: FreeformMessageWindow);
8
+ }
9
+ interface MessageInputLabels {
10
+ sending: string;
11
+ expired: string;
12
+ emoji: string;
13
+ attach: string;
14
+ send: string;
15
+ mic: string;
16
+ textarea: string;
17
+ sendFailed: string;
18
+ }
19
+ interface MessageInputProps {
20
+ onSend: (text: string) => void | Promise<void>;
21
+ conversation?: Conversation | null;
22
+ messages?: UIMessage[];
23
+ disabled?: boolean;
24
+ placeholder?: string;
25
+ className?: string;
26
+ /** @deprecated Prefer `conversation` (and optional `messages`) so the library owns window state. */
27
+ contextWindowOpen?: boolean;
28
+ onSendError?: (error: unknown, context: {
29
+ text: string;
30
+ }) => void;
31
+ onEmojiClick?: () => void;
32
+ onAttachClick?: () => void;
33
+ onMicClick?: () => void;
34
+ labels?: Partial<MessageInputLabels>;
35
+ }
36
+ declare function MessageInput({
37
+ onSend,
38
+ conversation,
39
+ messages,
40
+ disabled,
41
+ placeholder,
42
+ className,
43
+ contextWindowOpen,
44
+ onSendError,
45
+ onEmojiClick,
46
+ onAttachClick,
47
+ onMicClick,
48
+ labels: labelsProp
49
+ }: MessageInputProps): React.JSX.Element;
50
+ //#endregion
51
+ export { FreeformWindowClosedError, MessageInput, MessageInputLabels, MessageInputProps };
@@ -0,0 +1,5 @@
1
+ "use client";
2
+ import "./utils.mjs";
3
+ import "./composer.mjs";
4
+ import { n as MessageInput, t as FreeformWindowClosedError } from "./message-input-CkaL6fb4.mjs";
5
+ export { FreeformWindowClosedError, MessageInput };
@@ -0,0 +1,273 @@
1
+ "use client";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const require_utils = require("./utils-Bp9IahGG.cjs");
4
+ const require_whatsapp_dashboard = require("./whatsapp-dashboard.cjs");
5
+ const require_message_bubble = require("./message-bubble.cjs");
6
+ let react = require("react");
7
+ react = require_utils.__toESM(react);
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ let _hugeicons_react = require("@hugeicons/react");
10
+ let _hugeicons_core_free_icons = require("@hugeicons/core-free-icons");
11
+ let _legendapp_list_react = require("@legendapp/list/react");
12
+ //#region src/message-view.tsx
13
+ function MessageView({ children, className, style, ...props }) {
14
+ const ctx = require_whatsapp_dashboard.useOptionalWhatsappDashboard();
15
+ const isMobile = ctx?.isMobile ?? false;
16
+ const mobileView = ctx?.mobileView ?? "chat";
17
+ const hasContent = react.default.Children.count(children) > 0;
18
+ const isVisible = !isMobile || mobileView === "chat";
19
+ if (!hasContent) {
20
+ if (isMobile) return null;
21
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageViewEmpty, {
22
+ className,
23
+ style,
24
+ ...props
25
+ });
26
+ }
27
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
28
+ className: require_utils.cn("relative flex flex-1 flex-col bg-[#efeae2]", className),
29
+ style: {
30
+ ...style,
31
+ ...isVisible ? null : { display: "none" }
32
+ },
33
+ ...props,
34
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
35
+ position: "absolute",
36
+ backgroundImage: `url(${new URL("./wpp-bg.webp", require("url").pathToFileURL(__filename).href).href})`,
37
+ backgroundRepeat: "repeat",
38
+ opacity: .15,
39
+ inset: 0
40
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
41
+ className: "relative flex flex-1 flex-col min-h-0",
42
+ children
43
+ })]
44
+ });
45
+ }
46
+ const DEFAULT_HEADER_LABELS = {
47
+ back: "Voltar",
48
+ info: "Informações"
49
+ };
50
+ const iconButtonClassName = "inline-flex h-10 w-10 items-center justify-center rounded-full text-[#54656f] hover:bg-black/5 focus-visible:outline-2 focus-visible:outline-offset-2";
51
+ function MessageViewHeader({ conversation, onBack, onInfoClick, showBackButton, actions, labels: labelsProp, children, className, ...props }) {
52
+ const ctx = require_whatsapp_dashboard.useOptionalWhatsappDashboard();
53
+ const labels = {
54
+ ...DEFAULT_HEADER_LABELS,
55
+ ...labelsProp
56
+ };
57
+ const showBack = showBackButton ?? ctx?.isMobile ?? false;
58
+ const handleBack = () => {
59
+ ctx?.setMobileView("list");
60
+ onBack?.();
61
+ };
62
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
63
+ className: require_utils.cn("flex h-[59px] shrink-0 items-center justify-between bg-[#f0f2f5] px-4 z-20", className),
64
+ ...props,
65
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
66
+ className: "flex items-center gap-3",
67
+ children: [showBack && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
68
+ type: "button",
69
+ "aria-label": labels.back,
70
+ className: iconButtonClassName,
71
+ onClick: handleBack,
72
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
73
+ icon: _hugeicons_core_free_icons.ArrowLeft02Icon,
74
+ size: 20
75
+ })
76
+ }), children ?? (conversation ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
77
+ className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[#dfe5e7]",
78
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
79
+ icon: _hugeicons_core_free_icons.UserIcon,
80
+ size: 24,
81
+ className: "text-white"
82
+ })
83
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
84
+ className: "flex flex-col",
85
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
86
+ className: "text-[16px] font-medium text-[#111b21] leading-tight",
87
+ children: conversation.contactName || conversation.phone
88
+ }), conversation.contactName && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
89
+ className: "text-[13px] text-[#667781] leading-tight",
90
+ children: conversation.phone
91
+ })]
92
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "flex flex-col" }))]
93
+ }), actions ?? (onInfoClick ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
94
+ type: "button",
95
+ "aria-label": labels.info,
96
+ className: iconButtonClassName,
97
+ onClick: onInfoClick,
98
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
99
+ icon: _hugeicons_core_free_icons.InformationCircleIcon,
100
+ size: 20
101
+ })
102
+ }) : null)]
103
+ });
104
+ }
105
+ const MessageViewScrollContext = react.default.createContext(null);
106
+ function MessageViewContent({ children, autoScroll = true, onScrollTop, className, ...props }) {
107
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageViewScrollContext.Provider, {
108
+ value: {
109
+ autoScroll,
110
+ onScrollTop
111
+ },
112
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
113
+ className: require_utils.cn("flex min-h-0 flex-1 flex-col p-4 pb-0", className),
114
+ ...props,
115
+ children
116
+ })
117
+ });
118
+ }
119
+ const MemoizedMessageBubble = react.default.memo(require_message_bubble.MessageBubble);
120
+ const MemoizedDateDivider = react.default.memo(DateDivider);
121
+ function defaultFormatTime(isoDate) {
122
+ return new Date(isoDate).toLocaleTimeString("pt-BR", {
123
+ hour: "2-digit",
124
+ minute: "2-digit"
125
+ });
126
+ }
127
+ function sameGroup(a, b, resolveFormatDate) {
128
+ return resolveFormatDate(a.sentAt) === resolveFormatDate(b.sentAt) && a.direction === b.direction;
129
+ }
130
+ function getGroupPosition(messages, index, resolveFormatDate) {
131
+ const n = messages.length;
132
+ const prevSame = index > 0 && sameGroup(messages[index - 1], messages[index], resolveFormatDate);
133
+ const nextSame = index < n - 1 && sameGroup(messages[index], messages[index + 1], resolveFormatDate);
134
+ if (!prevSame && !nextSame) return "single";
135
+ if (!prevSame && nextSame) return "first";
136
+ if (prevSame && nextSame) return "middle";
137
+ return "last";
138
+ }
139
+ function MessageList({ messages, renderMessage, renderDateDivider, formatDate, formatTime, renderMessageLabel, autoScroll: autoScrollProp, onScrollTop: onScrollTopProp, className }) {
140
+ const scrollContext = (0, react.useContext)(MessageViewScrollContext);
141
+ const resolvedAutoScroll = autoScrollProp ?? scrollContext?.autoScroll ?? true;
142
+ const resolvedOnScrollTop = onScrollTopProp ?? scrollContext?.onScrollTop;
143
+ const resolveFormatDate = formatDate ?? require_utils.getDisplayDate;
144
+ const resolveFormatTime = formatTime ?? defaultFormatTime;
145
+ const { items, stickyHeaderIndices } = (0, react.useMemo)(() => {
146
+ const itemsNew = [];
147
+ const stickyHeaderIndicesNew = [];
148
+ let currentLabel = null;
149
+ const labelCounts = /* @__PURE__ */ new Map();
150
+ messages.forEach((msg, index) => {
151
+ const label = resolveFormatDate(msg.sentAt);
152
+ if (currentLabel !== label) {
153
+ currentLabel = label;
154
+ stickyHeaderIndicesNew.push(itemsNew.length);
155
+ const seen = labelCounts.get(label) ?? 0;
156
+ labelCounts.set(label, seen + 1);
157
+ const id = seen === 0 ? `date:${label}` : `date:${label}:${seen}`;
158
+ itemsNew.push({
159
+ type: "date",
160
+ id,
161
+ label,
162
+ date: msg.sentAt
163
+ });
164
+ }
165
+ itemsNew.push({
166
+ type: "message",
167
+ id: msg.id,
168
+ message: msg,
169
+ groupPosition: getGroupPosition(messages, index, resolveFormatDate)
170
+ });
171
+ });
172
+ return {
173
+ items: itemsNew,
174
+ stickyHeaderIndices: stickyHeaderIndicesNew
175
+ };
176
+ }, [messages, resolveFormatDate]);
177
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react.LegendList, {
178
+ alignItemsAtEnd: true,
179
+ className: require_utils.cn("chat-scrollbar", className),
180
+ contentContainerStyle: { paddingBottom: 16 },
181
+ data: items,
182
+ estimatedItemSize: 72,
183
+ getItemType: (item) => item.type,
184
+ initialScrollAtEnd: resolvedAutoScroll,
185
+ keyExtractor: (item) => item.id,
186
+ maintainScrollAtEnd: resolvedAutoScroll,
187
+ maintainVisibleContentPosition: true,
188
+ onStartReached: resolvedOnScrollTop ? () => resolvedOnScrollTop() : void 0,
189
+ onStartReachedThreshold: .1,
190
+ recycleItems: true,
191
+ renderItem: ({ item }) => {
192
+ if (item.type === "date") return renderDateDivider?.({
193
+ label: item.label,
194
+ date: item.date
195
+ }) ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedDateDivider, { children: item.label });
196
+ const { message, groupPosition } = item;
197
+ const label = renderMessageLabel?.(message);
198
+ const direction = message.direction;
199
+ const align = direction === "incoming" ? "start" : "end";
200
+ const ctx = {
201
+ message,
202
+ id: message.id,
203
+ direction,
204
+ align,
205
+ groupPosition,
206
+ label
207
+ };
208
+ return renderMessage?.(ctx) ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedMessageBubble, {
209
+ content: message.content || "",
210
+ sender: direction === "incoming" ? "user" : "bot",
211
+ timestamp: resolveFormatTime(message.sentAt),
212
+ status: message.status,
213
+ templateName: message.templateName || void 0,
214
+ label,
215
+ groupPosition
216
+ });
217
+ },
218
+ stickyHeaderIndices,
219
+ style: {
220
+ height: "100%",
221
+ minHeight: 0
222
+ }
223
+ });
224
+ }
225
+ function DateDivider({ children, className, ...props }) {
226
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
227
+ className: require_utils.cn("sticky top-0 z-10 flex justify-center w-full py-2 pointer-events-none", className),
228
+ ...props,
229
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
230
+ className: "bg-white shadow-[0_1px_0.5px_rgba(11,20,26,0.13)] text-[#54656f] text-[12.5px] font-medium px-3 py-[5px] rounded-[7.5px] uppercase pointer-events-auto",
231
+ children
232
+ })
233
+ });
234
+ }
235
+ function MessageViewEmpty({ className, children, ...props }) {
236
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
237
+ className: require_utils.cn("relative flex flex-1 flex-col items-center justify-center bg-[#f0f2f5] text-[#667781] p-6 border-b-[6px] border-[#25d366]", className),
238
+ ...props,
239
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
240
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
241
+ icon: _hugeicons_core_free_icons.Message01Icon,
242
+ size: 96,
243
+ strokeWidth: 1,
244
+ className: "mb-6 text-[#c6cdd2]"
245
+ }),
246
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
247
+ className: "mb-4 text-[32px] font-light text-[#41525d]",
248
+ children: "Better Zap"
249
+ }),
250
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
251
+ className: "max-w-md space-y-3 text-center",
252
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
253
+ className: "text-sm leading-[20px]",
254
+ children: [
255
+ "Esta é uma interface dedicada para visualização e monitoramento de mensagens da ",
256
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "API Oficial do WhatsApp" }),
257
+ "."
258
+ ]
259
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
260
+ className: "text-sm leading-[20px] opacity-80",
261
+ children: "Acompanhe o histórico de conversas, verifique o status de entrega e gerencie as interações do Cloud API de forma profissional."
262
+ })]
263
+ })
264
+ ] })
265
+ });
266
+ }
267
+ //#endregion
268
+ exports.DateDivider = DateDivider;
269
+ exports.MessageList = MessageList;
270
+ exports.MessageView = MessageView;
271
+ exports.MessageViewContent = MessageViewContent;
272
+ exports.MessageViewEmpty = MessageViewEmpty;
273
+ exports.MessageViewHeader = MessageViewHeader;
@@ -0,0 +1,109 @@
1
+ import React from "react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+ import { Conversation, UIMessage } from "better-zap";
4
+
5
+ //#region src/message-view.d.ts
6
+ interface MessageViewProps extends React.ComponentProps<"div"> {
7
+ children?: React.ReactNode;
8
+ }
9
+ declare function MessageView({
10
+ children,
11
+ className,
12
+ style,
13
+ ...props
14
+ }: MessageViewProps): react_jsx_runtime0.JSX.Element | null;
15
+ interface MessageViewHeaderLabels {
16
+ back: string;
17
+ info: string;
18
+ }
19
+ interface MessageViewHeaderProps extends React.ComponentProps<"div"> {
20
+ conversation?: Conversation;
21
+ onBack?: () => void;
22
+ onInfoClick?: () => void;
23
+ /** Force the back button outside dashboard context. Default: ctx?.isMobile ?? false */
24
+ showBackButton?: boolean;
25
+ /** Replaces the right-side default info button entirely. */
26
+ actions?: React.ReactNode;
27
+ labels?: Partial<MessageViewHeaderLabels>;
28
+ /** children replace the identity block (name + phone) when provided. */
29
+ children?: React.ReactNode;
30
+ }
31
+ declare function MessageViewHeader({
32
+ conversation,
33
+ onBack,
34
+ onInfoClick,
35
+ showBackButton,
36
+ actions,
37
+ labels: labelsProp,
38
+ children,
39
+ className,
40
+ ...props
41
+ }: MessageViewHeaderProps): react_jsx_runtime0.JSX.Element;
42
+ interface MessageViewContentProps extends React.ComponentProps<"div"> {
43
+ children?: React.ReactNode;
44
+ autoScroll?: boolean;
45
+ /** Called when the user scrolls to the top of the container. */
46
+ onScrollTop?: () => void;
47
+ }
48
+ declare function MessageViewContent({
49
+ children,
50
+ autoScroll,
51
+ onScrollTop,
52
+ className,
53
+ ...props
54
+ }: MessageViewContentProps): react_jsx_runtime0.JSX.Element;
55
+ type MessageGroupPosition = "single" | "first" | "middle" | "last";
56
+ interface MessageRenderContext {
57
+ message: UIMessage;
58
+ /** Stable list identity — always message.id. */
59
+ id: string;
60
+ direction: "incoming" | "outgoing";
61
+ /** Presentation alignment: incoming → "start", outgoing → "end". */
62
+ align: "start" | "end";
63
+ groupPosition: MessageGroupPosition;
64
+ /** Result of renderMessageLabel, when provided. */
65
+ label?: string;
66
+ }
67
+ interface DateDividerRenderContext {
68
+ /** Formatted label (output of formatDate / getDisplayDate). */
69
+ label: string;
70
+ /** sentAt of the first message under this divider (raw ISO string). */
71
+ date: string;
72
+ }
73
+ interface MessageListProps {
74
+ messages: UIMessage[];
75
+ renderMessage?: (context: MessageRenderContext) => React.ReactNode;
76
+ renderDateDivider?: (context: DateDividerRenderContext) => React.ReactNode;
77
+ /** Formats divider labels. Default: getDisplayDate (pt-BR HOJE/ONTEM). */
78
+ formatDate?: (isoDate: string) => string;
79
+ /** Formats the in-bubble timestamp used by the DEFAULT renderer. Default: pt-BR HH:mm. */
80
+ formatTime?: (isoDate: string) => string;
81
+ renderMessageLabel?: (message: UIMessage) => string | undefined;
82
+ /** Direct props — context from MessageViewContent still works; these win over it. */
83
+ autoScroll?: boolean;
84
+ onScrollTop?: () => void;
85
+ className?: string;
86
+ }
87
+ declare function MessageList({
88
+ messages,
89
+ renderMessage,
90
+ renderDateDivider,
91
+ formatDate,
92
+ formatTime,
93
+ renderMessageLabel,
94
+ autoScroll: autoScrollProp,
95
+ onScrollTop: onScrollTopProp,
96
+ className
97
+ }: MessageListProps): react_jsx_runtime0.JSX.Element;
98
+ declare function DateDivider({
99
+ children,
100
+ className,
101
+ ...props
102
+ }: React.ComponentProps<"div">): react_jsx_runtime0.JSX.Element;
103
+ declare function MessageViewEmpty({
104
+ className,
105
+ children,
106
+ ...props
107
+ }: React.ComponentProps<"div">): react_jsx_runtime0.JSX.Element;
108
+ //#endregion
109
+ export { DateDivider, DateDividerRenderContext, MessageGroupPosition, MessageList, MessageListProps, MessageRenderContext, MessageView, MessageViewContent, MessageViewContentProps, MessageViewEmpty, MessageViewHeader, MessageViewHeaderLabels, MessageViewHeaderProps, MessageViewProps };