@better-zap/react 0.2.1 → 0.2.3
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/bubble.cjs +93 -0
- package/dist/bubble.d.cts +61 -0
- package/dist/bubble.d.mts +61 -0
- package/dist/bubble.mjs +89 -0
- package/dist/composer.cjs +220 -0
- package/dist/composer.d.cts +96 -0
- package/dist/composer.d.mts +96 -0
- package/dist/composer.mjs +211 -0
- package/dist/conversation-list-BlhJhDsc.d.cts +100 -0
- package/dist/conversation-list-BwQ4oK2J.d.mts +100 -0
- package/dist/conversation-list-C0EmReS-.mjs +293 -0
- package/dist/conversation-list-CVWdddJh.cjs +311 -0
- package/dist/conversation-list.cjs +6 -0
- package/dist/conversation-list.d.cts +2 -0
- package/dist/conversation-list.d.mts +2 -0
- package/dist/conversation-list.mjs +5 -0
- package/dist/index.cjs +49 -0
- package/dist/index.d.cts +19 -0
- package/dist/index.d.mts +19 -0
- package/dist/index.mjs +11 -0
- package/dist/message-bubble.cjs +116 -0
- package/dist/message-bubble.d.cts +38 -0
- package/dist/message-bubble.d.mts +38 -0
- package/dist/message-bubble.mjs +114 -0
- package/dist/message-input-CkaL6fb4.mjs +173 -0
- package/dist/message-input-DcEzHQ9t.cjs +191 -0
- package/dist/message-input.cjs +6 -0
- package/dist/message-input.d.cts +51 -0
- package/dist/message-input.d.mts +51 -0
- package/dist/message-input.mjs +5 -0
- package/dist/message-view.cjs +273 -0
- package/dist/message-view.d.cts +109 -0
- package/dist/message-view.d.mts +109 -0
- package/dist/message-view.mjs +266 -0
- package/dist/message.cjs +50 -0
- package/dist/message.d.cts +40 -0
- package/dist/message.d.mts +40 -0
- package/dist/message.mjs +43 -0
- package/dist/tailwind.css +33 -0
- package/dist/utils-Bp9IahGG.cjs +91 -0
- package/dist/utils.cjs +5 -0
- package/dist/utils.d.cts +20 -0
- package/dist/utils.d.mts +20 -0
- package/dist/utils.mjs +45 -0
- package/dist/whatsapp-dashboard.cjs +64 -0
- package/dist/whatsapp-dashboard.d.cts +33 -0
- package/dist/whatsapp-dashboard.d.mts +33 -0
- package/dist/whatsapp-dashboard.mjs +60 -0
- package/dist/wpp-bg.webp +0 -0
- package/package.json +88 -35
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { cn } from "./utils.mjs";
|
|
2
|
+
import { useOptionalWhatsappDashboard } from "./whatsapp-dashboard.mjs";
|
|
3
|
+
import React, { useCallback, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { HugeiconsIcon } from "@hugeicons/react";
|
|
6
|
+
import { Message01Icon, Search01Icon, UserIcon } from "@hugeicons/core-free-icons";
|
|
7
|
+
import { LegendList } from "@legendapp/list/react";
|
|
8
|
+
//#region src/conversation-search.tsx
|
|
9
|
+
/**
|
|
10
|
+
* Memoized: with a stable onChange, sibling state changes (filter chips,
|
|
11
|
+
* selection) don't re-render the search box.
|
|
12
|
+
*/
|
|
13
|
+
const ConversationSearch = React.memo(function ConversationSearch({ value, onChange, className, placeholder, "aria-label": ariaLabel }) {
|
|
14
|
+
const effectivePlaceholder = placeholder ?? "Buscar conversa";
|
|
15
|
+
const effectiveAriaLabel = ariaLabel ?? effectivePlaceholder;
|
|
16
|
+
return /* @__PURE__ */ jsx("div", {
|
|
17
|
+
className: cn("shrink-0 px-3 py-2", className),
|
|
18
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
19
|
+
className: "flex items-center bg-[#f0f2f5] rounded-full px-3 h-[35px] gap-3 border border-transparent transition-colors focus-within:bg-white focus-within:border-[#e9edef]",
|
|
20
|
+
children: [/* @__PURE__ */ jsx(HugeiconsIcon, {
|
|
21
|
+
icon: Search01Icon,
|
|
22
|
+
size: 18,
|
|
23
|
+
className: "text-[#54656f] shrink-0"
|
|
24
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
25
|
+
type: "text",
|
|
26
|
+
placeholder: effectivePlaceholder,
|
|
27
|
+
"aria-label": effectiveAriaLabel,
|
|
28
|
+
value,
|
|
29
|
+
onChange: (e) => onChange(e.target.value),
|
|
30
|
+
className: "flex-1 border-none bg-transparent text-[15px] text-[#111b21] focus:outline-none h-full placeholder:text-[#667781]"
|
|
31
|
+
})]
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/conversation-filter-chips.tsx
|
|
37
|
+
/**
|
|
38
|
+
* Memoized: with stable onValueChange and labels, typing in the search box
|
|
39
|
+
* doesn't re-render the chips.
|
|
40
|
+
*/
|
|
41
|
+
const ConversationFilterChips = React.memo(function ConversationFilterChips({ value, onValueChange, unreadCount = 0, className, labels }) {
|
|
42
|
+
const chips = [{
|
|
43
|
+
label: labels?.all ?? "Tudo",
|
|
44
|
+
value: "all"
|
|
45
|
+
}, {
|
|
46
|
+
label: labels?.unread ?? "Não lidas",
|
|
47
|
+
value: "unread"
|
|
48
|
+
}];
|
|
49
|
+
return /* @__PURE__ */ jsx("div", {
|
|
50
|
+
className: cn("flex items-center gap-1.5 px-3 pb-2 pt-1", className),
|
|
51
|
+
children: chips.map((chip) => {
|
|
52
|
+
const isActive = chip.value === value;
|
|
53
|
+
const showCount = chip.value === "unread" && unreadCount > 0;
|
|
54
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
55
|
+
type: "button",
|
|
56
|
+
onClick: () => onValueChange(chip.value),
|
|
57
|
+
"aria-pressed": isActive,
|
|
58
|
+
className: cn("inline-flex h-8 items-center rounded-full px-3 text-[14px] transition-colors", isActive ? "bg-[#e7fce3] text-[#008069]" : "bg-[#f0f2f5] text-[#54656f] hover:bg-[#e9edef]"),
|
|
59
|
+
children: [/* @__PURE__ */ jsx("span", { children: chip.label }), showCount ? /* @__PURE__ */ jsx("span", {
|
|
60
|
+
className: "ml-1",
|
|
61
|
+
children: unreadCount
|
|
62
|
+
}) : null]
|
|
63
|
+
}, chip.value);
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/conversation-list.tsx
|
|
69
|
+
const DEFAULT_LABELS = {
|
|
70
|
+
searchPlaceholder: "Buscar conversa",
|
|
71
|
+
searchLabel: "Buscar conversa",
|
|
72
|
+
filterAll: "Tudo",
|
|
73
|
+
filterUnread: "Não lidas",
|
|
74
|
+
loading: "Carregando...",
|
|
75
|
+
error: "Erro ao carregar conversas",
|
|
76
|
+
empty: "Nenhuma conversa encontrada",
|
|
77
|
+
outgoingPrefix: "Você: ",
|
|
78
|
+
noPreview: "Sem mensagem",
|
|
79
|
+
yesterday: "Ontem"
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Memoized default row. Every prop is a primitive or a stable reference, so
|
|
83
|
+
* selecting a conversation re-renders exactly two rows (the newly selected
|
|
84
|
+
* and the previously selected one), not the whole list.
|
|
85
|
+
*/
|
|
86
|
+
const ConversationListRow = React.memo(function ConversationListRow({ conversation, isSelected, onSelectConversation, avatar, outgoingPrefix, noPreviewLabel, formatTime }) {
|
|
87
|
+
return /* @__PURE__ */ jsx(ConversationItem, {
|
|
88
|
+
conversation,
|
|
89
|
+
isSelected,
|
|
90
|
+
onClick: useCallback(() => {
|
|
91
|
+
onSelectConversation(conversation.id);
|
|
92
|
+
}, [onSelectConversation, conversation.id]),
|
|
93
|
+
avatar,
|
|
94
|
+
outgoingPrefix,
|
|
95
|
+
noPreviewLabel,
|
|
96
|
+
formatTime
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
function ConversationList({ conversations, isLoading = false, isError, selectedConversationId, onSelect, search: searchProp, defaultSearch = "", onSearchChange, filter: filterProp, defaultFilter = "all", onFilterChange, renderItem, renderAvatar, formatTime: formatTimeProp, labels: labelsProp, className, ...props }) {
|
|
100
|
+
const dashboard = useOptionalWhatsappDashboard();
|
|
101
|
+
const isMobile = dashboard?.isMobile ?? false;
|
|
102
|
+
const mobileView = dashboard?.mobileView ?? "list";
|
|
103
|
+
const isSearchControlled = searchProp !== void 0;
|
|
104
|
+
const [internalSearch, setInternalSearch] = useState(defaultSearch);
|
|
105
|
+
const search = isSearchControlled ? searchProp : internalSearch;
|
|
106
|
+
const isFilterControlled = filterProp !== void 0;
|
|
107
|
+
const [internalFilter, setInternalFilter] = useState(defaultFilter);
|
|
108
|
+
const filter = isFilterControlled ? filterProp : internalFilter;
|
|
109
|
+
const labels = {
|
|
110
|
+
...DEFAULT_LABELS,
|
|
111
|
+
...labelsProp,
|
|
112
|
+
searchLabel: labelsProp?.searchLabel ?? labelsProp?.searchPlaceholder ?? DEFAULT_LABELS.searchLabel
|
|
113
|
+
};
|
|
114
|
+
const isSearchControlledRef = useRef(isSearchControlled);
|
|
115
|
+
isSearchControlledRef.current = isSearchControlled;
|
|
116
|
+
const isFilterControlledRef = useRef(isFilterControlled);
|
|
117
|
+
isFilterControlledRef.current = isFilterControlled;
|
|
118
|
+
const onSearchChangeRef = useRef(onSearchChange);
|
|
119
|
+
onSearchChangeRef.current = onSearchChange;
|
|
120
|
+
const onFilterChangeRef = useRef(onFilterChange);
|
|
121
|
+
onFilterChangeRef.current = onFilterChange;
|
|
122
|
+
const onSelectRef = useRef(onSelect);
|
|
123
|
+
onSelectRef.current = onSelect;
|
|
124
|
+
const dashboardRef = useRef(dashboard);
|
|
125
|
+
dashboardRef.current = dashboard;
|
|
126
|
+
const formatTimePropRef = useRef(formatTimeProp);
|
|
127
|
+
formatTimePropRef.current = formatTimeProp;
|
|
128
|
+
const yesterdayLabelRef = useRef(labels.yesterday);
|
|
129
|
+
yesterdayLabelRef.current = labels.yesterday;
|
|
130
|
+
const handleSearchChange = useCallback((value) => {
|
|
131
|
+
if (!isSearchControlledRef.current) setInternalSearch(value);
|
|
132
|
+
onSearchChangeRef.current?.(value);
|
|
133
|
+
}, []);
|
|
134
|
+
const handleFilterChange = useCallback((value) => {
|
|
135
|
+
if (!isFilterControlledRef.current) setInternalFilter(value);
|
|
136
|
+
onFilterChangeRef.current?.(value);
|
|
137
|
+
}, []);
|
|
138
|
+
const handleSelect = useCallback((id) => {
|
|
139
|
+
onSelectRef.current?.(id);
|
|
140
|
+
dashboardRef.current?.setMobileView("chat");
|
|
141
|
+
}, []);
|
|
142
|
+
const effectiveFormatTime = useCallback((isoDate) => formatTimePropRef.current ? formatTimePropRef.current(isoDate) : formatTimeDefault(isoDate, yesterdayLabelRef.current), []);
|
|
143
|
+
const chipLabels = useMemo(() => ({
|
|
144
|
+
all: labels.filterAll,
|
|
145
|
+
unread: labels.filterUnread
|
|
146
|
+
}), [labels.filterAll, labels.filterUnread]);
|
|
147
|
+
const normalizedSearch = search.trim().toLowerCase();
|
|
148
|
+
const effectiveFilter = filter;
|
|
149
|
+
const unreadConversationsCount = useMemo(() => conversations.filter((c) => c.unreadCount > 0).length, [conversations]);
|
|
150
|
+
const filtered = useMemo(() => {
|
|
151
|
+
return conversations.filter((conversation) => {
|
|
152
|
+
const matchesSearch = normalizedSearch.length === 0 || conversation.phone.toLowerCase().includes(normalizedSearch) || conversation.contactName?.toLowerCase().includes(normalizedSearch);
|
|
153
|
+
const matchesFilter = effectiveFilter === "all" || conversation.unreadCount > 0;
|
|
154
|
+
return matchesSearch && matchesFilter;
|
|
155
|
+
});
|
|
156
|
+
}, [
|
|
157
|
+
conversations,
|
|
158
|
+
normalizedSearch,
|
|
159
|
+
effectiveFilter
|
|
160
|
+
]);
|
|
161
|
+
const isVisible = !isMobile || mobileView === "list";
|
|
162
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
163
|
+
className: cn("flex flex-col h-full bg-white border-r border-[#e9edef]", isMobile ? "w-full" : "min-w-[320px] max-w-105", className),
|
|
164
|
+
...props,
|
|
165
|
+
style: isVisible ? props.style : { display: "none" },
|
|
166
|
+
children: [
|
|
167
|
+
/* @__PURE__ */ jsx(ConversationSearch, {
|
|
168
|
+
value: search,
|
|
169
|
+
onChange: handleSearchChange,
|
|
170
|
+
placeholder: labels.searchPlaceholder,
|
|
171
|
+
"aria-label": labels.searchLabel
|
|
172
|
+
}),
|
|
173
|
+
/* @__PURE__ */ jsx(ConversationFilterChips, {
|
|
174
|
+
value: filter,
|
|
175
|
+
onValueChange: handleFilterChange,
|
|
176
|
+
unreadCount: unreadConversationsCount,
|
|
177
|
+
labels: chipLabels
|
|
178
|
+
}),
|
|
179
|
+
/* @__PURE__ */ jsx("div", {
|
|
180
|
+
className: "min-h-0 flex-1",
|
|
181
|
+
children: isLoading ? /* @__PURE__ */ jsx("div", {
|
|
182
|
+
className: "flex items-center justify-center h-full text-sm text-[#667781]",
|
|
183
|
+
children: labels.loading
|
|
184
|
+
}) : isError ? /* @__PURE__ */ jsx("div", {
|
|
185
|
+
className: "flex items-center justify-center h-full text-sm text-red-500",
|
|
186
|
+
children: labels.error
|
|
187
|
+
}) : filtered.length === 0 ? /* @__PURE__ */ jsxs("div", {
|
|
188
|
+
className: "flex flex-col items-center justify-center h-full gap-2 text-[#667781]",
|
|
189
|
+
children: [/* @__PURE__ */ jsx(HugeiconsIcon, {
|
|
190
|
+
icon: Message01Icon,
|
|
191
|
+
size: 32
|
|
192
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
193
|
+
className: "text-sm",
|
|
194
|
+
children: labels.empty
|
|
195
|
+
})]
|
|
196
|
+
}) : /* @__PURE__ */ jsx(LegendList, {
|
|
197
|
+
className: "chat-scrollbar",
|
|
198
|
+
data: filtered,
|
|
199
|
+
estimatedItemSize: 72,
|
|
200
|
+
extraData: selectedConversationId,
|
|
201
|
+
getFixedItemSize: () => 72,
|
|
202
|
+
keyExtractor: (conversation) => conversation.id,
|
|
203
|
+
recycleItems: true,
|
|
204
|
+
renderItem: ({ item: conversation }) => {
|
|
205
|
+
const isSelected = selectedConversationId === conversation.id;
|
|
206
|
+
if (renderItem) return renderItem(conversation, {
|
|
207
|
+
isSelected,
|
|
208
|
+
select: () => handleSelect(conversation.id)
|
|
209
|
+
});
|
|
210
|
+
return /* @__PURE__ */ jsx(ConversationListRow, {
|
|
211
|
+
conversation,
|
|
212
|
+
isSelected,
|
|
213
|
+
onSelectConversation: handleSelect,
|
|
214
|
+
avatar: renderAvatar?.(conversation),
|
|
215
|
+
outgoingPrefix: labels.outgoingPrefix,
|
|
216
|
+
noPreviewLabel: labels.noPreview,
|
|
217
|
+
formatTime: effectiveFormatTime
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
style: {
|
|
221
|
+
height: "100%",
|
|
222
|
+
overflowX: "hidden"
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
function ConversationItem({ conversation, isSelected = false, avatar, outgoingPrefix = "Você: ", noPreviewLabel = "Sem mensagem", formatTime: formatTimeProp, className, ...props }) {
|
|
230
|
+
const timeLabel = formatTimeProp ? formatTimeProp(conversation.lastMessageAt) : formatTimeDefault(conversation.lastMessageAt, "Ontem");
|
|
231
|
+
const hasUnread = conversation.unreadCount > 0;
|
|
232
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
233
|
+
...props,
|
|
234
|
+
type: "button",
|
|
235
|
+
"data-selected": isSelected,
|
|
236
|
+
"aria-current": isSelected ? "true" : void 0,
|
|
237
|
+
className: cn("group flex items-center w-full h-[72px] px-3 gap-3 transition-colors cursor-pointer text-left relative overflow-hidden hover:bg-[#f5f6f6] data-[selected=true]:bg-[#f0f2f5]", className),
|
|
238
|
+
children: [avatar ?? /* @__PURE__ */ jsx("div", {
|
|
239
|
+
className: "w-[49px] h-[49px] rounded-full bg-[#dfe5e7] flex items-center justify-center shrink-0",
|
|
240
|
+
children: /* @__PURE__ */ jsx(HugeiconsIcon, {
|
|
241
|
+
icon: UserIcon,
|
|
242
|
+
size: 28,
|
|
243
|
+
className: "text-white"
|
|
244
|
+
})
|
|
245
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
246
|
+
className: "flex-1 min-w-0 border-b border-[#e9edef]/70 h-full flex flex-col justify-center pr-1 group-last:border-none group-data-[selected=true]:border-transparent",
|
|
247
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
248
|
+
className: "flex justify-between items-baseline mb-0.5",
|
|
249
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
250
|
+
className: "text-[17px] font-normal text-[#111b21] truncate",
|
|
251
|
+
children: conversation.contactName || formatPhone(conversation.phone)
|
|
252
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
253
|
+
className: cn("text-xs shrink-0", hasUnread ? "text-[#1daa61]" : "text-[#667781]"),
|
|
254
|
+
children: timeLabel
|
|
255
|
+
})]
|
|
256
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
257
|
+
className: "flex justify-between items-center gap-2",
|
|
258
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
259
|
+
className: "text-[14px] text-[#667781] truncate",
|
|
260
|
+
children: [conversation.lastDirection === "incoming" ? "" : outgoingPrefix, conversation.lastMessagePreview || noPreviewLabel]
|
|
261
|
+
}), hasUnread && /* @__PURE__ */ jsx("span", {
|
|
262
|
+
className: "bg-[#25d366] text-white text-[11px] font-semibold rounded-full min-w-[20px] h-[20px] flex items-center justify-center px-1.5 shrink-0",
|
|
263
|
+
children: conversation.unreadCount
|
|
264
|
+
})]
|
|
265
|
+
})]
|
|
266
|
+
})]
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function formatPhone(phone) {
|
|
270
|
+
if (phone.length === 13 && phone.startsWith("55")) return `(${phone.slice(2, 4)}) ${phone.slice(4, 9)}-${phone.slice(9)}`;
|
|
271
|
+
return phone;
|
|
272
|
+
}
|
|
273
|
+
function formatTimeDefault(dateStr, yesterdayLabel) {
|
|
274
|
+
try {
|
|
275
|
+
const date = new Date(dateStr);
|
|
276
|
+
const now = /* @__PURE__ */ new Date();
|
|
277
|
+
if (date.toDateString() === now.toDateString()) return date.toLocaleTimeString("pt-BR", {
|
|
278
|
+
hour: "2-digit",
|
|
279
|
+
minute: "2-digit"
|
|
280
|
+
});
|
|
281
|
+
const yesterday = new Date(now);
|
|
282
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
283
|
+
if (date.toDateString() === yesterday.toDateString()) return yesterdayLabel;
|
|
284
|
+
return date.toLocaleDateString("pt-BR", {
|
|
285
|
+
day: "2-digit",
|
|
286
|
+
month: "2-digit"
|
|
287
|
+
});
|
|
288
|
+
} catch {
|
|
289
|
+
return "";
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
export { ConversationList as n, ConversationFilterChips as r, ConversationItem as t };
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
const require_utils = require("./utils-Bp9IahGG.cjs");
|
|
2
|
+
const require_whatsapp_dashboard = require("./whatsapp-dashboard.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 _legendapp_list_react = require("@legendapp/list/react");
|
|
9
|
+
//#region src/conversation-search.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Memoized: with a stable onChange, sibling state changes (filter chips,
|
|
12
|
+
* selection) don't re-render the search box.
|
|
13
|
+
*/
|
|
14
|
+
const ConversationSearch = react.default.memo(function ConversationSearch({ value, onChange, className, placeholder, "aria-label": ariaLabel }) {
|
|
15
|
+
const effectivePlaceholder = placeholder ?? "Buscar conversa";
|
|
16
|
+
const effectiveAriaLabel = ariaLabel ?? effectivePlaceholder;
|
|
17
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
18
|
+
className: require_utils.cn("shrink-0 px-3 py-2", className),
|
|
19
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
20
|
+
className: "flex items-center bg-[#f0f2f5] rounded-full px-3 h-[35px] gap-3 border border-transparent transition-colors focus-within:bg-white focus-within:border-[#e9edef]",
|
|
21
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
22
|
+
icon: _hugeicons_core_free_icons.Search01Icon,
|
|
23
|
+
size: 18,
|
|
24
|
+
className: "text-[#54656f] shrink-0"
|
|
25
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
26
|
+
type: "text",
|
|
27
|
+
placeholder: effectivePlaceholder,
|
|
28
|
+
"aria-label": effectiveAriaLabel,
|
|
29
|
+
value,
|
|
30
|
+
onChange: (e) => onChange(e.target.value),
|
|
31
|
+
className: "flex-1 border-none bg-transparent text-[15px] text-[#111b21] focus:outline-none h-full placeholder:text-[#667781]"
|
|
32
|
+
})]
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/conversation-filter-chips.tsx
|
|
38
|
+
/**
|
|
39
|
+
* Memoized: with stable onValueChange and labels, typing in the search box
|
|
40
|
+
* doesn't re-render the chips.
|
|
41
|
+
*/
|
|
42
|
+
const ConversationFilterChips = react.default.memo(function ConversationFilterChips({ value, onValueChange, unreadCount = 0, className, labels }) {
|
|
43
|
+
const chips = [{
|
|
44
|
+
label: labels?.all ?? "Tudo",
|
|
45
|
+
value: "all"
|
|
46
|
+
}, {
|
|
47
|
+
label: labels?.unread ?? "Não lidas",
|
|
48
|
+
value: "unread"
|
|
49
|
+
}];
|
|
50
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
51
|
+
className: require_utils.cn("flex items-center gap-1.5 px-3 pb-2 pt-1", className),
|
|
52
|
+
children: chips.map((chip) => {
|
|
53
|
+
const isActive = chip.value === value;
|
|
54
|
+
const showCount = chip.value === "unread" && unreadCount > 0;
|
|
55
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
56
|
+
type: "button",
|
|
57
|
+
onClick: () => onValueChange(chip.value),
|
|
58
|
+
"aria-pressed": isActive,
|
|
59
|
+
className: require_utils.cn("inline-flex h-8 items-center rounded-full px-3 text-[14px] transition-colors", isActive ? "bg-[#e7fce3] text-[#008069]" : "bg-[#f0f2f5] text-[#54656f] hover:bg-[#e9edef]"),
|
|
60
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: chip.label }), showCount ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
61
|
+
className: "ml-1",
|
|
62
|
+
children: unreadCount
|
|
63
|
+
}) : null]
|
|
64
|
+
}, chip.value);
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/conversation-list.tsx
|
|
70
|
+
const DEFAULT_LABELS = {
|
|
71
|
+
searchPlaceholder: "Buscar conversa",
|
|
72
|
+
searchLabel: "Buscar conversa",
|
|
73
|
+
filterAll: "Tudo",
|
|
74
|
+
filterUnread: "Não lidas",
|
|
75
|
+
loading: "Carregando...",
|
|
76
|
+
error: "Erro ao carregar conversas",
|
|
77
|
+
empty: "Nenhuma conversa encontrada",
|
|
78
|
+
outgoingPrefix: "Você: ",
|
|
79
|
+
noPreview: "Sem mensagem",
|
|
80
|
+
yesterday: "Ontem"
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Memoized default row. Every prop is a primitive or a stable reference, so
|
|
84
|
+
* selecting a conversation re-renders exactly two rows (the newly selected
|
|
85
|
+
* and the previously selected one), not the whole list.
|
|
86
|
+
*/
|
|
87
|
+
const ConversationListRow = react.default.memo(function ConversationListRow({ conversation, isSelected, onSelectConversation, avatar, outgoingPrefix, noPreviewLabel, formatTime }) {
|
|
88
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConversationItem, {
|
|
89
|
+
conversation,
|
|
90
|
+
isSelected,
|
|
91
|
+
onClick: (0, react.useCallback)(() => {
|
|
92
|
+
onSelectConversation(conversation.id);
|
|
93
|
+
}, [onSelectConversation, conversation.id]),
|
|
94
|
+
avatar,
|
|
95
|
+
outgoingPrefix,
|
|
96
|
+
noPreviewLabel,
|
|
97
|
+
formatTime
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
function ConversationList({ conversations, isLoading = false, isError, selectedConversationId, onSelect, search: searchProp, defaultSearch = "", onSearchChange, filter: filterProp, defaultFilter = "all", onFilterChange, renderItem, renderAvatar, formatTime: formatTimeProp, labels: labelsProp, className, ...props }) {
|
|
101
|
+
const dashboard = require_whatsapp_dashboard.useOptionalWhatsappDashboard();
|
|
102
|
+
const isMobile = dashboard?.isMobile ?? false;
|
|
103
|
+
const mobileView = dashboard?.mobileView ?? "list";
|
|
104
|
+
const isSearchControlled = searchProp !== void 0;
|
|
105
|
+
const [internalSearch, setInternalSearch] = (0, react.useState)(defaultSearch);
|
|
106
|
+
const search = isSearchControlled ? searchProp : internalSearch;
|
|
107
|
+
const isFilterControlled = filterProp !== void 0;
|
|
108
|
+
const [internalFilter, setInternalFilter] = (0, react.useState)(defaultFilter);
|
|
109
|
+
const filter = isFilterControlled ? filterProp : internalFilter;
|
|
110
|
+
const labels = {
|
|
111
|
+
...DEFAULT_LABELS,
|
|
112
|
+
...labelsProp,
|
|
113
|
+
searchLabel: labelsProp?.searchLabel ?? labelsProp?.searchPlaceholder ?? DEFAULT_LABELS.searchLabel
|
|
114
|
+
};
|
|
115
|
+
const isSearchControlledRef = (0, react.useRef)(isSearchControlled);
|
|
116
|
+
isSearchControlledRef.current = isSearchControlled;
|
|
117
|
+
const isFilterControlledRef = (0, react.useRef)(isFilterControlled);
|
|
118
|
+
isFilterControlledRef.current = isFilterControlled;
|
|
119
|
+
const onSearchChangeRef = (0, react.useRef)(onSearchChange);
|
|
120
|
+
onSearchChangeRef.current = onSearchChange;
|
|
121
|
+
const onFilterChangeRef = (0, react.useRef)(onFilterChange);
|
|
122
|
+
onFilterChangeRef.current = onFilterChange;
|
|
123
|
+
const onSelectRef = (0, react.useRef)(onSelect);
|
|
124
|
+
onSelectRef.current = onSelect;
|
|
125
|
+
const dashboardRef = (0, react.useRef)(dashboard);
|
|
126
|
+
dashboardRef.current = dashboard;
|
|
127
|
+
const formatTimePropRef = (0, react.useRef)(formatTimeProp);
|
|
128
|
+
formatTimePropRef.current = formatTimeProp;
|
|
129
|
+
const yesterdayLabelRef = (0, react.useRef)(labels.yesterday);
|
|
130
|
+
yesterdayLabelRef.current = labels.yesterday;
|
|
131
|
+
const handleSearchChange = (0, react.useCallback)((value) => {
|
|
132
|
+
if (!isSearchControlledRef.current) setInternalSearch(value);
|
|
133
|
+
onSearchChangeRef.current?.(value);
|
|
134
|
+
}, []);
|
|
135
|
+
const handleFilterChange = (0, react.useCallback)((value) => {
|
|
136
|
+
if (!isFilterControlledRef.current) setInternalFilter(value);
|
|
137
|
+
onFilterChangeRef.current?.(value);
|
|
138
|
+
}, []);
|
|
139
|
+
const handleSelect = (0, react.useCallback)((id) => {
|
|
140
|
+
onSelectRef.current?.(id);
|
|
141
|
+
dashboardRef.current?.setMobileView("chat");
|
|
142
|
+
}, []);
|
|
143
|
+
const effectiveFormatTime = (0, react.useCallback)((isoDate) => formatTimePropRef.current ? formatTimePropRef.current(isoDate) : formatTimeDefault(isoDate, yesterdayLabelRef.current), []);
|
|
144
|
+
const chipLabels = (0, react.useMemo)(() => ({
|
|
145
|
+
all: labels.filterAll,
|
|
146
|
+
unread: labels.filterUnread
|
|
147
|
+
}), [labels.filterAll, labels.filterUnread]);
|
|
148
|
+
const normalizedSearch = search.trim().toLowerCase();
|
|
149
|
+
const effectiveFilter = filter;
|
|
150
|
+
const unreadConversationsCount = (0, react.useMemo)(() => conversations.filter((c) => c.unreadCount > 0).length, [conversations]);
|
|
151
|
+
const filtered = (0, react.useMemo)(() => {
|
|
152
|
+
return conversations.filter((conversation) => {
|
|
153
|
+
const matchesSearch = normalizedSearch.length === 0 || conversation.phone.toLowerCase().includes(normalizedSearch) || conversation.contactName?.toLowerCase().includes(normalizedSearch);
|
|
154
|
+
const matchesFilter = effectiveFilter === "all" || conversation.unreadCount > 0;
|
|
155
|
+
return matchesSearch && matchesFilter;
|
|
156
|
+
});
|
|
157
|
+
}, [
|
|
158
|
+
conversations,
|
|
159
|
+
normalizedSearch,
|
|
160
|
+
effectiveFilter
|
|
161
|
+
]);
|
|
162
|
+
const isVisible = !isMobile || mobileView === "list";
|
|
163
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
164
|
+
className: require_utils.cn("flex flex-col h-full bg-white border-r border-[#e9edef]", isMobile ? "w-full" : "min-w-[320px] max-w-105", className),
|
|
165
|
+
...props,
|
|
166
|
+
style: isVisible ? props.style : { display: "none" },
|
|
167
|
+
children: [
|
|
168
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConversationSearch, {
|
|
169
|
+
value: search,
|
|
170
|
+
onChange: handleSearchChange,
|
|
171
|
+
placeholder: labels.searchPlaceholder,
|
|
172
|
+
"aria-label": labels.searchLabel
|
|
173
|
+
}),
|
|
174
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConversationFilterChips, {
|
|
175
|
+
value: filter,
|
|
176
|
+
onValueChange: handleFilterChange,
|
|
177
|
+
unreadCount: unreadConversationsCount,
|
|
178
|
+
labels: chipLabels
|
|
179
|
+
}),
|
|
180
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
181
|
+
className: "min-h-0 flex-1",
|
|
182
|
+
children: isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
183
|
+
className: "flex items-center justify-center h-full text-sm text-[#667781]",
|
|
184
|
+
children: labels.loading
|
|
185
|
+
}) : isError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
186
|
+
className: "flex items-center justify-center h-full text-sm text-red-500",
|
|
187
|
+
children: labels.error
|
|
188
|
+
}) : filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
189
|
+
className: "flex flex-col items-center justify-center h-full gap-2 text-[#667781]",
|
|
190
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
191
|
+
icon: _hugeicons_core_free_icons.Message01Icon,
|
|
192
|
+
size: 32
|
|
193
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
194
|
+
className: "text-sm",
|
|
195
|
+
children: labels.empty
|
|
196
|
+
})]
|
|
197
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_legendapp_list_react.LegendList, {
|
|
198
|
+
className: "chat-scrollbar",
|
|
199
|
+
data: filtered,
|
|
200
|
+
estimatedItemSize: 72,
|
|
201
|
+
extraData: selectedConversationId,
|
|
202
|
+
getFixedItemSize: () => 72,
|
|
203
|
+
keyExtractor: (conversation) => conversation.id,
|
|
204
|
+
recycleItems: true,
|
|
205
|
+
renderItem: ({ item: conversation }) => {
|
|
206
|
+
const isSelected = selectedConversationId === conversation.id;
|
|
207
|
+
if (renderItem) return renderItem(conversation, {
|
|
208
|
+
isSelected,
|
|
209
|
+
select: () => handleSelect(conversation.id)
|
|
210
|
+
});
|
|
211
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConversationListRow, {
|
|
212
|
+
conversation,
|
|
213
|
+
isSelected,
|
|
214
|
+
onSelectConversation: handleSelect,
|
|
215
|
+
avatar: renderAvatar?.(conversation),
|
|
216
|
+
outgoingPrefix: labels.outgoingPrefix,
|
|
217
|
+
noPreviewLabel: labels.noPreview,
|
|
218
|
+
formatTime: effectiveFormatTime
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
style: {
|
|
222
|
+
height: "100%",
|
|
223
|
+
overflowX: "hidden"
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
function ConversationItem({ conversation, isSelected = false, avatar, outgoingPrefix = "Você: ", noPreviewLabel = "Sem mensagem", formatTime: formatTimeProp, className, ...props }) {
|
|
231
|
+
const timeLabel = formatTimeProp ? formatTimeProp(conversation.lastMessageAt) : formatTimeDefault(conversation.lastMessageAt, "Ontem");
|
|
232
|
+
const hasUnread = conversation.unreadCount > 0;
|
|
233
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
234
|
+
...props,
|
|
235
|
+
type: "button",
|
|
236
|
+
"data-selected": isSelected,
|
|
237
|
+
"aria-current": isSelected ? "true" : void 0,
|
|
238
|
+
className: require_utils.cn("group flex items-center w-full h-[72px] px-3 gap-3 transition-colors cursor-pointer text-left relative overflow-hidden hover:bg-[#f5f6f6] data-[selected=true]:bg-[#f0f2f5]", className),
|
|
239
|
+
children: [avatar ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
240
|
+
className: "w-[49px] h-[49px] rounded-full bg-[#dfe5e7] flex items-center justify-center shrink-0",
|
|
241
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_hugeicons_react.HugeiconsIcon, {
|
|
242
|
+
icon: _hugeicons_core_free_icons.UserIcon,
|
|
243
|
+
size: 28,
|
|
244
|
+
className: "text-white"
|
|
245
|
+
})
|
|
246
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
247
|
+
className: "flex-1 min-w-0 border-b border-[#e9edef]/70 h-full flex flex-col justify-center pr-1 group-last:border-none group-data-[selected=true]:border-transparent",
|
|
248
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
249
|
+
className: "flex justify-between items-baseline mb-0.5",
|
|
250
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
251
|
+
className: "text-[17px] font-normal text-[#111b21] truncate",
|
|
252
|
+
children: conversation.contactName || formatPhone(conversation.phone)
|
|
253
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
254
|
+
className: require_utils.cn("text-xs shrink-0", hasUnread ? "text-[#1daa61]" : "text-[#667781]"),
|
|
255
|
+
children: timeLabel
|
|
256
|
+
})]
|
|
257
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
258
|
+
className: "flex justify-between items-center gap-2",
|
|
259
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
260
|
+
className: "text-[14px] text-[#667781] truncate",
|
|
261
|
+
children: [conversation.lastDirection === "incoming" ? "" : outgoingPrefix, conversation.lastMessagePreview || noPreviewLabel]
|
|
262
|
+
}), hasUnread && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
263
|
+
className: "bg-[#25d366] text-white text-[11px] font-semibold rounded-full min-w-[20px] h-[20px] flex items-center justify-center px-1.5 shrink-0",
|
|
264
|
+
children: conversation.unreadCount
|
|
265
|
+
})]
|
|
266
|
+
})]
|
|
267
|
+
})]
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function formatPhone(phone) {
|
|
271
|
+
if (phone.length === 13 && phone.startsWith("55")) return `(${phone.slice(2, 4)}) ${phone.slice(4, 9)}-${phone.slice(9)}`;
|
|
272
|
+
return phone;
|
|
273
|
+
}
|
|
274
|
+
function formatTimeDefault(dateStr, yesterdayLabel) {
|
|
275
|
+
try {
|
|
276
|
+
const date = new Date(dateStr);
|
|
277
|
+
const now = /* @__PURE__ */ new Date();
|
|
278
|
+
if (date.toDateString() === now.toDateString()) return date.toLocaleTimeString("pt-BR", {
|
|
279
|
+
hour: "2-digit",
|
|
280
|
+
minute: "2-digit"
|
|
281
|
+
});
|
|
282
|
+
const yesterday = new Date(now);
|
|
283
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
284
|
+
if (date.toDateString() === yesterday.toDateString()) return yesterdayLabel;
|
|
285
|
+
return date.toLocaleDateString("pt-BR", {
|
|
286
|
+
day: "2-digit",
|
|
287
|
+
month: "2-digit"
|
|
288
|
+
});
|
|
289
|
+
} catch {
|
|
290
|
+
return "";
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
294
|
+
Object.defineProperty(exports, "ConversationFilterChips", {
|
|
295
|
+
enumerable: true,
|
|
296
|
+
get: function() {
|
|
297
|
+
return ConversationFilterChips;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
Object.defineProperty(exports, "ConversationItem", {
|
|
301
|
+
enumerable: true,
|
|
302
|
+
get: function() {
|
|
303
|
+
return ConversationItem;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
Object.defineProperty(exports, "ConversationList", {
|
|
307
|
+
enumerable: true,
|
|
308
|
+
get: function() {
|
|
309
|
+
return ConversationList;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
require("./whatsapp-dashboard.cjs");
|
|
4
|
+
const require_conversation_list = require("./conversation-list-CVWdddJh.cjs");
|
|
5
|
+
exports.ConversationItem = require_conversation_list.ConversationItem;
|
|
6
|
+
exports.ConversationList = require_conversation_list.ConversationList;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as ConversationListProps, i as ConversationListLabels, n as ConversationItemProps, r as ConversationList, t as ConversationItem } from "./conversation-list-BlhJhDsc.cjs";
|
|
2
|
+
export { ConversationItem, ConversationItemProps, ConversationList, ConversationListLabels, ConversationListProps };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as ConversationListProps, i as ConversationListLabels, n as ConversationItemProps, r as ConversationList, t as ConversationItem } from "./conversation-list-BwQ4oK2J.mjs";
|
|
2
|
+
export { ConversationItem, ConversationItemProps, ConversationList, ConversationListLabels, ConversationListProps };
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
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_bubble = require("./bubble.cjs");
|
|
6
|
+
const require_message = require("./message.cjs");
|
|
7
|
+
const require_message_bubble = require("./message-bubble.cjs");
|
|
8
|
+
const require_message_view = require("./message-view.cjs");
|
|
9
|
+
const require_conversation_list = require("./conversation-list-CVWdddJh.cjs");
|
|
10
|
+
const require_composer = require("./composer.cjs");
|
|
11
|
+
const require_message_input = require("./message-input-DcEzHQ9t.cjs");
|
|
12
|
+
exports.Bubble = require_bubble.Bubble;
|
|
13
|
+
exports.BubbleContent = require_bubble.BubbleContent;
|
|
14
|
+
exports.BubbleGroup = require_bubble.BubbleGroup;
|
|
15
|
+
exports.BubbleReactions = require_bubble.BubbleReactions;
|
|
16
|
+
exports.Composer = require_composer.Composer;
|
|
17
|
+
exports.ComposerButton = require_composer.ComposerButton;
|
|
18
|
+
exports.ComposerError = require_composer.ComposerError;
|
|
19
|
+
exports.ComposerSend = require_composer.ComposerSend;
|
|
20
|
+
exports.ComposerTextarea = require_composer.ComposerTextarea;
|
|
21
|
+
exports.ConversationFilterChips = require_conversation_list.ConversationFilterChips;
|
|
22
|
+
exports.ConversationItem = require_conversation_list.ConversationItem;
|
|
23
|
+
exports.ConversationList = require_conversation_list.ConversationList;
|
|
24
|
+
exports.DateDivider = require_message_view.DateDivider;
|
|
25
|
+
exports.FormattedMessage = require_message_bubble.FormattedMessage;
|
|
26
|
+
exports.FreeformWindowClosedError = require_message_input.FreeformWindowClosedError;
|
|
27
|
+
exports.Message = require_message.Message;
|
|
28
|
+
exports.MessageAvatar = require_message.MessageAvatar;
|
|
29
|
+
exports.MessageBubble = require_message_bubble.MessageBubble;
|
|
30
|
+
exports.MessageContent = require_message.MessageContent;
|
|
31
|
+
exports.MessageFooter = require_message.MessageFooter;
|
|
32
|
+
exports.MessageGroup = require_message.MessageGroup;
|
|
33
|
+
exports.MessageHeader = require_message.MessageHeader;
|
|
34
|
+
exports.MessageInput = require_message_input.MessageInput;
|
|
35
|
+
exports.MessageList = require_message_view.MessageList;
|
|
36
|
+
exports.MessageView = require_message_view.MessageView;
|
|
37
|
+
exports.MessageViewContent = require_message_view.MessageViewContent;
|
|
38
|
+
exports.MessageViewEmpty = require_message_view.MessageViewEmpty;
|
|
39
|
+
exports.MessageViewHeader = require_message_view.MessageViewHeader;
|
|
40
|
+
exports.WhatsappDashboard = require_whatsapp_dashboard.WhatsappDashboard;
|
|
41
|
+
exports.cn = require_utils.cn;
|
|
42
|
+
exports.getDisplayDate = require_utils.getDisplayDate;
|
|
43
|
+
exports.renderSlot = require_utils.renderSlot;
|
|
44
|
+
exports.useComposer = require_composer.useComposer;
|
|
45
|
+
exports.useComposerState = require_composer.useComposerState;
|
|
46
|
+
exports.useComposerValue = require_composer.useComposerValue;
|
|
47
|
+
exports.useFreeformMessageWindow = require_message_input.useFreeformMessageWindow;
|
|
48
|
+
exports.useOptionalWhatsappDashboard = require_whatsapp_dashboard.useOptionalWhatsappDashboard;
|
|
49
|
+
exports.useWhatsappDashboard = require_whatsapp_dashboard.useWhatsappDashboard;
|