@ant-design/x
Version:
Craft AI-driven interfaces effortlessly
297 lines (279 loc) • 10.8 kB
JavaScript
;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "SenderContext", {
enumerable: true,
get: function () {
return _context.SenderContext;
}
});
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _util = require("@rc-component/util");
var _pickAttrs = _interopRequireDefault(require("@rc-component/util/lib/pickAttrs"));
var _antd = require("antd");
var _clsx = require("clsx");
var _react = _interopRequireWildcard(require("react"));
var _useProxyImperativeHandle = _interopRequireDefault(require("../_util/hooks/use-proxy-imperative-handle"));
var _useXComponentConfig = _interopRequireDefault(require("../_util/hooks/use-x-component-config"));
var _xProvider = require("../x-provider");
var _ActionButton = require("./components/ActionButton");
var _ClearButton = _interopRequireDefault(require("./components/ClearButton"));
var _LoadingButton = _interopRequireDefault(require("./components/LoadingButton"));
var _SendButton = _interopRequireDefault(require("./components/SendButton"));
var _SlotTextArea = _interopRequireDefault(require("./components/SlotTextArea"));
var _SpeechButton = _interopRequireDefault(require("./components/SpeechButton"));
var _TextArea = _interopRequireDefault(require("./components/TextArea"));
var _context = require("./context");
var _useSpeech = _interopRequireDefault(require("./hooks/use-speech"));
var _SenderHeader = require("./SenderHeader");
var _style = _interopRequireDefault(require("./style"));
/** Used for actions render needed components */
const sharedRenderComponents = {
SendButton: _SendButton.default,
ClearButton: _ClearButton.default,
LoadingButton: _LoadingButton.default,
SpeechButton: _SpeechButton.default
};
const ForwardSender = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
const {
prefixCls: customizePrefixCls,
styles = {},
classNames = {},
className,
rootClassName,
style,
defaultValue,
value,
slotConfig,
readOnly,
submitType = 'enter',
onSubmit,
loading,
components,
onCancel,
onChange,
suffix,
onKeyUp,
onKeyDown,
disabled,
allowSpeech,
prefix,
footer,
header,
onPaste,
onPasteFile,
autoSize = {
maxRows: 8
},
placeholder,
onFocus,
onBlur,
skill,
...restProps
} = props;
const domProps = (0, _pickAttrs.default)(restProps, {
attr: true,
aria: true,
data: true
});
const id = _react.default.useId();
const isSlotMode = Array.isArray(slotConfig) || skill?.value;
// ============================= MISC =============================
const {
direction,
getPrefixCls
} = (0, _xProvider.useXProviderContext)();
const prefixCls = getPrefixCls('sender', customizePrefixCls);
// ============================= Refs =============================
const containerRef = _react.default.useRef(null);
const inputRef = _react.default.useRef(null);
(0, _useProxyImperativeHandle.default)(ref, () => {
return {
nativeElement: containerRef.current,
inputElement: inputRef.current?.nativeElement,
focus: options => inputRef.current?.focus(options),
blur: () => inputRef.current?.blur(),
insert: (...args) => inputRef.current?.insert?.(...args),
clear: () => inputRef.current?.clear(),
getValue: () => inputRef.current?.getValue() ?? {
value: '',
slotConfig: [],
skill: undefined
}
};
});
// ======================= Component Config =======================
const contextConfig = (0, _useXComponentConfig.default)('sender');
const inputCls = `${prefixCls}-input`;
// ============================ Styles ============================
const [hashId, cssVarCls] = (0, _style.default)(prefixCls);
const mergedCls = (0, _clsx.clsx)(prefixCls, contextConfig.className, className, rootClassName, contextConfig.classNames.root, classNames.root, hashId, cssVarCls, `${prefixCls}-main`, {
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-disabled`]: disabled
});
const actionBtnCls = `${prefixCls}-actions-btn`;
const actionListCls = `${prefixCls}-actions-list`;
// ============================ Value =============================
const [innerValue, setInnerValue] = (0, _util.useControlledState)(defaultValue || '', value);
const triggerValueChange = (nextValue, event, slotConfig, skill) => {
setInnerValue(nextValue);
onChange?.(nextValue, event, slotConfig ?? [], skill);
};
// ============================ Speech ============================
const [speechPermission, triggerSpeech, speechRecording] = (0, _useSpeech.default)(transcript => {
if (isSlotMode) {
inputRef.current?.insert?.([{
type: 'text',
value: transcript
}]);
} else {
triggerValueChange(`${innerValue} ${transcript}`);
}
}, allowSpeech);
// ============================ Events ============================
const triggerSend = () => {
if (inputRef?.current && onSubmit && !loading && !submitDisabled) {
const inputValue = inputRef.current.getValue();
onSubmit(inputValue.value, inputValue.slotConfig, inputValue.skill);
}
};
const triggerClear = () => {
triggerValueChange('');
if (isSlotMode) {
inputRef.current?.clear?.();
}
};
// ============================ Action ============================
const actionNode = /*#__PURE__*/_react.default.createElement(_antd.Flex, {
className: `${actionListCls}-presets`
}, allowSpeech && /*#__PURE__*/_react.default.createElement(_SpeechButton.default, null), loading ? /*#__PURE__*/_react.default.createElement(_LoadingButton.default, null) : /*#__PURE__*/_react.default.createElement(_SendButton.default, null));
// ============================ Suffix ============================
let suffixNode = actionNode;
if (typeof suffix === 'function') {
suffixNode = suffix(actionNode, {
components: sharedRenderComponents
});
} else if (suffix || suffix === false) {
suffixNode = suffix;
}
// ============================ Prefix ============================
const prefixNode = typeof prefix === 'function' ? prefix(actionNode, {
components: sharedRenderComponents
}) : prefix || null;
// ============================ Header ============================
const headerNode = typeof header === 'function' ? header(actionNode, {
components: sharedRenderComponents
}) : header || null;
// ============================ Footer ============================
const footerNode = typeof footer === 'function' ? footer(actionNode, {
components: sharedRenderComponents
}) : footer || null;
// ============================ Action context Data ============================
const [submitDisabled, setSubmitDisabled] = (0, _react.useState)(!innerValue);
// Custom actions context props
const actionsContextProps = {
prefixCls: actionBtnCls,
onSend: triggerSend,
onSendDisabled: !innerValue,
onClear: triggerClear,
onClearDisabled: !innerValue,
onCancel,
onCancelDisabled: !loading,
onSpeech: () => triggerSpeech(false),
onSpeechDisabled: !speechPermission,
speechRecording,
disabled,
setSubmitDisabled
};
// ============================ Context ============================
const contextValue = _react.default.useMemo(() => ({
value: innerValue,
onChange: triggerValueChange,
slotConfig,
onKeyUp,
onKeyDown,
onPaste,
onPasteFile,
disabled,
readOnly,
submitType,
prefixCls,
styles,
classNames,
autoSize,
components,
triggerSend,
placeholder,
onFocus,
onBlur,
skill,
...restProps
}), [innerValue, triggerValueChange, slotConfig, onKeyUp, onKeyDown, onPaste, onPasteFile, disabled, readOnly, submitType, prefixCls, styles, classNames, autoSize, components, triggerSend, placeholder, onFocus, onBlur, skill, restProps]);
// ============================ Focus =============================
const onContentMouseDown = e => {
// If input focused but click on the container,
// input will lose focus.
// We call `preventDefault` to prevent this behavior
if (!isSlotMode && e.target !== containerRef.current?.querySelector(`.${inputCls}`)) {
e.preventDefault();
}
if (e.target === containerRef.current?.querySelector(`.${inputCls}`)) {
inputRef.current?.focus();
}
};
// ============================ Render ============================
return /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({
key: id,
ref: containerRef,
className: mergedCls,
style: {
...contextConfig.style,
...style,
...contextConfig.styles.root,
...styles.root
}
}, domProps), /*#__PURE__*/_react.default.createElement(_context.SenderContext.Provider, {
value: contextValue
}, /*#__PURE__*/_react.default.createElement(_ActionButton.ActionButtonContext.Provider, {
value: actionsContextProps
}, headerNode && /*#__PURE__*/_react.default.createElement(_SenderHeader.SendHeaderContext.Provider, {
value: {
prefixCls
}
}, headerNode), /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-content`, classNames.content),
style: styles.content,
onMouseDown: onContentMouseDown
}, prefixNode && /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-prefix`, contextConfig.classNames.prefix, classNames.prefix),
style: {
...contextConfig.styles.prefix,
...styles.prefix
}
}, prefixNode), isSlotMode ? /*#__PURE__*/_react.default.createElement(_SlotTextArea.default, {
ref: inputRef
}) : /*#__PURE__*/_react.default.createElement(_TextArea.default, {
ref: inputRef
}), suffixNode && /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(actionListCls, contextConfig.classNames.suffix, classNames.suffix),
style: {
...contextConfig.styles.suffix,
...styles.suffix
}
}, suffixNode)), footerNode && /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-footer`, contextConfig.classNames.footer, classNames.footer),
style: {
...contextConfig.styles.footer,
...styles.footer
}
}, footerNode))));
});
const Sender = ForwardSender;
if (process.env.NODE_ENV !== 'production') {
Sender.displayName = 'Sender';
}
var _default = exports.default = Sender;