whatsapp-ui-react 0.0.3 → 0.0.5
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 +120 -57
- package/dist/components/Animated/Animated.d.ts +17 -0
- package/dist/components/Animated/Animated.d.ts.map +1 -0
- package/dist/components/Animated/index.d.ts +3 -0
- package/dist/components/Animated/index.d.ts.map +1 -0
- package/dist/components/Chat/Chat.d.ts +15 -3
- package/dist/components/Chat/Chat.d.ts.map +1 -1
- package/dist/components/Chat/Header.d.ts.map +1 -1
- package/dist/components/History/History.d.ts +16 -0
- package/dist/components/History/History.d.ts.map +1 -0
- package/dist/components/History/index.d.ts +3 -0
- package/dist/components/History/index.d.ts.map +1 -0
- package/dist/components/Message/Message.d.ts +17 -6
- package/dist/components/Message/Message.d.ts.map +1 -1
- package/dist/components/Message/MessageContext.d.ts +1 -4
- package/dist/components/Message/MessageContext.d.ts.map +1 -1
- package/dist/components/Message/Voice/Waveform.d.ts +1 -1
- package/dist/components/Message/Voice/Waveform.d.ts.map +1 -1
- package/dist/hooks/useMessages.d.ts +2 -8
- package/dist/hooks/useMessages.d.ts.map +1 -1
- package/dist/index.cjs +156 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +156 -25
- package/dist/index.js.map +1 -1
- package/dist/style.css +688 -0
- package/dist/utils/groupMessages.d.ts +1 -1
- package/package.json +8 -5
- package/src/css/dark.css +31 -0
- package/src/css/light.css +31 -0
- package/src/css/preset.css +1 -0
- package/dist/tailwind.css +0 -2
package/dist/index.cjs
CHANGED
|
@@ -3360,7 +3360,7 @@ function StatusIcon({ status, className }) {
|
|
|
3360
3360
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3361
3361
|
StatusDoubleCheckIcon,
|
|
3362
3362
|
{
|
|
3363
|
-
className: cn("size-4", status === "read" && "text-wa-
|
|
3363
|
+
className: cn("size-4", status === "read" && "text-wa-teal", className)
|
|
3364
3364
|
}
|
|
3365
3365
|
);
|
|
3366
3366
|
}
|
|
@@ -3396,8 +3396,8 @@ function Message({
|
|
|
3396
3396
|
"div",
|
|
3397
3397
|
{
|
|
3398
3398
|
className: cn(
|
|
3399
|
-
"relative w-fit rounded-
|
|
3400
|
-
isOut ? "bg-wa-bubble-out text-wa-text
|
|
3399
|
+
"relative w-fit rounded-[0.525rem] px-3 py-1.5 shadow-md",
|
|
3400
|
+
isOut ? "bg-wa-bubble-out text-wa-text" : "bg-wa-bubble-in text-wa-text",
|
|
3401
3401
|
top && isOut && "rounded-tr-none",
|
|
3402
3402
|
top && !isOut && "rounded-tl-none"
|
|
3403
3403
|
),
|
|
@@ -3652,9 +3652,9 @@ function Audio({ src, duration, fileName }) {
|
|
|
3652
3652
|
if (progress > 0) setHasPlayed(true);
|
|
3653
3653
|
}, [progress]);
|
|
3654
3654
|
const displayDuration = totalDuration > 0 ? fmtTime(remaining) : duration ?? "0:00";
|
|
3655
|
-
const trackColor = isOut ? "bg-wa-
|
|
3656
|
-
const trackColorFaint = isOut ? "bg-wa-
|
|
3657
|
-
const dotColor = hasPlayed || isOut ? "bg-wa-
|
|
3655
|
+
const trackColor = isOut ? "bg-wa-teal" : "bg-wa-text-secondary";
|
|
3656
|
+
const trackColorFaint = isOut ? "bg-wa-teal/40" : "bg-wa-text-secondary/40";
|
|
3657
|
+
const dotColor = hasPlayed || isOut ? "bg-wa-teal" : "bg-wa-text-secondary";
|
|
3658
3658
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3659
3659
|
/* @__PURE__ */ jsxRuntime.jsx("audio", { ref: audioRef, src, preload: "metadata" }),
|
|
3660
3660
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-13.75 w-84 min-w-84 shrink-0 items-center", children: [
|
|
@@ -3741,9 +3741,12 @@ const CANVAS_HEIGHT = 30;
|
|
|
3741
3741
|
const BAR_MAX_HEIGHT = 26;
|
|
3742
3742
|
const BAR_WIDTH = 3;
|
|
3743
3743
|
const SLOT_WIDTH = CANVAS_WIDTH / BAR_COUNT;
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3744
|
+
const COLORS = {
|
|
3745
|
+
waveformOut: "#53bdeb",
|
|
3746
|
+
waveformIn: "#8696a0",
|
|
3747
|
+
waveformOutFaint: "rgba(83, 189, 235, 0.4)",
|
|
3748
|
+
waveformInFaint: "rgba(134, 150, 160, 0.4)"
|
|
3749
|
+
};
|
|
3747
3750
|
function Waveform({ bars, progress, isOut, hasPlayed, seek }) {
|
|
3748
3751
|
const canvasRef = React.useRef(null);
|
|
3749
3752
|
React.useEffect(() => {
|
|
@@ -3752,8 +3755,8 @@ function Waveform({ bars, progress, isOut, hasPlayed, seek }) {
|
|
|
3752
3755
|
const ctx = canvas.getContext("2d");
|
|
3753
3756
|
if (!ctx) return;
|
|
3754
3757
|
ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
|
|
3755
|
-
const colorPlayed = isOut ?
|
|
3756
|
-
const colorUnplayed = isOut ?
|
|
3758
|
+
const colorPlayed = isOut ? COLORS.waveformOut : COLORS.waveformIn;
|
|
3759
|
+
const colorUnplayed = isOut ? COLORS.waveformOutFaint : COLORS.waveformInFaint;
|
|
3757
3760
|
bars.forEach((amplitude, i) => {
|
|
3758
3761
|
const level = Math.max(1, amplitude);
|
|
3759
3762
|
const barHeight = Math.round(level / 10 * BAR_MAX_HEIGHT);
|
|
@@ -3781,7 +3784,7 @@ function Waveform({ bars, progress, isOut, hasPlayed, seek }) {
|
|
|
3781
3784
|
"aria-hidden": "true",
|
|
3782
3785
|
className: cn(
|
|
3783
3786
|
"pointer-events-none absolute top-1/2 size-3 -translate-x-1/2 -translate-y-1/2 rounded-full",
|
|
3784
|
-
hasPlayed || isOut ? "bg-wa-
|
|
3787
|
+
hasPlayed || isOut ? "bg-wa-teal" : "bg-wa-text-secondary"
|
|
3785
3788
|
),
|
|
3786
3789
|
style: { left: `${progress * 100}%` }
|
|
3787
3790
|
}
|
|
@@ -3899,7 +3902,7 @@ function Gif({ className }) {
|
|
|
3899
3902
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-[336px]", className) });
|
|
3900
3903
|
}
|
|
3901
3904
|
function Image({ src, alt = "", className }) {
|
|
3902
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-[336px]", className), children: /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, className: "w-full rounded-
|
|
3905
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-[336px]", className), children: /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, className: "w-full rounded-[0.525rem] object-cover" }) });
|
|
3903
3906
|
}
|
|
3904
3907
|
let _counter = 0;
|
|
3905
3908
|
function uid() {
|
|
@@ -3929,13 +3932,13 @@ function ChatHeader({ className, name, avatarUrl, subtitle }) {
|
|
|
3929
3932
|
"div",
|
|
3930
3933
|
{
|
|
3931
3934
|
className: cn(
|
|
3932
|
-
"flex items-center gap-3 bg-wa-
|
|
3935
|
+
"flex items-center gap-3 bg-wa-bg px-4 py-3 shadow-[0_1px_4px_rgba(0,0,0,0.15)]",
|
|
3933
3936
|
className
|
|
3934
3937
|
),
|
|
3935
3938
|
children: [
|
|
3936
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: avatarUrl, alt: name, className: "size-10 rounded-full object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-10 items-center justify-center rounded-full bg-wa-avatar text-sm font-medium text-wa-text
|
|
3939
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: avatarUrl, alt: name, className: "size-10 rounded-full object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-10 items-center justify-center rounded-full bg-wa-avatar text-sm font-medium text-wa-text", children: initials }) }),
|
|
3937
3940
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
3938
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[15px] font-medium text-wa-text
|
|
3941
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[15px] font-medium text-wa-text", children: name }),
|
|
3939
3942
|
subtitle && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-wa-text-secondary", children: subtitle })
|
|
3940
3943
|
] })
|
|
3941
3944
|
]
|
|
@@ -3953,7 +3956,7 @@ function ActionButton({
|
|
|
3953
3956
|
type: "button",
|
|
3954
3957
|
onClick,
|
|
3955
3958
|
"aria-label": label,
|
|
3956
|
-
className: "inline-flex size-8 shrink-0 items-center justify-center rounded-full text-wa-icon transition-colors hover:text-wa-text
|
|
3959
|
+
className: "inline-flex size-8 shrink-0 items-center justify-center rounded-full text-wa-icon transition-colors hover:text-wa-text",
|
|
3957
3960
|
children
|
|
3958
3961
|
}
|
|
3959
3962
|
);
|
|
@@ -3989,7 +3992,7 @@ function InputfieldActions({
|
|
|
3989
3992
|
type: "button",
|
|
3990
3993
|
onClick: onMicClick,
|
|
3991
3994
|
"aria-label": "Record voice message",
|
|
3992
|
-
className: "group inline-flex size-8 shrink-0 items-center justify-center rounded-full text-wa-icon transition-colors hover:bg-wa-
|
|
3995
|
+
className: "group inline-flex size-8 shrink-0 items-center justify-center rounded-full text-wa-icon transition-colors hover:bg-wa-hover hover:text-black",
|
|
3993
3996
|
children: [
|
|
3994
3997
|
/* @__PURE__ */ jsxRuntime.jsx(MicOutlineIcon, { className: "size-5 group-hover:hidden" }),
|
|
3995
3998
|
/* @__PURE__ */ jsxRuntime.jsx(MicFillIcon, { className: "hidden size-5 group-hover:block" })
|
|
@@ -4114,7 +4117,7 @@ function getDisplayDate(date) {
|
|
|
4114
4117
|
}
|
|
4115
4118
|
function DayDivider({ className, date }) {
|
|
4116
4119
|
const displayDate = getDisplayDate(date.toISOString());
|
|
4117
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex justify-center py-3", className), children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-
|
|
4120
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex justify-center py-3", className), children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-[0.325rem] bg-wa-divider px-3 py-1 text-[13px] font-medium text-wa-text-secondary shadow-sm", children: displayDate }) });
|
|
4118
4121
|
}
|
|
4119
4122
|
function MessageList({ className, messages }) {
|
|
4120
4123
|
const dayGroups = groupMessagesByDay(messages);
|
|
@@ -4157,15 +4160,22 @@ const Chat$1 = React.forwardRef(function Chat2({
|
|
|
4157
4160
|
onEmojiClick,
|
|
4158
4161
|
onAttachClick,
|
|
4159
4162
|
onCameraClick,
|
|
4160
|
-
onMicClick
|
|
4163
|
+
onMicClick,
|
|
4164
|
+
theme
|
|
4161
4165
|
}, ref) {
|
|
4162
4166
|
const [messages, setMessages] = React.useState(messageHistory ?? []);
|
|
4163
4167
|
const scrollRef = React.useRef(null);
|
|
4168
|
+
const contentRef = React.useRef(null);
|
|
4164
4169
|
React.useEffect(() => {
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4170
|
+
const content = contentRef.current;
|
|
4171
|
+
const scroll = scrollRef.current;
|
|
4172
|
+
if (!content || !scroll) return;
|
|
4173
|
+
const ro = new ResizeObserver(() => {
|
|
4174
|
+
scroll.scrollTop = scroll.scrollHeight;
|
|
4175
|
+
});
|
|
4176
|
+
ro.observe(content);
|
|
4177
|
+
return () => ro.disconnect();
|
|
4178
|
+
}, []);
|
|
4169
4179
|
function sendMessage(text) {
|
|
4170
4180
|
const trimmed = text.trim();
|
|
4171
4181
|
if (!trimmed) return;
|
|
@@ -4203,6 +4213,7 @@ const Chat$1 = React.forwardRef(function Chat2({
|
|
|
4203
4213
|
{
|
|
4204
4214
|
className: cn("flex h-full min-h-0 flex-col", isDefaultBg ? "bg-wa-bg" : "", className),
|
|
4205
4215
|
style: isDefaultBg ? void 0 : bgStyle,
|
|
4216
|
+
...theme ? { "data-wa-theme": theme } : {},
|
|
4206
4217
|
children: [
|
|
4207
4218
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4208
4219
|
ChatHeader,
|
|
@@ -4220,7 +4231,17 @@ const Chat$1 = React.forwardRef(function Chat2({
|
|
|
4220
4231
|
style: { backgroundImage: `url(${backgroundUrl})` }
|
|
4221
4232
|
}
|
|
4222
4233
|
),
|
|
4223
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4234
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4235
|
+
"div",
|
|
4236
|
+
{
|
|
4237
|
+
ref: scrollRef,
|
|
4238
|
+
className: "flex-1 overflow-y-auto py-2 px-12 [scrollbar-width:thin] [scrollbar-color:rgba(255,255,255,0.1)_transparent] [&::-webkit-scrollbar]:w-1 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-[rgba(255,255,255,0.18)] [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb:hover]:bg-[rgba(255,255,255,0.3)]",
|
|
4239
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: contentRef, children: [
|
|
4240
|
+
children,
|
|
4241
|
+
messages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(MessageList, { messages })
|
|
4242
|
+
] })
|
|
4243
|
+
}
|
|
4244
|
+
),
|
|
4224
4245
|
showInputfield && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4225
4246
|
Inputfield,
|
|
4226
4247
|
{
|
|
@@ -4252,6 +4273,114 @@ const ChatParts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
|
|
|
4252
4273
|
Root: Chat$1
|
|
4253
4274
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
4254
4275
|
const Chat = Object.assign(Chat$1, ChatParts);
|
|
4276
|
+
function Animated({ delay = 0, children, className }) {
|
|
4277
|
+
const [mounted, setMounted] = React.useState(delay === 0);
|
|
4278
|
+
const [visible, setVisible] = React.useState(false);
|
|
4279
|
+
React.useEffect(() => {
|
|
4280
|
+
if (delay === 0) {
|
|
4281
|
+
const raf = requestAnimationFrame(() => setVisible(true));
|
|
4282
|
+
return () => cancelAnimationFrame(raf);
|
|
4283
|
+
}
|
|
4284
|
+
const timer = setTimeout(() => setMounted(true), delay);
|
|
4285
|
+
return () => clearTimeout(timer);
|
|
4286
|
+
}, [delay]);
|
|
4287
|
+
React.useLayoutEffect(() => {
|
|
4288
|
+
if (mounted && delay !== 0) {
|
|
4289
|
+
const raf = requestAnimationFrame(() => setVisible(true));
|
|
4290
|
+
return () => cancelAnimationFrame(raf);
|
|
4291
|
+
}
|
|
4292
|
+
}, [mounted, delay]);
|
|
4293
|
+
if (!mounted) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
4294
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4295
|
+
"div",
|
|
4296
|
+
{
|
|
4297
|
+
className: cn(
|
|
4298
|
+
"transition-all duration-300 ease-out",
|
|
4299
|
+
visible ? "translate-y-0 opacity-100" : "translate-y-2 opacity-0",
|
|
4300
|
+
className
|
|
4301
|
+
),
|
|
4302
|
+
children
|
|
4303
|
+
}
|
|
4304
|
+
);
|
|
4305
|
+
}
|
|
4306
|
+
function History({ children, className }) {
|
|
4307
|
+
const childArray = React.Children.toArray(children ?? []);
|
|
4308
|
+
const result = [];
|
|
4309
|
+
let lastGroupKey = void 0;
|
|
4310
|
+
let lastDate = null;
|
|
4311
|
+
const today = /* @__PURE__ */ new Date();
|
|
4312
|
+
let hasAnyMessage = false;
|
|
4313
|
+
function extractMessageInfo(node) {
|
|
4314
|
+
if (!React.isValidElement(node)) return null;
|
|
4315
|
+
const el = node;
|
|
4316
|
+
if (el.type === Message) {
|
|
4317
|
+
return {
|
|
4318
|
+
msgProps: el.props,
|
|
4319
|
+
isAnimated: false,
|
|
4320
|
+
msgEl: el
|
|
4321
|
+
};
|
|
4322
|
+
}
|
|
4323
|
+
if (el.type === Animated) {
|
|
4324
|
+
const innerChildren = React.Children.toArray(
|
|
4325
|
+
el.props.children ?? []
|
|
4326
|
+
);
|
|
4327
|
+
const innerMsg = innerChildren.find(
|
|
4328
|
+
(c) => React.isValidElement(c) && c.type === Message
|
|
4329
|
+
);
|
|
4330
|
+
if (innerMsg) {
|
|
4331
|
+
return {
|
|
4332
|
+
msgProps: innerMsg.props,
|
|
4333
|
+
isAnimated: true,
|
|
4334
|
+
animatedEl: el,
|
|
4335
|
+
msgEl: innerMsg
|
|
4336
|
+
};
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
return null;
|
|
4340
|
+
}
|
|
4341
|
+
for (const child of childArray) {
|
|
4342
|
+
const info = extractMessageInfo(child);
|
|
4343
|
+
if (!info) {
|
|
4344
|
+
result.push(child);
|
|
4345
|
+
continue;
|
|
4346
|
+
}
|
|
4347
|
+
hasAnyMessage = true;
|
|
4348
|
+
const { msgProps, isAnimated } = info;
|
|
4349
|
+
const groupKey = msgProps.senderId ?? msgProps.direction;
|
|
4350
|
+
const currentDate = toDate(msgProps.timestamp);
|
|
4351
|
+
const isNewDay = currentDate !== null && (lastDate === null || !isSameCalendarDay(currentDate, lastDate));
|
|
4352
|
+
if (isNewDay && currentDate) {
|
|
4353
|
+
result.push(/* @__PURE__ */ jsxRuntime.jsx(DayDivider, { date: currentDate }, `divider-${currentDate.toISOString()}`));
|
|
4354
|
+
}
|
|
4355
|
+
const isNewGroup = lastGroupKey === void 0 || groupKey !== lastGroupKey || isNewDay;
|
|
4356
|
+
if (isAnimated) {
|
|
4357
|
+
const { animatedEl, msgEl } = info;
|
|
4358
|
+
const newMsg = React.cloneElement(msgEl, {
|
|
4359
|
+
top: isNewGroup
|
|
4360
|
+
});
|
|
4361
|
+
result.push(
|
|
4362
|
+
React.cloneElement(animatedEl, {
|
|
4363
|
+
children: newMsg
|
|
4364
|
+
})
|
|
4365
|
+
);
|
|
4366
|
+
} else {
|
|
4367
|
+
result.push(
|
|
4368
|
+
React.cloneElement(info.msgEl, {
|
|
4369
|
+
top: isNewGroup
|
|
4370
|
+
})
|
|
4371
|
+
);
|
|
4372
|
+
}
|
|
4373
|
+
lastGroupKey = groupKey;
|
|
4374
|
+
if (currentDate) lastDate = currentDate;
|
|
4375
|
+
}
|
|
4376
|
+
if (!hasAnyMessage) {
|
|
4377
|
+
result.push(/* @__PURE__ */ jsxRuntime.jsx(DayDivider, { date: today }, "history-today-divider"));
|
|
4378
|
+
}
|
|
4379
|
+
if (className) {
|
|
4380
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(className), children: result });
|
|
4381
|
+
}
|
|
4382
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: result });
|
|
4383
|
+
}
|
|
4255
4384
|
function extractTextFromNode(node) {
|
|
4256
4385
|
if (node === null || node === void 0) return "";
|
|
4257
4386
|
if (typeof node === "string") return node;
|
|
@@ -4304,6 +4433,7 @@ function useMessages(callback) {
|
|
|
4304
4433
|
for (const m of newMessages) callbackRef.current(m);
|
|
4305
4434
|
}, [messages]);
|
|
4306
4435
|
}
|
|
4436
|
+
exports.Animated = Animated;
|
|
4307
4437
|
exports.Audio = Audio;
|
|
4308
4438
|
exports.Chat = Chat;
|
|
4309
4439
|
exports.Contact = Contact;
|
|
@@ -4311,6 +4441,7 @@ exports.Emoji = Emoji;
|
|
|
4311
4441
|
exports.Event = Event;
|
|
4312
4442
|
exports.File = File;
|
|
4313
4443
|
exports.Gif = Gif;
|
|
4444
|
+
exports.History = History;
|
|
4314
4445
|
exports.Image = Image;
|
|
4315
4446
|
exports.Location = Location;
|
|
4316
4447
|
exports.Message = Message;
|