UNPKG

@ant-design/x

Version:

Craft AI-driven interfaces effortlessly

196 lines (190 loc) 8.17 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _pickAttrs = _interopRequireDefault(require("@rc-component/util/lib/pickAttrs")); var _clsx = require("clsx"); var _react = _interopRequireDefault(require("react")); var _useXComponentConfig = _interopRequireDefault(require("../_util/hooks/use-x-component-config")); var _xProvider = require("../x-provider"); var _context = require("./context"); var _EditableContent = require("./EditableContent"); var _loading = _interopRequireDefault(require("./loading")); var _style = _interopRequireDefault(require("./style")); var _TypingContent = require("./TypingContent"); const Bubble = ({ prefixCls: customizePrefixCls, rootClassName, style, className, styles = {}, classNames = {}, placement = 'start', content, contentRender, editable = false, typing, streaming = false, variant = 'filled', shape = 'default', header, footer, avatar, extra, footerPlacement, loading, loadingRender, onTyping, onTypingComplete, onEditConfirm, onEditCancel, ...restProps }, ref) => { // ======================== Ref ========================== const rootDiv = _react.default.useRef(null); _react.default.useImperativeHandle(ref, () => ({ nativeElement: rootDiv.current })); // ===================== Component Config ========================= const contextConfig = (0, _useXComponentConfig.default)('bubble'); // ============================ Prefix ============================ const { direction, getPrefixCls } = (0, _xProvider.useXProviderContext)(); const prefixCls = getPrefixCls('bubble', customizePrefixCls); // ============================= Bubble context ============================== const context = _react.default.useContext(_context.BubbleContext); // ============================ Styles ============================ const [hashId, cssVarCls] = (0, _style.default)(prefixCls); const rootMergedStyle = { ...contextConfig.style, ...contextConfig.styles.root, ...styles.root, ...style }; const rootMergedCls = (0, _clsx.clsx)(prefixCls, contextConfig.className, contextConfig.classNames.root, classNames.root, rootClassName, className, hashId, cssVarCls, `${prefixCls}-${placement}`, { [`${prefixCls}-${context.status}`]: context.status, [`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-loading`]: loading }); const domProps = (0, _pickAttrs.default)(restProps, { attr: true, aria: true, data: true }); const info = { key: context?.key, status: context?.status, extraInfo: context?.extraInfo }; // ============================= process content ============================== const memoedContent = _react.default.useMemo(() => contentRender ? contentRender(content, info) : content, [content, contentRender, info.key, info.status, info.extraInfo]); const mergeTyping = typeof typing === 'function' ? typing(content, info) : typing; const usingInnerAnimation = !!mergeTyping && typeof memoedContent === 'string'; /** * 1、启用内置动画的情况下,由 TypingContent 来负责通知。 * 2、不启用内置动画的情况下,也应当有一个回调来反映 content 的变化。 * 没有动画,则 content 的变化、渲染是全量的,等同于动画是瞬时完成的,合该用 onTypingComplete 来通知变化。 * 3、流式输入 content 的场景下,应当在流式结束时(streaming === false)才执行 onTypingComplete, * 保证一次流式传输归属于一个动画周期。 **/ _react.default.useEffect(() => { if (usingInnerAnimation) return; if (streaming) return; content && onTypingComplete?.(content); }, [memoedContent, usingInnerAnimation, streaming]); // ============================= render ============================== const _footerPlacement = footerPlacement || (placement === 'start' ? 'outer-start' : 'outer-end'); const isEditing = typeof editable === 'boolean' ? editable : editable.editing; const renderContent = () => { if (loading) return loadingRender ? loadingRender() : /*#__PURE__*/_react.default.createElement(_loading.default, { prefixCls: prefixCls }); const _content = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, usingInnerAnimation ? /*#__PURE__*/_react.default.createElement(_TypingContent.TypingContent, { prefixCls: prefixCls, streaming: streaming, typing: mergeTyping, content: memoedContent, onTyping: onTyping, onTypingComplete: onTypingComplete }) : memoedContent); const isFooterIn = _footerPlacement.includes('inner'); return /*#__PURE__*/_react.default.createElement("div", { className: getSlotClassName('body'), style: getSlotStyle('body') }, renderHeader(), /*#__PURE__*/_react.default.createElement("div", { style: { ...contextConfig.styles.content, ...styles.content }, className: (0, _clsx.clsx)(`${prefixCls}-content`, `${prefixCls}-content-${variant}`, contextConfig.classNames.content, classNames.content, { [`${prefixCls}-content-${context?.status}`]: context?.status, [`${prefixCls}-content-${shape}`]: variant !== 'borderless', [`${prefixCls}-content-editing`]: isEditing, [`${prefixCls}-content-string`]: typeof memoedContent === 'string' }) }, isEditing ? /*#__PURE__*/_react.default.createElement(_EditableContent.EditableContent, { prefixCls: prefixCls, content: content, okText: editable?.okText, cancelText: editable?.cancelText, onEditConfirm: onEditConfirm, onEditCancel: onEditCancel }) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, isFooterIn ? /*#__PURE__*/_react.default.createElement("div", { className: (0, _clsx.clsx)(`${prefixCls}-content-with-footer`) }, _content) : _content, isFooterIn && renderFooter())), !isEditing && !isFooterIn && renderFooter()); }; const getSlotClassName = slotType => (0, _clsx.clsx)(`${prefixCls}-${slotType}`, contextConfig.classNames[slotType], classNames[slotType]); const getSlotStyle = slotType => ({ ...contextConfig.styles[slotType], ...styles[slotType] }); const renderSlot = slot => typeof slot === 'function' ? slot(content, info) : slot; const renderAvatar = () => { if (!avatar) return null; return /*#__PURE__*/_react.default.createElement("div", { className: getSlotClassName('avatar'), style: getSlotStyle('avatar') }, renderSlot(avatar)); }; const renderExtra = () => { if (!extra) return null; return /*#__PURE__*/_react.default.createElement("div", { className: getSlotClassName('extra'), style: getSlotStyle('extra') }, renderSlot(extra)); }; const renderHeader = () => { if (!header) return null; return /*#__PURE__*/_react.default.createElement("div", { className: getSlotClassName('header'), style: getSlotStyle('header') }, renderSlot(header)); }; const renderFooter = () => { if (!footer) return null; const cls = (0, _clsx.clsx)(getSlotClassName('footer'), { [`${prefixCls}-footer-start`]: _footerPlacement.includes('start'), [`${prefixCls}-footer-end`]: _footerPlacement.includes('end') }); return /*#__PURE__*/_react.default.createElement("div", { className: cls, style: getSlotStyle('footer') }, renderSlot(footer)); }; return /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({ className: rootMergedCls, style: rootMergedStyle }, restProps, domProps, { ref: rootDiv }), renderAvatar(), renderContent(), !isEditing && !loading && renderExtra()); }; const ForwardBubble = /*#__PURE__*/_react.default.forwardRef(Bubble); if (process.env.NODE_ENV !== 'production') { ForwardBubble.displayName = 'Bubble'; } var _default = exports.default = ForwardBubble;