react-text-to-speech
Advanced tools
Comparing version 0.6.8 to 0.7.0
import React from "react"; | ||
export function HiVolumeUp(props) { | ||
return React.createElement("span", Object.assign({}, props), | ||
return (React.createElement("span", Object.assign({}, props), | ||
React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": true, width: "1.25rem", height: "1.25rem" }, | ||
React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z" }))); | ||
React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z" })))); | ||
} | ||
export function HiVolumeOff(props) { | ||
return React.createElement("span", Object.assign({}, props), | ||
return (React.createElement("span", Object.assign({}, props), | ||
React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": true, width: "1.25rem", height: "1.25rem" }, | ||
React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM12.293 7.293a1 1 0 011.414 0L15 8.586l1.293-1.293a1 1 0 111.414 1.414L16.414 10l1.293 1.293a1 1 0 01-1.414 1.414L15 11.414l-1.293 1.293a1 1 0 01-1.414-1.414L13.586 10l-1.293-1.293a1 1 0 010-1.414z" }))); | ||
React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM12.293 7.293a1 1 0 011.414 0L15 8.586l1.293-1.293a1 1 0 111.414 1.414L16.414 10l1.293 1.293a1 1 0 01-1.414 1.414L15 11.414l-1.293 1.293a1 1 0 01-1.414-1.414L13.586 10l-1.293-1.293a1 1 0 010-1.414z" })))); | ||
} | ||
export function HiMiniStop(props) { | ||
return React.createElement("span", Object.assign({}, props), | ||
return (React.createElement("span", Object.assign({}, props), | ||
React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": true, width: "1.25rem", height: "1.25rem" }, | ||
React.createElement("path", { d: "M5.25 3A2.25 2.25 0 003 5.25v9.5A2.25 2.25 0 005.25 17h9.5A2.25 2.25 0 0017 14.75v-9.5A2.25 2.25 0 0014.75 3h-9.5z" }))); | ||
React.createElement("path", { d: "M5.25 3A2.25 2.25 0 003 5.25v9.5A2.25 2.25 0 005.25 17h9.5A2.25 2.25 0 0017 14.75v-9.5A2.25 2.25 0 0014.75 3h-9.5z" })))); | ||
} |
@@ -1,4 +0,4 @@ | ||
import React, { DetailedHTMLProps, HTMLAttributes, ReactNode } from 'react'; | ||
import React, { DetailedHTMLProps, HTMLAttributes, ReactNode } from "react"; | ||
export type Button = JSX.Element | string | null; | ||
export type SpeechStatus = 'started' | 'paused' | 'stopped'; | ||
export type SpeechStatus = "started" | "paused" | "stopped"; | ||
export type ChildrenOptions = { | ||
@@ -18,2 +18,3 @@ speechStatus?: SpeechStatus; | ||
lang?: string; | ||
voiceURI?: string | string[]; | ||
startBtn?: Button; | ||
@@ -27,3 +28,3 @@ pauseBtn?: Button; | ||
}; | ||
export type { IconProps } from './icons.js'; | ||
export default function Speech({ text, pitch, rate, volume, lang, startBtn, pauseBtn, stopBtn, useStopOverPause, onError, children, props }: SpeechProps): string | number | boolean | React.JSX.Element | Iterable<React.ReactNode> | null | undefined; | ||
export type { IconProps } from "./icons.js"; | ||
export default function Speech({ text, pitch, rate, volume, lang, voiceURI, startBtn, pauseBtn, stopBtn, useStopOverPause, onError, children, props, }: SpeechProps): string | number | boolean | React.JSX.Element | Iterable<React.ReactNode> | null | undefined; |
@@ -1,8 +0,8 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import { HiMiniStop, HiVolumeOff, HiVolumeUp } from './icons.js'; | ||
export default function Speech({ text, pitch = 1, rate = 1, volume = 1, lang = '', startBtn = React.createElement(HiVolumeUp, null), pauseBtn = React.createElement(HiVolumeOff, null), stopBtn = React.createElement(HiMiniStop, null), useStopOverPause, onError = () => alert('Browser not supported! Try some other browser.'), children, props = {} }) { | ||
const [speechStatus, setSpeechStatus] = useState('stopped'); | ||
import React, { useEffect, useState } from "react"; | ||
import { HiMiniStop, HiVolumeOff, HiVolumeUp } from "./icons.js"; | ||
export default function Speech({ text, pitch = 1, rate = 1, volume = 1, lang, voiceURI, startBtn = React.createElement(HiVolumeUp, null), pauseBtn = React.createElement(HiVolumeOff, null), stopBtn = React.createElement(HiMiniStop, null), useStopOverPause, onError = () => alert("Browser not supported! Try some other browser."), children, props = {}, }) { | ||
const [speechStatus, setSpeechStatus] = useState("stopped"); | ||
const [useStop, setUseStop] = useState(); | ||
const pause = () => { var _a; return speechStatus !== 'paused' && ((_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.pause()); }; | ||
const stop = () => { var _a; return speechStatus !== 'stopped' && ((_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.cancel()); }; | ||
const pause = () => { var _a; return speechStatus !== "paused" && ((_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.pause()); }; | ||
const stop = () => { var _a; return speechStatus !== "stopped" && ((_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.cancel()); }; | ||
function start() { | ||
@@ -12,14 +12,21 @@ const synth = window.speechSynthesis; | ||
return onError(); | ||
setSpeechStatus('started'); | ||
if (speechStatus === 'paused') | ||
setSpeechStatus("started"); | ||
if (speechStatus === "paused") | ||
return synth.resume(); | ||
if (synth.speaking) | ||
synth.cancel(); | ||
const utterance = new window.SpeechSynthesisUtterance(text === null || text === void 0 ? void 0 : text.replace(/\s/g, ' ')); | ||
const utterance = new window.SpeechSynthesisUtterance(text === null || text === void 0 ? void 0 : text.replace(/\s/g, " ")); | ||
utterance.pitch = pitch; | ||
utterance.rate = rate; | ||
utterance.volume = volume; | ||
utterance.lang = lang; | ||
if (lang) | ||
utterance.lang = lang; | ||
if (voiceURI) { | ||
if (!Array.isArray(voiceURI)) | ||
voiceURI = [voiceURI]; | ||
const voices = synth.getVoices(); | ||
utterance.voice = voiceURI.flatMap((uri) => voices.find((voice) => voice.voiceURI === uri) || [])[0] || null; | ||
} | ||
function setStopped() { | ||
setSpeechStatus('stopped'); | ||
setSpeechStatus("stopped"); | ||
utterance.onpause = null; | ||
@@ -31,3 +38,3 @@ utterance.onend = null; | ||
} | ||
utterance.onpause = () => setSpeechStatus('paused'); | ||
utterance.onpause = () => setSpeechStatus("paused"); | ||
utterance.onend = setStopped; | ||
@@ -37,10 +44,2 @@ utterance.onerror = setStopped; | ||
} | ||
function speech() { | ||
if (speechStatus !== 'started') | ||
start(); | ||
else if (useStop === false) | ||
pause(); | ||
else | ||
stop(); | ||
} | ||
useEffect(() => { | ||
@@ -51,5 +50,5 @@ var _a, _b; | ||
}, []); | ||
return typeof children === 'function' ? children({ speechStatus, start, pause, stop }) : React.createElement("div", Object.assign({ style: { display: 'flex', columnGap: '1rem' } }, props), | ||
React.createElement("span", { role: 'button', onClick: speech }, speechStatus !== 'started' ? startBtn : useStop === false ? pauseBtn : stopBtn), | ||
useStop === false && stopBtn && React.createElement("span", { role: 'button', onClick: stop }, stopBtn)); | ||
return typeof children === "function" ? (children({ speechStatus, start, pause, stop })) : (React.createElement("div", Object.assign({ style: { display: "flex", columnGap: "1rem" } }, props), | ||
speechStatus !== "started" ? (React.createElement("span", { role: "button", onClick: start }, startBtn)) : useStop === false ? (React.createElement("span", { role: "button", onClick: pause }, pauseBtn)) : (React.createElement("span", { role: "button", onClick: stop }, stopBtn)), | ||
useStop === false && stopBtn && (React.createElement("span", { role: "button", onClick: stop }, stopBtn)))); | ||
} |
{ | ||
"name": "react-text-to-speech", | ||
"version": "0.6.8", | ||
"version": "0.7.0", | ||
"description": "An easy to use react component for the Web Speech API.", | ||
@@ -34,3 +34,3 @@ "type": "module", | ||
"devDependencies": { | ||
"@types/react": "^18.2.48" | ||
"@types/react": "^18.2.52" | ||
}, | ||
@@ -37,0 +37,0 @@ "scripts": { |
176
README.md
# react-text-to-speech | ||
An easy to use react component for the [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API). | ||
It is as easy as to import a React component! | ||
## Features | ||
- Text-to-speech | ||
@@ -11,4 +14,7 @@ - Easy to use | ||
- Fully Customizable. See [usage with FaC](#full-customization) | ||
## Installation | ||
To install react-text-to-speech | ||
```bash | ||
@@ -27,95 +33,131 @@ # with npm: | ||
``` | ||
## Usage | ||
When you use the `<Speech>` component of `react-text-to-speech`, initially the user will see the `startBtn` and when the user clicks on it, the speech instance will start and the user will see the `pauseBtn` which can be used to pause the speech instance. The user will also see the `stopBtn` which can be used to stop the speech instance at any moment. | ||
#### Basic Usage | ||
```jsx | ||
import React from 'react' | ||
import Speech from 'react-text-to-speech' | ||
import React from "react"; | ||
import Speech from "react-text-to-speech"; | ||
export default function App() { | ||
return <Speech text='This library is awesome!' /> | ||
return <Speech text="This library is awesome!" />; | ||
} | ||
``` | ||
#### Advanced Usage | ||
This is the use case where `react-text-to-speech` outshines the other text-to-speech libraries. | ||
Let's assume that you fetch news from any News API and the API returns 3 news in response as shown below. Now if the user clicks on `startBtn` of #1 news (assuming # as id) and then clicks on `startBtn` on #2 news before the speech instance of #1 news ends, then `react-text-to-speech` will not just stop the #1 news speech instance and start the #2 news speech instance, but will also convert the `pauseBtn` of #1 news to `startBtn`, thus avoiding any confusion. | ||
```jsx | ||
import React from 'react' | ||
import Speech from 'react-text-to-speech' | ||
import React from "react"; | ||
import Speech from "react-text-to-speech"; | ||
export default function App() { | ||
// 'news' holds response from some News API | ||
const news = [ | ||
{ id: '1', title: 'First random title', desc: 'First random description' }, | ||
{ id: '2', title: 'Second random title', desc: 'Second random description' }, | ||
{ id: '3', title: 'Third random title', desc: 'Third random description' }, | ||
] | ||
// 'news' holds response from some News API | ||
const news = [ | ||
{ id: "1", title: "First random title", desc: "First random description" }, | ||
{ id: "2", title: "Second random title", desc: "Second random description" }, | ||
{ id: "3", title: "Third random title", desc: "Third random description" }, | ||
]; | ||
return <> | ||
{news.map(({ id, title, desc }) => <div key={id}> | ||
<h4>{title}</h4> | ||
<div>{desc}</div> | ||
<Speech text={`${title}. ${desc}`} /> | ||
</div>)} | ||
return ( | ||
<> | ||
{news.map(({ id, title, desc }) => ( | ||
<div key={id}> | ||
<h4>{title}</h4> | ||
<div>{desc}</div> | ||
<Speech text={`${title}. ${desc}`} /> | ||
</div> | ||
))} | ||
</> | ||
); | ||
} | ||
``` | ||
#### Partial Customization | ||
Use props provided by `<Speech>` component to customize it. | ||
```jsx | ||
import React from 'react'; | ||
import Speech from 'react-text-to-speech'; | ||
import React from "react"; | ||
import Speech from "react-text-to-speech"; | ||
export default function App() { | ||
const startBtn = <button className='my-start-btn'>Start Speech</button> | ||
const pauseBtn = <button className='my-pause-btn'>Pause Speech</button> | ||
const stopBtn = <button className='my-stop-btn'>Stop Speech</button> | ||
const startBtn = <button className="my-start-btn">Start Speech</button>; | ||
const pauseBtn = <button className="my-pause-btn">Pause Speech</button>; | ||
const stopBtn = <button className="my-stop-btn">Stop Speech</button>; | ||
return <Speech | ||
text='This is a partially customized speech component.' | ||
pitch={1.5} | ||
rate={2} | ||
volume={0.5} | ||
startBtn={startBtn} | ||
pauseBtn={pauseBtn} | ||
stopBtn={stopBtn} | ||
props={{ title: 'React Text-To-Speech Component' }} | ||
onError={() => console.error('Browser not supported!')} | ||
return ( | ||
<Speech | ||
text="This is a partially customized speech component." | ||
pitch={1.5} | ||
rate={2} | ||
volume={0.5} | ||
voiceURI="Microsoft Heera - English (India)" | ||
startBtn={startBtn} | ||
pauseBtn={pauseBtn} | ||
stopBtn={stopBtn} | ||
props={{ title: "React Text-To-Speech Component" }} | ||
onError={() => console.error("Browser not supported!")} | ||
/> | ||
); | ||
} | ||
``` | ||
#### Full Customization | ||
#### Full Customization | ||
Use Function as Children(FaC) to fully customize the `<Speech>` component. | ||
```jsx | ||
import React from 'react' | ||
import Speech from 'react-text-to-speech' | ||
import React from "react"; | ||
import Speech from "react-text-to-speech"; | ||
export default function App() { | ||
return <Speech | ||
text='This is a fully customized speech component.' | ||
pitch={1.5} | ||
rate={2} | ||
volume={0.5} | ||
onError={() => console.error('Browser not supported!')} | ||
return ( | ||
<Speech | ||
text="This is a fully customized speech component." | ||
pitch={1.5} | ||
rate={2} | ||
volume={0.5} | ||
voiceURI="Microsoft Heera - English (India)" | ||
onError={() => console.error("Browser not supported!")} | ||
> | ||
{({ speechStatus, start, pause, stop }) => ( | ||
<YourCustomComponent> | ||
{speechStatus !== 'started' && <button className='my-start-btn' onClick={start}>Start Speech</button>} | ||
{speechStatus === 'started' && <button className='my-pause-btn' onClick={pause}>Pause Speech</button>} | ||
<button className='my-stop-btn' onClick={stop}>Stop Speech</button> | ||
</YourCustomComponent> | ||
)} | ||
{({ speechStatus, start, pause, stop }) => ( | ||
<YourCustomComponent> | ||
{speechStatus !== "started" && ( | ||
<button className="my-start-btn" onClick={start}> | ||
Start Speech | ||
</button> | ||
)} | ||
{speechStatus === "started" && ( | ||
<button className="my-pause-btn" onClick={pause}> | ||
Pause Speech | ||
</button> | ||
)} | ||
<button className="my-stop-btn" onClick={stop}> | ||
Stop Speech | ||
</button> | ||
</YourCustomComponent> | ||
)} | ||
</Speech> | ||
); | ||
} | ||
``` | ||
## Speech Component API Reference | ||
Here is the full API for the `<Speech>` component, these properties can be set on an instance of Speech: | ||
| Parameter | Type | Required | Default | Description | | ||
| - | - | - | - | - | | ||
| `text` | `String` | Yes | - | It contains the text to be spoken when `startBtn` is clicked. | | ||
| `pitch` | `Number (0 to 2)` | No | 1 | The pitch at which the utterance will be spoken. | | ||
| `rate` | `Number (0.1 to 10)` | No | 1 | The speed at which the utterance will be spoken. | | ||
| `volume` | `Number (0 to 1)` | No | 1 | The volume at which the utterance will be spoken. | | ||
| `lang` | `String` | No | - | The language in which the utterance will be spoken. | | ||
| `text` | `string` | Yes | - | It contains the text to be spoken when `startBtn` is clicked. | | ||
| `pitch` | `number (0 to 2)` | No | 1 | The pitch at which the utterance will be spoken. | | ||
| `rate` | `number (0.1 to 10)` | No | 1 | The speed at which the utterance will be spoken. | | ||
| `volume` | `number (0 to 1)` | No | 1 | The volume at which the utterance will be spoken. | | ||
| `lang` | `string` | No | - | The language in which the utterance will be spoken. | | ||
| `voiceURI` | `string \| string[]` | No | - | The voice using which the utterance will be spoken. If provided an array, further voices will be used as fallback if initial voices are not found. See possible values [here](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis/getVoices). | | ||
| `startBtn` | [`Button`](#button) | No | `<HiVolumeUp />` | Button to start the speech instance. | | ||
@@ -128,15 +170,31 @@ | `pauseBtn` | [`Button`](#button) | No | `<HiVolumeOff />` | Button to pause the speech instance. | | ||
| `children` | [`Children`](#children) | No | - | See [usage with FaC](#full-customization) | | ||
### Types | ||
#### Button | ||
```typescript | ||
type Button = JSX.Element | string | null | ||
type Button = JSX.Element | string | null; | ||
``` | ||
#### Children | ||
```typescript | ||
import { ReactNode } from 'react'; | ||
type SpeechStatus = 'started' | 'paused' | 'stopped' | ||
type ChildrenOptions = { speechStatus?: SpeechStatus, start?: Function, pause?: Function, stop?: Function } | ||
type Children = (childrenOptions: ChildrenOptions) => ReactNode | ||
import { ReactNode } from "react"; | ||
type SpeechStatus = "started" | "paused" | "stopped"; | ||
type ChildrenOptions = { | ||
speechStatus?: SpeechStatus; | ||
start?: Function; | ||
pause?: Function; | ||
stop?: Function; | ||
}; | ||
type Children = (childrenOptions: ChildrenOptions) => ReactNode; | ||
``` | ||
## Author | ||
[Sahil Aggarwal](https://www.github.com/SahilAggarwal2004) | ||
[Sahil Aggarwal](https://www.github.com/SahilAggarwal2004) | ||
### Contributors | ||
- [Akshay Srivastava](https://github.com/akshaypx) |
15557
198