@chatui/core
Version:
The React library for Chatbot UI
133 lines (132 loc) • 4.6 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import React, { useState, useEffect, useRef, useImperativeHandle, useCallback } from 'react';
import canUse from '../../utils/canUse';
import { ClickOutside } from '../ClickOutside';
import { useLocale } from '../ConfigProvider';
import { Icon } from '../Icon';
import { SoundWave } from './SoundWave';
var canPassive = canUse('passiveListener');
var listenerOpts = canPassive ? {
passive: true
} : false;
var listenerOptsWithoutPassive = canPassive ? {
passive: false
} : false;
var MOVE_INTERVAL = 80;
var ts = 0;
var startY = 0;
/** 语音输入状态枚举 */
export var VoiceInputStatus = {
INITED: 'inited',
RECORDING: 'recording',
WILL_CANCEL: 'willCancel'
};
/** 状态文案映射 */
var statusTextMap = _defineProperty(_defineProperty(_defineProperty({}, VoiceInputStatus.INITED, 'hold2talk'), VoiceInputStatus.RECORDING, 'releaseOrSwipe'), VoiceInputStatus.WILL_CANCEL, 'release2send');
export var VoiceInput = /*#__PURE__*/React.forwardRef(function (props, ref) {
var onStart = props.onStart,
onEnd = props.onEnd,
onCancel = props.onCancel,
onClose = props.onClose,
onStatusChange = props.onStatusChange;
var _useState = useState(VoiceInputStatus.INITED),
_useState2 = _slicedToArray(_useState, 2),
status = _useState2[0],
setStatus = _useState2[1];
var btnRef = useRef(null);
var _useLocale = useLocale('VoiceInput'),
trans = _useLocale.trans;
var updateStatus = useCallback(function (newStatus) {
setStatus(newStatus);
if (onStatusChange) {
onStatusChange(newStatus);
}
}, [onStatusChange]);
var doEnd = useCallback(function () {
var duration = Date.now() - ts;
if (onEnd) {
onEnd({
duration: duration
});
}
}, [onEnd]);
var handleClose = useCallback(function () {
updateStatus(VoiceInputStatus.INITED);
if (onClose) {
onClose();
}
}, [onClose, updateStatus]);
useImperativeHandle(ref, function () {
return {
stop: function stop() {
updateStatus(VoiceInputStatus.INITED);
doEnd();
ts = 0;
}
};
});
useEffect(function () {
var btn = btnRef.current;
if (!btn) return;
function handleTouchStart(e) {
if (e.cancelable) {
e.preventDefault();
}
var touch0 = e.touches[0];
startY = touch0.pageY;
ts = Date.now();
updateStatus(VoiceInputStatus.RECORDING);
if (onStart) {
onStart();
}
}
function handleTouchMove(e) {
if (!ts) return;
var nowY = e.touches[0].pageY;
var isCancel = startY - nowY > MOVE_INTERVAL;
updateStatus(isCancel ? VoiceInputStatus.WILL_CANCEL : VoiceInputStatus.RECORDING);
}
function handleTouchEnd(e) {
if (!ts) return;
var endY = e.changedTouches[0].pageY;
var isRecording = startY - endY < MOVE_INTERVAL;
updateStatus(VoiceInputStatus.INITED);
if (isRecording) {
doEnd();
} else if (onCancel) {
onCancel();
}
}
btn.addEventListener('touchstart', handleTouchStart, listenerOptsWithoutPassive);
btn.addEventListener('touchmove', handleTouchMove, listenerOpts);
btn.addEventListener('touchend', handleTouchEnd);
btn.addEventListener('touchcancel', handleTouchEnd);
return function () {
btn.removeEventListener('touchstart', handleTouchStart);
btn.removeEventListener('touchmove', handleTouchMove);
btn.removeEventListener('touchend', handleTouchEnd);
btn.removeEventListener('touchcancel', handleTouchEnd);
};
}, [doEnd, onCancel, onStart, updateStatus]);
var isRecording = status === VoiceInputStatus.RECORDING;
var isCancel = status === VoiceInputStatus.WILL_CANCEL;
return /*#__PURE__*/React.createElement(ClickOutside, {
onClick: handleClose
}, /*#__PURE__*/React.createElement("div", {
className: "VoiceInput",
"data-voice-status": status
}, /*#__PURE__*/React.createElement("div", {
className: "VoiceInput-tip"
}, trans(statusTextMap[status])), /*#__PURE__*/React.createElement("div", {
className: "VoiceInput-control"
}, isRecording && /*#__PURE__*/React.createElement(SoundWave, null), /*#__PURE__*/React.createElement("div", {
className: "VoiceInput-btn",
role: "button",
"aria-label": trans(statusTextMap[status]),
ref: btnRef
}, /*#__PURE__*/React.createElement(Icon, {
className: "VoiceInput-btnIcon",
type: isCancel ? 'cancel' : 'mic'
})))));
});