@douyinfe/semi-ui
Version:
A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.
422 lines (421 loc) • 15.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {};
exports.default = void 0;
var React = _interopRequireWildcard(require("react"));
var _baseComponent = _interopRequireDefault(require("../_base/baseComponent"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _classnames = _interopRequireDefault(require("classnames"));
require("@douyinfe/semi-foundation/lib/cjs/aiChatDialogue/aiChatDialogue.css");
var _reasoning = require("./widgets/contentItem/reasoning");
var _dialogueStep = require("./widgets/contentItem/dialogueStep");
var _annotation = require("./widgets/contentItem/annotation");
var _code = _interopRequireDefault(require("./widgets/contentItem/code"));
var _Dialogue = _interopRequireDefault(require("./Dialogue"));
var _foundation = _interopRequireWildcard(require("@douyinfe/semi-foundation/lib/cjs/aiChatDialogue/foundation"));
Object.keys(_foundation).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _foundation[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _foundation[key];
}
});
});
var _utils = require("../_utils");
var _constants = require("@douyinfe/semi-foundation/lib/cjs/aiChatDialogue/constants");
var _dialogueHint = _interopRequireDefault(require("./widgets/dialogueHint"));
var _index = require("../index");
var _semiIcons = require("@douyinfe/semi-icons");
var _interface = require("./interface");
Object.keys(_interface).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _interface[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _interface[key];
}
});
});
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var __rest = void 0 && (void 0).__rest || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
}
return t;
};
const {
DIALOGUE_ALIGN,
MODE
} = _constants.strings;
const {
PREFIX
} = _constants.cssClasses;
class AIChatDialogue extends _baseComponent.default {
constructor(props) {
super(props);
this.selectAll = () => {
this.foundation.handleSelectAll();
};
this.deselectAll = () => {
this.foundation.handleDeselectAll();
};
this.scrollToBottom = animation => {
if (animation) {
this.foundation.scrollToBottomWithAnimation();
} else {
this.foundation.scrollToBottomImmediately();
}
};
this.scrollToTop = animation => {
if (animation) {
this.foundation.scrollToTopWithAnimation();
} else {
this.foundation.scrollToTopImmediately();
}
};
this.containerScroll = e => {
this.scrollTargetRef.current = e.target;
if (e.target !== e.currentTarget) {
return;
}
this.foundation.containerScroll(e);
};
this.foundation = new _foundation.default(this.adapter);
this.containerRef = /*#__PURE__*/React.createRef();
this.scrollTargetRef = /*#__PURE__*/React.createRef();
this.wheelEventHandler = null;
this.state = {
cacheHints: [],
selectedIds: new Set(),
chats: [],
backBottomVisible: false,
wheelScroll: false
};
this.onSelectOrRemove = this.onSelectOrRemove.bind(this);
}
get adapter() {
return Object.assign(Object.assign({}, super.adapter), {
getContainerRef: () => {
var _a;
return (_a = this.containerRef) === null || _a === void 0 ? void 0 : _a.current;
},
setWheelScroll: flag => {
this.setState({
wheelScroll: flag
});
},
updateSelected: selectedIds => {
this.setState({
selectedIds
});
},
notifySelect: selectedIds => {
const {
onSelect
} = this.props;
onSelect && onSelect(selectedIds);
},
notifyChatsChange: chats => {
const {
onChatsChange
} = this.props;
onChatsChange && onChatsChange(chats);
},
notifyCopyMessage: message => {
const {
onMessageCopy
} = this.props;
onMessageCopy && onMessageCopy(message);
},
notifyLikeMessage: message => {
const {
onMessageGoodFeedback
} = this.props;
onMessageGoodFeedback && onMessageGoodFeedback(message);
},
notifyDislikeMessage: message => {
const {
onMessageBadFeedback
} = this.props;
onMessageBadFeedback && onMessageBadFeedback(message);
},
notifyEditMessage: message => {
const {
onMessageEdit
} = this.props;
onMessageEdit && onMessageEdit(message);
},
notifyHintClick: hint => {
const {
onHintClick
} = this.props;
onHintClick && onHintClick(hint);
},
setBackBottomVisible: visible => {
this.setState(state => {
if (state.backBottomVisible !== visible) {
return {
backBottomVisible: visible
};
}
return null;
});
},
registerWheelEvent: () => {
this.adapter.unRegisterWheelEvent();
const containerElement = this.containerRef.current;
if (!containerElement) {
return;
}
this.wheelEventHandler = e => {
var _a;
/**
* Why use this.scrollTargetRef.current and wheel's currentTarget target comparison?
* Both scroll and wheel events are on the container
* his.scrollTargetRef.current is the object where scrolling actually occurs
* wheel's currentTarget is the container,
* Only when the wheel event occurs and there is scroll, the following logic(show scroll bar) needs to be executed
*/
if (((_a = this.scrollTargetRef) === null || _a === void 0 ? void 0 : _a.current) !== e.currentTarget) {
return;
}
this.adapter.setWheelScroll(true);
this.adapter.unRegisterWheelEvent();
};
containerElement.addEventListener('wheel', this.wheelEventHandler);
},
unRegisterWheelEvent: () => {
if (this.wheelEventHandler) {
const containerElement = this.containerRef.current;
if (!containerElement) {
return;
} else {
containerElement.removeEventListener('wheel', this.wheelEventHandler);
}
this.wheelEventHandler = null;
}
}
});
}
static getDerivedStateFromProps(nextProps, prevState) {
const {
chats,
hints
} = nextProps;
const newState = {};
if (chats !== prevState.chats) {
newState.chats = chats !== null && chats !== void 0 ? chats : [];
}
if (hints !== prevState.cacheHints) {
newState.cacheHints = hints;
}
if (Object.keys(newState).length) {
return newState;
}
return null;
}
componentDidMount() {
this.foundation.init();
}
componentDidUpdate(prevProps, prevState, snapshot) {
const {
chats: newChats,
hints: newHints
} = this.props;
const {
chats: oldChats,
cacheHints
} = prevState;
const {
wheelScroll
} = this.state;
let shouldScroll = false;
if (newChats.length > oldChats.length) {
this.adapter.setWheelScroll(false);
this.adapter.registerWheelEvent();
this.foundation.scrollToBottomImmediately();
}
if (newChats !== oldChats) {
this.foundation.handleChatsChange(newChats);
if (Array.isArray(newChats) && Array.isArray(oldChats)) {
const newLastChat = newChats[newChats.length - 1];
const oldLastChat = oldChats[oldChats.length - 1];
if (newChats.length > oldChats.length) {
if (oldChats.length === 0 || newLastChat.id !== oldLastChat.id) {
shouldScroll = true;
}
} else if (newChats.length === oldChats.length && newChats.length && (newLastChat.status !== 'completed' || newLastChat.status !== oldLastChat.status)) {
shouldScroll = true;
}
}
}
if (newHints !== cacheHints) {
if (newHints.length > cacheHints.length) {
shouldScroll = true;
}
}
if (!wheelScroll && shouldScroll) {
this.foundation.scrollToBottomImmediately();
}
}
componentWillUnmount() {
this.foundation.destroy();
}
onSelectOrRemove(isChecked, item) {
this.foundation.handleSelectOrRemove(isChecked, item);
}
render() {
const _a = this.props,
{
roleConfig,
onMessageBadFeedback,
onMessageGoodFeedback,
onMessageReset,
onMessageEdit,
onMessageDelete,
onHintClick,
selecting,
hintCls,
hintStyle,
hints,
renderHintBox,
style,
className
} = _a,
restProps = __rest(_a, ["roleConfig", "onMessageBadFeedback", "onMessageGoodFeedback", "onMessageReset", "onMessageEdit", "onMessageDelete", "onHintClick", "selecting", "hintCls", "hintStyle", "hints", "renderHintBox", "style", "className"]);
const {
selectedIds,
chats,
backBottomVisible,
wheelScroll
} = this.state;
return /*#__PURE__*/React.createElement("div", {
className: (0, _classnames.default)(`${PREFIX}`, className),
style: style
}, /*#__PURE__*/React.createElement("div", {
className: (0, _classnames.default)(`${PREFIX}-list`, {
[`${PREFIX}-list-scroll-hidden`]: !wheelScroll
}),
onScroll: this.containerScroll,
ref: this.containerRef
}, chats.map((chat, index) => {
var _a;
const isLastChat = index === chats.length - 1;
const continueSend = index > 0 && (chat === null || chat === void 0 ? void 0 : chat.role) === ((_a = chats[index - 1]) === null || _a === void 0 ? void 0 : _a.role);
return /*#__PURE__*/React.createElement(_Dialogue.default, Object.assign({
key: chat.id,
message: chat,
role: roleConfig[chat.role],
onSelectChange: this.onSelectOrRemove,
isSelected: selectedIds.has(chat.id),
roleConfig: roleConfig,
onMessageBadFeedback: this.foundation.dislikeMessage,
onMessageGoodFeedback: this.foundation.likeMessage,
onMessageReset: this.foundation.resetMessage,
onMessageEdit: this.foundation.editMessage,
onMessageDelete: this.foundation.deleteMessage,
isLastChat: isLastChat,
// todo: 不太确定用户的需求场景,暂时设置成 false,如果用户有相关需求,转为一个对外提供的 api
// todo: Not sure about the user's demand scenario, temporarily set it to false.
// If the user has relevant needs, turn it into an external API
continueSend: false,
selecting: selecting
}, restProps));
}), !!(hints === null || hints === void 0 ? void 0 : hints.length) && /*#__PURE__*/React.createElement(_dialogueHint.default, {
className: hintCls,
style: hintStyle,
hints: hints,
onHintClick: this.foundation.onHintClick,
renderHintBox: renderHintBox,
selecting: selecting
})), backBottomVisible && (/*#__PURE__*/React.createElement("span", {
className: `${PREFIX}-backBottom`
}, /*#__PURE__*/React.createElement(_index.Button, {
className: `${PREFIX}-backBottom-button`,
icon: /*#__PURE__*/React.createElement(_semiIcons.IconChevronDown, {
size: "extra-large"
}),
type: "tertiary",
onClick: this.foundation.scrollToBottomWithAnimation
}))));
}
}
AIChatDialogue.__SemiComponentName__ = "AIChatDialogue";
AIChatDialogue.Reasoning = _reasoning.ReasoningWidget;
AIChatDialogue.Step = _dialogueStep.DialogueStepWidget;
AIChatDialogue.Annotation = _annotation.AnnotationWidget;
AIChatDialogue.defaultComponents = {
code: _code.default
};
AIChatDialogue.propTypes = {
align: _propTypes.default.oneOf(['leftRight', 'leftAlign']),
chats: _propTypes.default.arrayOf(_propTypes.default.shape({
id: _propTypes.default.string.isRequired,
content: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.array]),
output_text: _propTypes.default.string,
role: _propTypes.default.string.isRequired,
name: _propTypes.default.string,
createdAt: _propTypes.default.number,
updatedAt: _propTypes.default.number,
model: _propTypes.default.string,
status: _propTypes.default.string
})),
className: _propTypes.default.string,
disabledFileItemClick: _propTypes.default.bool,
hints: _propTypes.default.arrayOf(_propTypes.default.string),
hintCls: _propTypes.default.string,
hintStyle: _propTypes.default.object,
selecting: _propTypes.default.bool,
markdownRenderProps: _propTypes.default.object,
messageEditRender: _propTypes.default.func,
mode: _propTypes.default.oneOf(['bubble', 'noBubble', 'userBubble']),
roleConfig: _propTypes.default.object,
style: _propTypes.default.object,
dialogueRenderConfig: _propTypes.default.shape({
renderDialogueAction: _propTypes.default.func,
renderDialogueAvatar: _propTypes.default.func,
renderDialogueContent: _propTypes.default.func,
renderDialogueTitle: _propTypes.default.func,
renderFullDialogue: _propTypes.default.func
}),
renderHintBox: _propTypes.default.func,
renderDialogueContentItem: _propTypes.default.object,
onAnnotationClick: _propTypes.default.func,
onChatsChange: _propTypes.default.func,
onFileClick: _propTypes.default.func,
onImageClick: _propTypes.default.func,
onHintClick: _propTypes.default.func,
onMessageBadFeedback: _propTypes.default.func,
onMessageCopy: _propTypes.default.func,
onMessageDelete: _propTypes.default.func,
onMessageEdit: _propTypes.default.func,
onMessageGoodFeedback: _propTypes.default.func,
onMessageReset: _propTypes.default.func,
onMessageShare: _propTypes.default.func,
onReferenceClick: _propTypes.default.func,
onSelect: _propTypes.default.func,
showReset: _propTypes.default.bool,
showReference: _propTypes.default.bool,
escapeHtml: _propTypes.default.bool
};
AIChatDialogue.defaultProps = (0, _utils.getDefaultPropsFromGlobalConfig)(AIChatDialogue.__SemiComponentName__, {
align: DIALOGUE_ALIGN.LEFT_RIGHT,
mode: MODE.BUBBLE,
selecting: false,
disabledFileItemClick: false,
showReset: true,
showReference: false,
escapeHtml: true
});
var _default = exports.default = AIChatDialogue;