UNPKG

@amaui/ui-react

Version:
161 lines (159 loc) 7.27 kB
import _extends from "@babel/runtime/helpers/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; const _excluded = ["size", "read", "speechSynthesisUtterance", "language", "pitch", "rate", "text", "voice", "volume", "loading", "disabled", "Icon", "IconPause", "onStart", "onPause", "onResume", "onEnd", "onError", "TooltipProps", "IconButtonProps", "IconProps", "className"]; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } import React from 'react'; import { is, isEnvironment, wait } from '@amaui/utils'; import { classNames, style as styleMethod, useAmauiTheme } from '@amaui/style-react'; import IconMaterialPause from '@amaui/icons-material-rounded-react/IconMaterialPauseW100'; import IconMaterialTextToSpeech from '@amaui/icons-material-rounded-react/IconMaterialTextToSpeechW100'; import LineElement from '../Line'; import TooltipElement from '../Tooltip'; import IconButtonElement from '../IconButton'; import { staticClassName } from '../utils'; const useStyle = styleMethod(theme => ({ root: {}, iconButton: { '&.amaui-IconButton-root': { transition: theme.methods.transitions.make('transform'), '&:active': { transform: 'scale(0.94)' } } } }), { name: 'amaui-TextToSpeech' }); const TextToSpeech = /*#__PURE__*/React.forwardRef((props_, ref) => { const theme = useAmauiTheme(); const props = React.useMemo(() => _objectSpread(_objectSpread(_objectSpread({}, theme?.ui?.elements?.all?.props?.default), theme?.ui?.elements?.amauiTextToSpeech?.props?.default), props_), [props_]); const Line = React.useMemo(() => theme?.elements?.Line || LineElement, [theme]); const Tooltip = React.useMemo(() => theme?.elements?.Tooltip || TooltipElement, [theme]); const IconButton = React.useMemo(() => theme?.elements?.IconButton || IconButtonElement, [theme]); const { size = 'regular', read, speechSynthesisUtterance, language, pitch, rate, text, voice, volume, loading, disabled, Icon: Icon_ = IconMaterialTextToSpeech, IconPause = IconMaterialPause, onStart: onStart_, onPause: onPause_, onResume: onResume_, onEnd: onEnd_, onError: onError_, TooltipProps, IconButtonProps, IconProps, className } = props, other = _objectWithoutProperties(props, _excluded); const [status, setStatus] = React.useState('initial'); const { classes } = useStyle(); const refs = { root: React.useRef(undefined), recognition: React.useRef(undefined), status: React.useRef(status) }; refs.status.current = status; const supported = isEnvironment('browser') && 'speechSynthesis' in window && 'SpeechSynthesisUtterance' in window; const onPause = React.useCallback(async event => { setStatus('paused'); if (is('function', onPause_)) onPause_(event); }, [onPause_]); const onResume = React.useCallback(async event => { setStatus('resumed'); if (is('function', onResume_)) onResume_(event); }, [onResume_]); const onEnd = React.useCallback(event => { setStatus('initial'); if (is('function', onEnd_)) onEnd_(event); }, [onEnd_]); const onError = React.useCallback(event => { setStatus('initial'); if (is('function', onError_)) onError_(event); }, [onError_]); React.useEffect(() => { // Clean up previous utterance // if there was any if (supported) { window.speechSynthesis.cancel(); setStatus('initial'); } }, [read, speechSynthesisUtterance, supported, language, pitch, rate, text, voice, volume]); const onStart = React.useCallback(async event => { if (supported && is('string', read)) { setStatus('started'); window.speechSynthesis.cancel(); await wait(140); const utterance = speechSynthesisUtterance || new SpeechSynthesisUtterance(read); // properties if (language !== undefined) utterance.lang = language; if (pitch !== undefined) utterance.pitch = pitch; if (rate !== undefined) utterance.rate = rate; if (text !== undefined) utterance.text = text; if (voice !== undefined) utterance.voice = voice; if (volume !== undefined) utterance.volume = volume; // events utterance.addEventListener('pause', onPause); utterance.addEventListener('resume', onResume); utterance.addEventListener('end', onEnd); utterance.addEventListener('error', onError); window.speechSynthesis.speak(utterance); if (is('function', onStart_)) onStart_(event); } }, [read, speechSynthesisUtterance, supported, onStart_, onPause, onResume, onEnd, language, pitch, rate, text, voice, volume]); const onClick = React.useCallback(async () => { const status_ = refs.status.current; if (['started', 'resumed'].includes(status_)) window.speechSynthesis.pause();else if (status_ === 'paused') window.speechSynthesis.resume();else if (status_ === 'initial') onStart(); }, [onStart]); const iconProps = _objectSpread({ size }, IconProps); const iconButtonProps = _objectSpread({ size, loading, disabled }, IconButtonProps); if (!supported) return null; let IconToUse = Icon_; let name = 'Text to speech'; if (['started', 'resumed'].includes(status)) { IconToUse = IconPause; name = 'Pause'; } if (status === 'paused') name = 'Resume'; return /*#__PURE__*/React.createElement(Line, _extends({ ref: item => { if (ref) { if (is('function', ref)) ref(item);else ref.current = item; } refs.root.current = item; }, gap: 1, direction: "row", align: "center", className: classNames([staticClassName('TextToSpeech', theme) && [`amaui-TextToSpeech-root`, `amaui-TextToSpeech-size-${size}`], className, classes.root]) }, other), /*#__PURE__*/React.createElement(Tooltip, _extends({ name: name }, TooltipProps), /*#__PURE__*/React.createElement(IconButton, _extends({ onClick: onClick }, iconButtonProps, { selected: status === 'started', disabled: disabled !== undefined ? disabled : !supported, className: classNames([staticClassName('TextToSpeech', theme) && [`amaui-TextToSpeech-iconButton`], classes.iconButton]) }), /*#__PURE__*/React.createElement(IconToUse, iconProps)))); }); TextToSpeech.displayName = 'amaui-TextToSpeech'; export default TextToSpeech;