@cometchat/chat-uikit-react
Version:
Ready-to-use Chat UI Components for React
874 lines (869 loc) • 32.2 kB
JavaScript
import './index.css';
import { CometChatPopover, useCometChatPopoverContext } from './chunk-GI7Y3RZV.js';
import { CometChatAvatar } from './chunk-LDXTHTWH.js';
import { useLocale } from './chunk-UVWJYQJ3.js';
import React, { createContext, useCallback, useContext, useRef, useEffect, useMemo, useReducer, useState } from 'react';
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import { CometChat } from '@cometchat/chat-sdk-javascript';
var CometChatReactionsContext = createContext(null);
function useCometChatReactionsContext() {
const ctx = useContext(CometChatReactionsContext);
if (!ctx) {
throw new Error("useCometChatReactionsContext must be used within <CometChatReactions.Root>");
}
return ctx;
}
var CometChatReactionsChip = React.memo(
function CometChatReactionsChip2({ reaction, className }) {
const { onReactionClick } = useCometChatReactionsContext();
const { getLocalizedString } = useLocale();
const emoji = reaction.getReaction();
const count = reaction.getCount();
const reactedByMe = reaction.getReactedByMe();
const handleClick = useCallback(() => {
onReactionClick(emoji);
}, [emoji, onReactionClick]);
const chipClass = [
"cometchat-reactions__chip",
reactedByMe ? "cometchat-reactions__chip--active" : "",
className ?? ""
].filter(Boolean).join(" ");
return /* @__PURE__ */ jsxs(
"button",
{
type: "button",
className: chipClass,
onClick: handleClick,
"aria-label": getLocalizedString("accessibility_reacted_by").replace("{emoji}", emoji).replace("{count}", String(count)),
"aria-pressed": reactedByMe,
children: [
/* @__PURE__ */ jsx("span", { className: "cometchat-reactions__chip-emoji", children: emoji }),
/* @__PURE__ */ jsx("span", { className: "cometchat-reactions__chip-count", children: count })
]
}
);
},
(prev, next) => prev.reaction.getReaction() === next.reaction.getReaction() && prev.reaction.getCount() === next.reaction.getCount() && prev.reaction.getReactedByMe() === next.reaction.getReactedByMe() && prev.className === next.className
);
var CometChatReactionsOverflow = ({
count,
className
}) => {
const { getLocalizedString } = useLocale();
const overflowClass = ["cometchat-reactions__overflow", className ?? ""].filter(Boolean).join(" ");
return /* @__PURE__ */ jsx(
"button",
{
type: "button",
className: overflowClass,
"aria-label": getLocalizedString("accessibility_more_reactions").replace(
"{count}",
String(count)
),
children: /* @__PURE__ */ jsxs("span", { className: "cometchat-reactions__overflow-count", children: [
"+",
count
] })
}
);
};
var INFO_LIMIT = 3;
var CometChatReactionsInfo = ({
emoji,
className
}) => {
const { message, reactionsRequestBuilder } = useCometChatReactionsContext();
const { getLocalizedString } = useLocale();
const [names, setNames] = useState([]);
const [totalCount, setTotalCount] = useState(0);
const [infoState, setInfoState] = useState("idle");
const fetchedRef = useRef(false);
useEffect(() => {
const reactions = message.getReactions();
const match = reactions.find((r) => r.getReaction() === emoji);
setTotalCount(match ? match.getCount() : 0);
}, [message, emoji]);
const fetchReactorNames = useCallback(async () => {
if (fetchedRef.current) return;
fetchedRef.current = true;
if (!CometChat.isInitialized()) {
setInfoState("loaded");
return;
}
setInfoState("loading");
try {
const messageId = message.getId();
if (!messageId) {
setInfoState("loaded");
return;
}
const builder = reactionsRequestBuilder ?? new CometChat.ReactionsRequestBuilder().setLimit(INFO_LIMIT);
builder.setMessageId(messageId);
builder.setReaction(emoji);
const request = builder.build();
const reactors = await request.fetchNext();
let loggedInUid;
try {
const loggedInUser = await CometChat.getLoggedinUser();
loggedInUid = loggedInUser?.getUid();
} catch {
}
const fetchedNames = [];
for (const reactor of reactors) {
const reactedBy = reactor.getReactedBy();
if (loggedInUid && reactedBy.getUid() === loggedInUid) {
fetchedNames.unshift(getLocalizedString("reaction_popup_you") || "You");
} else {
fetchedNames.push(reactedBy.getName());
}
}
setNames(fetchedNames);
setInfoState("loaded");
} catch {
setInfoState("loaded");
}
}, [message, emoji, reactionsRequestBuilder, getLocalizedString]);
useEffect(() => {
void fetchReactorNames();
}, [fetchReactorNames]);
const infoClass = ["cometchat-reactions__info", className ?? ""].filter(Boolean).join(" ");
const pendingCount = totalCount - names.length;
return /* @__PURE__ */ jsx("div", { className: infoClass, role: "tooltip", children: /* @__PURE__ */ jsxs("div", { className: "cometchat-reactions__info-content", children: [
infoState === "loading" && /* @__PURE__ */ jsx("div", { className: "cometchat-reactions__info-loading" }),
infoState === "error" && /* @__PURE__ */ jsx("div", { className: "cometchat-reactions__info-error", children: getLocalizedString("error_text") || "Failed to load" }),
infoState === "loaded" && /* @__PURE__ */ jsxs("div", { className: "cometchat-reactions__info-emoji-text", children: [
/* @__PURE__ */ jsx("span", { className: "cometchat-reactions__info-emoji", children: emoji }),
/* @__PURE__ */ jsxs("div", { children: [
/* @__PURE__ */ jsxs("div", { className: "cometchat-reactions__info-title", children: [
names.join(", "),
pendingCount > 0 && ` ${getLocalizedString("reaction_popup_and") || "and"} ${String(pendingCount)} ${getLocalizedString("reaction_popup_others") || "others"}`
] }),
/* @__PURE__ */ jsx("div", { className: "cometchat-reactions__info-description", children: getLocalizedString("reaction_reacted") || "reacted" })
] })
] })
] }) });
};
var CometChatReactionListContext = createContext(
null
);
function useCometChatReactionListContext() {
const ctx = useContext(CometChatReactionListContext);
if (!ctx) {
throw new Error(
"useCometChatReactionListContext must be used within <CometChatReactionList.Root>"
);
}
return ctx;
}
var DEFAULT_LIMIT = 20;
var CometChatReactionListManager = class {
constructor(messageId, builder) {
this.request = null;
if (!CometChat.isInitialized()) {
this.request = null;
return;
}
try {
const requestBuilder = builder ?? new CometChat.ReactionsRequestBuilder().setLimit(DEFAULT_LIMIT);
requestBuilder.setMessageId(messageId);
this.request = requestBuilder.build();
} catch {
this.request = null;
}
}
/** Fetch the next page of reactions. Returns empty array when exhausted. */
fetchNext() {
if (!this.request) return Promise.resolve([]);
return this.request.fetchNext();
}
};
// src/components/CometChatReactionList/CometChatReactionList.reducer.ts
function groupByEmoji(reactions) {
const map = /* @__PURE__ */ new Map();
for (const reaction of reactions) {
const emoji = reaction.getReaction();
const existing = map.get(emoji) ?? [];
map.set(emoji, [...existing, reaction]);
}
return map;
}
var initialReactionListState = {
allReactions: [],
groupedReactions: /* @__PURE__ */ new Map(),
selectedEmoji: null,
fetchState: "idle",
hasMore: true,
isFetching: false
};
function reactionListReducer(state, action) {
switch (action.type) {
case "FETCH_START":
return {
...state,
isFetching: true,
// Only set fetchState to loading if we have no data yet
fetchState: state.allReactions.length === 0 ? "loading" : state.fetchState
};
case "FETCH_SUCCESS": {
const merged = [...state.allReactions, ...action.reactions];
const grouped = groupByEmoji(merged);
return {
...state,
allReactions: merged,
groupedReactions: grouped,
fetchState: merged.length === 0 ? "empty" : "loaded",
hasMore: action.hasMore,
isFetching: false
};
}
case "FETCH_ERROR":
return {
...state,
fetchState: "error",
isFetching: false
};
case "SELECT_EMOJI":
return {
...state,
selectedEmoji: action.emoji
};
case "REMOVE_REACTION": {
const filtered = state.allReactions.filter((r) => {
const key = `${r.getReactedBy().getUid()}-${r.getReaction()}`;
return key !== action.reactionId;
});
const grouped = groupByEmoji(filtered);
let selectedEmoji = state.selectedEmoji;
if (selectedEmoji !== null && !grouped.has(selectedEmoji)) {
selectedEmoji = null;
}
return {
...state,
allReactions: filtered,
groupedReactions: grouped,
selectedEmoji,
fetchState: filtered.length === 0 ? "empty" : state.fetchState
};
}
case "RESET":
return initialReactionListState;
default:
return state;
}
}
// src/components/CometChatReactionList/useCometChatReactionList.ts
var DEFAULT_LIMIT2 = 20;
function useCometChatReactionList(options) {
const { message, reactionsRequestBuilder, onItemClick, onEmpty, onError } = options;
const [state, dispatch] = useReducer(reactionListReducer, initialReactionListState);
const managerRef = useRef(null);
const fetchIdRef = useRef("");
const loggedInUserUidRef = useRef("");
const [loggedInUserUid, setLoggedInUserUid] = useStateRef("");
useEffect(() => {
CometChat.getLoggedinUser().then((user) => {
if (user) {
loggedInUserUidRef.current = user.getUid();
setLoggedInUserUid(user.getUid());
}
}).catch(() => {
});
}, []);
const handleError = useCallback(
(error) => {
onError?.(error);
dispatch({ type: "FETCH_ERROR" });
},
[onError]
);
const fetchMore = useCallback(async () => {
if (!managerRef.current || !state.hasMore || state.isFetching) return;
const currentFetchId = `fetch_${String(Date.now())}`;
fetchIdRef.current = currentFetchId;
dispatch({ type: "FETCH_START" });
try {
const reactions = await managerRef.current.fetchNext();
if (fetchIdRef.current !== currentFetchId) return;
dispatch({
type: "FETCH_SUCCESS",
reactions,
hasMore: reactions.length >= DEFAULT_LIMIT2
});
} catch (error) {
if (fetchIdRef.current !== currentFetchId) return;
handleError(error);
}
}, [state.hasMore, state.isFetching, handleError]);
useEffect(() => {
const messageId = message.getId();
if (!messageId) return;
managerRef.current = new CometChatReactionListManager(messageId, reactionsRequestBuilder);
dispatch({ type: "RESET" });
const currentFetchId = `fetch_${String(Date.now())}`;
fetchIdRef.current = currentFetchId;
dispatch({ type: "FETCH_START" });
managerRef.current.fetchNext().then((reactions) => {
if (fetchIdRef.current !== currentFetchId) return;
dispatch({
type: "FETCH_SUCCESS",
reactions,
hasMore: reactions.length >= DEFAULT_LIMIT2
});
}).catch((error) => {
if (fetchIdRef.current !== currentFetchId) return;
handleError(error);
});
}, [message.getId(), reactionsRequestBuilder]);
const selectEmoji = useCallback((emoji) => {
dispatch({ type: "SELECT_EMOJI", emoji });
}, []);
const isCurrentUser = useCallback(
(reaction) => {
const uid = loggedInUserUidRef.current || loggedInUserUid;
return reaction.getReactedBy().getUid() === uid;
},
[loggedInUserUid]
);
const handleItemClick = useCallback(
(reaction) => {
if (!isCurrentUser(reaction)) return;
const reactionId = `${reaction.getReactedBy().getUid()}-${reaction.getReaction()}`;
dispatch({ type: "REMOVE_REACTION", reactionId });
onItemClick?.(reaction, message);
const remainingCount = state.allReactions.filter((r) => {
const key = `${r.getReactedBy().getUid()}-${r.getReaction()}`;
return key !== reactionId;
}).length;
if (remainingCount === 0) {
onEmpty?.();
}
},
[isCurrentUser, onItemClick, message, onEmpty, state.allReactions]
);
const retry = useCallback(() => {
const messageId = message.getId();
if (!messageId) return;
managerRef.current = new CometChatReactionListManager(messageId, reactionsRequestBuilder);
dispatch({ type: "RESET" });
const currentFetchId = `fetch_${String(Date.now())}`;
fetchIdRef.current = currentFetchId;
dispatch({ type: "FETCH_START" });
managerRef.current.fetchNext().then((reactions) => {
if (fetchIdRef.current !== currentFetchId) return;
dispatch({
type: "FETCH_SUCCESS",
reactions,
hasMore: reactions.length >= DEFAULT_LIMIT2
});
}).catch((error) => {
if (fetchIdRef.current !== currentFetchId) return;
handleError(error);
});
}, [message, reactionsRequestBuilder, handleError]);
const emojiTabs = useMemo(
() => Array.from(state.groupedReactions.keys()),
[state.groupedReactions]
);
const totalCount = state.allReactions.length;
const filteredReactions = useMemo(() => {
if (state.selectedEmoji === null) return state.allReactions;
return state.groupedReactions.get(state.selectedEmoji) ?? [];
}, [state.allReactions, state.groupedReactions, state.selectedEmoji]);
return {
message,
allReactions: state.allReactions,
groupedReactions: state.groupedReactions,
selectedEmoji: state.selectedEmoji,
fetchState: state.fetchState,
hasMore: state.hasMore,
isFetching: state.isFetching,
emojiTabs,
totalCount,
filteredReactions,
selectEmoji,
fetchMore,
handleItemClick,
isCurrentUser,
retry,
loggedInUserUid: loggedInUserUidRef.current || loggedInUserUid
};
}
function useStateRef(initial) {
const [value, setValue] = useReducer((_, next) => next, initial);
return [value, setValue];
}
var CometChatReactionListTabs = ({
className
}) => {
const { selectedEmoji, emojiTabs, totalCount, groupedReactions, selectEmoji } = useCometChatReactionListContext();
const { getLocalizedString } = useLocale();
const tabsContainerRef = useRef(null);
const allText = getLocalizedString("reaction_list_all") || "All";
const tabs = useMemo(
() => [
{ id: null, label: allText, count: totalCount },
...emojiTabs.map((emoji) => ({
id: emoji,
label: emoji,
count: groupedReactions.get(emoji)?.length ?? 0
}))
],
[allText, totalCount, emojiTabs, groupedReactions]
);
const handleTabKeyDown = useCallback(
(e, tabIndex) => {
let nextIndex = tabIndex;
if (e.key === "ArrowRight") {
nextIndex = (tabIndex + 1) % tabs.length;
e.preventDefault();
} else if (e.key === "ArrowLeft") {
nextIndex = (tabIndex - 1 + tabs.length) % tabs.length;
e.preventDefault();
} else if (e.key === "Home") {
nextIndex = 0;
e.preventDefault();
} else if (e.key === "End") {
nextIndex = tabs.length - 1;
e.preventDefault();
} else {
return;
}
const nextTab = tabs[nextIndex];
if (nextTab) {
selectEmoji(nextTab.id);
if (tabsContainerRef.current) {
const tabElements = tabsContainerRef.current.querySelectorAll('[role="tab"]');
tabElements[nextIndex]?.focus();
}
}
},
[tabs, selectEmoji]
);
const rootClass = ["cometchat-reaction-list__tabs", className ?? ""].filter(Boolean).join(" ");
return /* @__PURE__ */ jsx(
"div",
{
ref: tabsContainerRef,
className: rootClass,
role: "tablist",
"aria-label": getLocalizedString("accessibility_reaction_filters"),
children: tabs.map((tab, index) => {
const isActive = selectedEmoji === tab.id;
const tabClass = [
"cometchat-reaction-list__tabs-tab",
isActive ? "cometchat-reaction-list__tabs-tab--active" : ""
].filter(Boolean).join(" ");
return /* @__PURE__ */ jsxs(
"button",
{
type: "button",
role: "tab",
"aria-selected": isActive,
tabIndex: isActive ? 0 : -1,
className: tabClass,
onClick: () => {
selectEmoji(tab.id);
},
onKeyDown: (e) => {
handleTabKeyDown(e, index);
},
children: [
/* @__PURE__ */ jsx(
"span",
{
className: [
"cometchat-reaction-list__tabs-tab-emoji",
isActive ? "cometchat-reaction-list__tabs-tab-emoji--active" : ""
].filter(Boolean).join(" "),
children: tab.label
}
),
/* @__PURE__ */ jsx(
"span",
{
className: [
"cometchat-reaction-list__tabs-tab-count",
isActive ? "cometchat-reaction-list__tabs-tab-count--active" : ""
].filter(Boolean).join(" "),
children: tab.count
}
)
]
},
tab.id ?? "all"
);
})
}
);
};
var CometChatReactionListItems = ({
className
}) => {
const {
filteredReactions,
fetchState,
hasMore,
isFetching,
fetchMore,
handleItemClick,
isCurrentUser,
selectedEmoji
} = useCometChatReactionListContext();
const { getLocalizedString } = useLocale();
const sentinelRef = useRef(null);
const youText = getLocalizedString("reaction_list_you") || "You";
const clickToRemoveText = getLocalizedString("reaction_list_click_to_remove") || "Click to remove";
useEffect(() => {
const sentinel = sentinelRef.current;
if (!sentinel || !hasMore || isFetching) return;
const observer = new IntersectionObserver(
(entries) => {
const entry = entries[0];
if (entry.isIntersecting) {
void fetchMore();
}
},
{ rootMargin: "50px" }
);
observer.observe(sentinel);
return () => {
observer.disconnect();
};
}, [hasMore, isFetching, fetchMore]);
const handleKeyDown = useCallback(
(e, reaction) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleItemClick(reaction);
}
},
[handleItemClick]
);
if (fetchState === "loading" || fetchState === "error" || fetchState === "empty") return null;
const rootClass = ["cometchat-reaction-list__list", className ?? ""].filter(Boolean).join(" ");
return /* @__PURE__ */ jsxs(
"div",
{
className: rootClass,
role: "list",
"aria-label": selectedEmoji ? `Users who reacted with ${selectedEmoji}` : "All users who reacted",
children: [
filteredReactions.map((reaction) => {
const reactedBy = reaction.getReactedBy();
const uid = reactedBy.getUid();
const name = reactedBy.getName();
const avatar = reactedBy.getAvatar();
const emoji = reaction.getReaction();
const isMine = isCurrentUser(reaction);
const displayName = isMine ? youText : name;
const itemClass = [
"cometchat-reaction-list__list-item",
isMine ? "cometchat-reaction-list__list-item--current-user" : "cometchat-reaction-list__list-item--readonly"
].filter(Boolean).join(" ");
const ariaLabel = isMine ? `${displayName} reacted with ${emoji}. ${clickToRemoveText}` : `${name} reacted with ${emoji}`;
return /* @__PURE__ */ jsxs(
"div",
{
className: itemClass,
role: "listitem",
tabIndex: isMine ? 0 : -1,
"aria-label": ariaLabel,
onClick: isMine ? () => {
handleItemClick(reaction);
} : void 0,
onKeyDown: isMine ? (e) => {
handleKeyDown(e, reaction);
} : void 0,
children: [
/* @__PURE__ */ jsx("div", { className: "cometchat-reaction-list__item-avatar", children: /* @__PURE__ */ jsxs(CometChatAvatar.Root, { name, image: avatar, children: [
/* @__PURE__ */ jsx(CometChatAvatar.Image, {}),
/* @__PURE__ */ jsx(CometChatAvatar.Initials, {})
] }) }),
/* @__PURE__ */ jsxs("div", { className: "cometchat-reaction-list__item-info", children: [
/* @__PURE__ */ jsx("span", { className: "cometchat-reaction-list__item-name", children: displayName }),
isMine && /* @__PURE__ */ jsx("span", { className: "cometchat-reaction-list__item-hint", children: clickToRemoveText })
] }),
/* @__PURE__ */ jsx("span", { className: "cometchat-reaction-list__item-emoji", "aria-hidden": "true", children: emoji })
]
},
`${uid}-${emoji}`
);
}),
isFetching && filteredReactions.length > 0 && /* @__PURE__ */ jsx("div", { className: "cometchat-reaction-list__loading-more", "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { className: "cometchat-reaction-list__spinner" }) }),
hasMore && /* @__PURE__ */ jsx("div", { ref: sentinelRef, "aria-hidden": "true", style: { height: 1 } })
]
}
);
};
var SHIMMER_ITEM_COUNT = 4;
var CometChatReactionListLoadingState = ({ className }) => {
const { getLocalizedString } = useLocale();
const { fetchState } = useCometChatReactionListContext();
if (fetchState !== "loading") return null;
const rootClass = ["cometchat-reaction-list__shimmer", className ?? ""].filter(Boolean).join(" ");
return /* @__PURE__ */ jsx(
"div",
{
className: rootClass,
"aria-busy": "true",
"aria-label": getLocalizedString("accessibility_loading_reactions"),
children: Array.from({ length: SHIMMER_ITEM_COUNT }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "cometchat-reaction-list__shimmer-item", children: [
/* @__PURE__ */ jsx("div", { className: "cometchat-reaction-list__shimmer-item-icon" }),
/* @__PURE__ */ jsx("div", { className: "cometchat-reaction-list__shimmer-item-content" }),
/* @__PURE__ */ jsx("div", { className: "cometchat-reaction-list__shimmer-item-tailview" })
] }, i))
}
);
};
var CometChatReactionListErrorState = ({
className
}) => {
const { fetchState, retry } = useCometChatReactionListContext();
const { getLocalizedString } = useLocale();
if (fetchState !== "error") return null;
const errorText = getLocalizedString("reaction_list_error") || "Something went wrong";
const retryText = getLocalizedString("reaction_list_retry") || "Retry";
const rootClass = ["cometchat-reaction-list__error", className ?? ""].filter(Boolean).join(" ");
return /* @__PURE__ */ jsxs("div", { className: rootClass, role: "alert", children: [
/* @__PURE__ */ jsx("p", { className: "cometchat-reaction-list__error-text", children: errorText }),
/* @__PURE__ */ jsx("button", { type: "button", className: "cometchat-reaction-list__retry-button", onClick: retry, children: retryText })
] });
};
var CometChatReactionListEmptyState = ({
className
}) => {
const { fetchState } = useCometChatReactionListContext();
const { getLocalizedString } = useLocale();
if (fetchState !== "empty") return null;
const emptyText = getLocalizedString("reaction_list_empty") || "No reactions yet";
const rootClass = ["cometchat-reaction-list__empty", className ?? ""].filter(Boolean).join(" ");
return /* @__PURE__ */ jsx("div", { className: rootClass, role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsx("p", { className: "cometchat-reaction-list__empty-text", children: emptyText }) });
};
var DefaultLayout = () => {
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(CometChatReactionListTabs, {}),
/* @__PURE__ */ jsx(CometChatReactionListLoadingState, {}),
/* @__PURE__ */ jsx(CometChatReactionListErrorState, {}),
/* @__PURE__ */ jsx(CometChatReactionListEmptyState, {}),
/* @__PURE__ */ jsx(CometChatReactionListItems, {})
] });
};
var CometChatReactionListRoot = ({
message,
reactionsRequestBuilder,
onItemClick,
onEmpty,
onError,
children,
className
}) => {
const hookOptions = { message };
if (reactionsRequestBuilder !== void 0)
hookOptions.reactionsRequestBuilder = reactionsRequestBuilder;
if (onItemClick !== void 0) hookOptions.onItemClick = onItemClick;
if (onEmpty !== void 0) hookOptions.onEmpty = onEmpty;
if (onError !== void 0) hookOptions.onError = onError;
const { getLocalizedString } = useLocale();
const hookReturn = useCometChatReactionList(hookOptions);
const contextValue = useMemo(
() => ({
...hookReturn,
message
}),
[hookReturn, message]
);
const hasChildren = React.Children.count(children) > 0;
const rootClass = ["cometchat-reaction-list", className ?? ""].filter(Boolean).join(" ");
return /* @__PURE__ */ jsx(CometChatReactionListContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
"div",
{
className: rootClass,
role: "dialog",
"aria-label": getLocalizedString("accessibility_reaction_list"),
"aria-modal": "false",
children: hasChildren ? children : /* @__PURE__ */ jsx(DefaultLayout, {})
}
) });
};
var CometChatReactionListComponent = (props) => {
return /* @__PURE__ */ jsx(CometChatReactionListRoot, { ...props });
};
var CometChatReactionList = Object.assign(CometChatReactionListComponent, {
Root: CometChatReactionListRoot,
Tabs: CometChatReactionListTabs,
Items: CometChatReactionListItems,
LoadingState: CometChatReactionListLoadingState,
ErrorState: CometChatReactionListErrorState,
EmptyState: CometChatReactionListEmptyState
});
var CHIP_WIDTH = 48;
var REACTIONS_PADDING = 8;
var ReactionListPopoverContent = () => {
const { message, reactionsRequestBuilder, onReactionClick } = useCometChatReactionsContext();
const { close } = useCometChatPopoverContext();
const handleItemClick = useCallback(
(reaction) => {
onReactionClick(reaction.getReaction());
},
[onReactionClick]
);
const handleEmpty = useCallback(() => {
close();
}, [close]);
return /* @__PURE__ */ jsx(
CometChatReactionList.Root,
{
message,
reactionsRequestBuilder,
onItemClick: handleItemClick,
onEmpty: handleEmpty
}
);
};
var CometChatReactionsBar = ({
maxVisible: maxVisibleProp,
className
}) => {
const { getLocalizedString } = useLocale();
const { reactions, alignment } = useCometChatReactionsContext();
const barRef = useRef(null);
const [computedMaxVisible, setComputedMaxVisible] = useState(
maxVisibleProp ?? reactions.length
// Show all initially; ResizeObserver constrains after layout
);
const maxVisible = maxVisibleProp ?? computedMaxVisible;
useEffect(() => {
if (maxVisibleProp != null || !barRef.current) return;
const footerView = barRef.current.closest('[class*="body-footer-view"]');
const body = footerView?.parentElement;
const contentView = body?.querySelector('[class*="body-content-view"]');
if (!contentView) {
setComputedMaxVisible(100);
return;
}
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const width = entry.contentRect.width - REACTIONS_PADDING;
const calculated = Math.max(1, Math.floor(width / CHIP_WIDTH));
setComputedMaxVisible(calculated);
}
});
observer.observe(contentView);
return () => {
observer.disconnect();
};
}, [maxVisibleProp]);
const totalReactions = reactions.length;
const showOverflow = totalReactions > maxVisible && maxVisible > 2;
const visibleCount = showOverflow ? maxVisible - 1 : Math.min(totalReactions, maxVisible);
const visibleReactions = reactions.slice(0, visibleCount);
const overflowCount = totalReactions - visibleCount;
const handleKeyDown = useCallback((e) => {
const target = e.target;
if (!barRef.current) return;
const buttons = barRef.current.querySelectorAll("button");
if (!buttons.length) return;
const currentIndex = Array.from(buttons).indexOf(target);
if (currentIndex === -1) return;
let nextIndex = currentIndex;
if (e.key === "ArrowRight") {
nextIndex = (currentIndex + 1) % buttons.length;
e.preventDefault();
} else if (e.key === "ArrowLeft") {
nextIndex = (currentIndex - 1 + buttons.length) % buttons.length;
e.preventDefault();
} else {
return;
}
const nextButton = buttons[nextIndex];
if (nextButton) {
nextButton.focus();
}
}, []);
const barClass = ["cometchat-reactions__bar", className ?? ""].filter(Boolean).join(" ");
const listPlacement = alignment === "left" ? "right" : "left";
if (reactions.length === 0) return null;
return /* @__PURE__ */ jsxs(
"div",
{
ref: barRef,
className: barClass,
role: "group",
"aria-label": getLocalizedString("accessibility_reactions"),
"aria-live": "polite",
onKeyDown: handleKeyDown,
children: [
visibleReactions.map((reaction) => /* @__PURE__ */ jsx("div", { className: "cometchat-reactions__info-wrapper", children: /* @__PURE__ */ jsx(
CometChatPopover,
{
showOnHover: true,
debounceOnHover: 500,
placement: "top",
trigger: /* @__PURE__ */ jsx(CometChatReactionsChip, { reaction }),
content: /* @__PURE__ */ jsx(CometChatReactionsInfo, { emoji: reaction.getReaction() })
}
) }, reaction.getReaction())),
overflowCount > 0 && /* @__PURE__ */ jsx(
CometChatPopover,
{
placement: listPlacement,
closeOnOutsideClick: true,
trigger: /* @__PURE__ */ jsx(CometChatReactionsOverflow, { count: overflowCount }),
content: /* @__PURE__ */ jsx(ReactionListPopoverContent, {})
}
)
]
}
);
};
var CometChatReactionsRoot = ({
message,
alignment = "left",
reactionsRequestBuilder,
onReactionClick,
hoverDebounceTime = 500,
onError,
children,
className
}) => {
const reactions = useMemo(() => message.getReactions(), [message]);
const maxVisible = reactions.length;
const visibleReactions = reactions;
const overflowCount = 0;
const handleReactionClick = useCallback(
(emoji) => {
onReactionClick?.(emoji, message);
},
[onReactionClick, message]
);
const contextValue = useMemo(
() => ({
message,
reactions,
alignment,
maxVisible,
visibleReactions,
overflowCount,
onReactionClick: handleReactionClick,
reactionsRequestBuilder,
hoverDebounceTime,
onError
}),
[
message,
reactions,
alignment,
maxVisible,
visibleReactions,
overflowCount,
handleReactionClick,
reactionsRequestBuilder,
hoverDebounceTime,
onError
]
);
const rootClass = ["cometchat-reactions", className ?? ""].filter(Boolean).join(" ");
if (reactions.length === 0) return null;
return /* @__PURE__ */ jsx(CometChatReactionsContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { className: rootClass, children: children ?? /* @__PURE__ */ jsx(CometChatReactionsBar, {}) }) });
};
export { CometChatReactionList, CometChatReactionsBar, CometChatReactionsChip, CometChatReactionsInfo, CometChatReactionsOverflow, CometChatReactionsRoot, useCometChatReactionList, useCometChatReactionListContext, useCometChatReactionsContext };