UNPKG

@chatui/core

Version:

The React library for Chatbot UI

133 lines (132 loc) 4.6 kB
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' }))))); });