UNPKG

stream-chat-react

Version:

React components to create chat conversations or livestream style chat

217 lines (216 loc) 7.96 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const require_useNotificationApi = require("./useNotificationApi.9ffe5761.js"); let react = require("react"); react = require_useNotificationApi.__toESM(react); let react_jsx_runtime = require("react/jsx-runtime"); let stream_chat = require("stream-chat"); let lodash_mergewith = require("lodash.mergewith"); lodash_mergewith = require_useNotificationApi.__toESM(lodash_mergewith); let _emoji_mart_react = require("@emoji-mart/react"); _emoji_mart_react = require_useNotificationApi.__toESM(_emoji_mart_react); //#region src/plugins/Emojis/EmojiPicker.tsx var Picker = _emoji_mart_react.default.default ?? _emoji_mart_react.default; var isShadowRoot = (node) => !!node.host; var defaultButtonClassName = "str-chat__emoji-picker-button"; var classNames = { pickerContainerClassName: "str-chat__message-textarea-emoji-picker-container", wrapperClassName: "str-chat__message-textarea-emoji-picker" }; var EmojiPicker = (props) => { const { t } = require_useNotificationApi.useTranslationContext("EmojiPicker"); const { textareaRef } = require_useNotificationApi.useMessageComposerContext("EmojiPicker"); const { textComposer } = require_useNotificationApi.useMessageComposerController(); const isCooldownActive = require_useNotificationApi.useIsCooldownActive(); const [displayPicker, setDisplayPicker] = (0, react.useState)(false); const [referenceElement, setReferenceElement] = (0, react.useState)(null); const [popperElement, setPopperElement] = (0, react.useState)(null); const { refs, strategy, x, y } = require_useNotificationApi.usePopoverPosition({ offset: 8, placement: props.placement ?? "top-end" }); (0, react.useEffect)(() => { refs.setReference(referenceElement); }, [referenceElement, refs]); (0, react.useEffect)(() => { refs.setFloating(popperElement); }, [popperElement, refs]); const { pickerContainerClassName, wrapperClassName } = classNames; const { ButtonIconComponent = require_useNotificationApi.IconEmoji } = props; const pickerStyle = props.pickerProps?.style; (0, react.useEffect)(() => { if (!popperElement || !referenceElement) return; const handlePointerDown = (e) => { const target = e.target; const rootNode = target.getRootNode(); if (popperElement.contains(isShadowRoot(rootNode) ? rootNode.host : target) || referenceElement.contains(target)) return; setDisplayPicker(false); }; window.addEventListener("pointerdown", handlePointerDown); return () => window.removeEventListener("pointerdown", handlePointerDown); }, [referenceElement, popperElement]); return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { className: props.wrapperClassName ?? wrapperClassName, children: [displayPicker && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: props.pickerContainerClassName ?? pickerContainerClassName, ref: setPopperElement, style: { left: x ?? 0, position: strategy, top: y ?? 0 }, children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Picker, { data: async () => (await import("@emoji-mart/data")).default, onEmojiSelect: (e) => { const textarea = textareaRef.current; if (!textarea) return; textComposer.insertText({ text: e.native }); textarea.focus(); if (props.closeOnEmojiSelect) setDisplayPicker(false); }, ...props.pickerProps, style: { ...pickerStyle, "--shadow": "none" } }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_useNotificationApi.Button, { appearance: "ghost", "aria-expanded": displayPicker, "aria-label": t("aria/Emoji picker"), circular: true, className: props.buttonClassName ?? defaultButtonClassName, disabled: isCooldownActive, onClick: () => setDisplayPicker((cv) => !cv), ref: setReferenceElement, size: "sm", type: "button", variant: "secondary", children: ButtonIconComponent && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ButtonIconComponent, {}) })] }); }; //#endregion //#region src/plugins/Emojis/middleware/textComposerEmojiMiddleware.ts var EmojiSearchSource = class extends stream_chat.BaseSearchSource { constructor(emojiSearchIndex, options) { super(options); this.type = "emoji"; this.emojiSearchIndex = emojiSearchIndex; } async query(searchQuery) { if (searchQuery.length === 0) return { items: [], next: null }; return { items: (await this.emojiSearchIndex.search(searchQuery) ?? []).filter(Boolean).slice(0, 7).map(({ emoticons = [], id, name, native, skins = [] }) => { const [firstSkin] = skins; return { emoticons, id, name, native: native ?? firstSkin.native }; }), next: null }; } filterQueryResults(items) { return items.map((item) => ({ ...item, ...(0, stream_chat.getTokenizedSuggestionDisplayName)({ displayName: item.id, searchToken: this.searchQuery }) })); } }; var DEFAULT_OPTIONS = { minChars: 1, trigger: ":" }; /** * TextComposer middleware for mentions * Usage: * * const textComposer = new TextComposer(options); * * textComposer.use(new createTextComposerEmojiMiddleware(emojiSearchIndex, { * minChars: 2 * })); * * @param emojiSearchIndex * @param {{ * minChars: number; * trigger: string; * }} options * @returns */ var createTextComposerEmojiMiddleware = (emojiSearchIndex, options) => { const finalOptions = (0, lodash_mergewith.default)(DEFAULT_OPTIONS, options ?? {}); const emojiSearchSource = new EmojiSearchSource(emojiSearchIndex); emojiSearchSource.activate(); return { id: "stream-io/emoji-middleware", handlers: { onChange: async ({ complete, forward, next, state }) => { if (!state.selection) return forward(); const triggerWithToken = (0, stream_chat.getTriggerCharWithToken)({ acceptTrailingSpaces: false, text: state.text.slice(0, state.selection.end), trigger: finalOptions.trigger }); if (!triggerWithToken || triggerWithToken.length < finalOptions.minChars) { const hasSuggestionsForTrigger = state.suggestions?.trigger === finalOptions.trigger; const newState = { ...state }; if (hasSuggestionsForTrigger && newState.suggestions) delete newState.suggestions; return next(newState); } if (triggerWithToken && triggerWithToken === finalOptions.trigger) emojiSearchSource.resetStateAndActivate(); const textWithReplacedWord = await (0, stream_chat.replaceWordWithEntity)({ caretPosition: state.selection.end, getEntityString: async (word) => { const { items } = await emojiSearchSource.query(word); const emoji = items.filter(Boolean).slice(0, 10).find(({ emoticons }) => !!emoticons?.includes(word)); if (!emoji) return null; const [firstSkin] = emoji.skins ?? []; return emoji.native ?? firstSkin.native; }, text: state.text }); if (textWithReplacedWord !== state.text) return complete({ ...state, suggestions: void 0, text: textWithReplacedWord }); return complete({ ...state, suggestions: { query: triggerWithToken.slice(1), searchSource: emojiSearchSource, trigger: finalOptions.trigger } }); }, onSuggestionItemSelect: ({ complete, forward, state }) => { const { selectedSuggestion } = state.change ?? {}; if (!selectedSuggestion || state.suggestions?.trigger !== finalOptions.trigger) return forward(); emojiSearchSource.resetStateAndActivate(); return complete({ ...state, ...(0, stream_chat.insertItemWithTrigger)({ insertText: `${"native" in selectedSuggestion ? selectedSuggestion.native : ""} `, selection: state.selection, text: state.text, trigger: finalOptions.trigger }), suggestions: void 0 }); } } }; }; //#endregion exports.EmojiPicker = EmojiPicker; exports.createTextComposerEmojiMiddleware = createTextComposerEmojiMiddleware; //# sourceMappingURL=emojis.js.map