Skip to content

Commit 6630c86

Browse files
committed
Initial commit
1 parent d546d76 commit 6630c86

9 files changed

+478
-0
lines changed

package.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "react",
3+
"version": "1.0.0",
4+
"description": "React example starter project",
5+
"keywords": [
6+
"react",
7+
"starter"
8+
],
9+
"main": "src/index.js",
10+
"dependencies": {
11+
"react": "17.0.0",
12+
"react-dom": "17.0.0",
13+
"react-scripts": "3.4.3",
14+
"react-speech": "1.0.2"
15+
},
16+
"devDependencies": {
17+
"typescript": "3.8.3"
18+
},
19+
"scripts": {
20+
"start": "react-scripts start",
21+
"build": "react-scripts build",
22+
"test": "react-scripts test --env=jsdom",
23+
"eject": "react-scripts eject"
24+
},
25+
"browserslist": [
26+
">0.2%",
27+
"not dead",
28+
"not ie <= 11",
29+
"not op_mini all"
30+
]
31+
}

public/index.html

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7+
<meta name="theme-color" content="#000000">
8+
<!--
9+
manifest.json provides metadata used when your web app is added to the
10+
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
11+
-->
12+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
13+
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
14+
<!--
15+
Notice the use of %PUBLIC_URL% in the tags above.
16+
It will be replaced with the URL of the `public` folder during the build.
17+
Only files inside the `public` folder can be referenced from the HTML.
18+
19+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
20+
work correctly both with client-side routing and a non-root public URL.
21+
Learn how to configure a non-root public URL by running `npm run build`.
22+
-->
23+
<title>React App</title>
24+
</head>
25+
26+
<body>
27+
<noscript>
28+
You need to enable JavaScript to run this app.
29+
</noscript>
30+
<div id="root"></div>
31+
<!--
32+
This HTML file is a template.
33+
If you open it directly in the browser, you will see an empty page.
34+
35+
You can add webfonts, meta tags, or analytics to this file.
36+
The build step will place the bundled scripts into the <body> tag.
37+
38+
To begin the development, run `npm start` or `yarn start`.
39+
To create a production bundle, use `npm run build` or `yarn build`.
40+
-->
41+
</body>
42+
43+
</html>

src/App.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from "react";
2+
import Speech from "./speech";
3+
import "./styles.css";
4+
5+
export default function App() {
6+
return (
7+
<div className="App">
8+
<h1>Text to speech próba</h1>
9+
<Speech lang="hu-HU" text="Jónapot kívánok" pause resume />
10+
</div>
11+
);
12+
}

src/button.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import React, { Component } from "react";
2+
import PropTypes from "prop-types";
3+
4+
export default class Button extends Component {
5+
constructor(props) {
6+
super(props);
7+
this.state = {
8+
focus: false,
9+
hover: false,
10+
color: this.props.styles.button.Color,
11+
backgroundColor: this.props.styles.button.backgroundColor
12+
};
13+
this.enter = this.enter.bind(this);
14+
this.leave = this.leave.bind(this);
15+
}
16+
17+
enter() {
18+
this.setState({ hover: true });
19+
}
20+
21+
leave() {
22+
this.setState({ hover: false });
23+
}
24+
25+
render() {
26+
const backgroundColor = this.state.hover
27+
? this.props.styles.hover.backgroundColor
28+
: this.state.backgroundColor;
29+
30+
const color = this.state.hover
31+
? this.props.styles.hover.color
32+
: this.state.color;
33+
34+
const style = Object.assign({}, this.props.styles.button, {
35+
color: color,
36+
backgroundColor: backgroundColor
37+
});
38+
39+
return (
40+
<button
41+
type="button"
42+
{...this.props}
43+
style={style}
44+
onMouseEnter={this.enter}
45+
onMouseLeave={this.leave}
46+
/>
47+
);
48+
}
49+
}
50+
51+
Button.propTypes = {
52+
styles: PropTypes.object.isRequired,
53+
onClick: PropTypes.func.isRequired
54+
};

src/index.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from "react";
2+
import ReactDOM from "react-dom";
3+
4+
import App from "./App";
5+
6+
const rootElement = document.getElementById("root");
7+
ReactDOM.render(
8+
<React.StrictMode>
9+
<App />
10+
</React.StrictMode>,
11+
rootElement
12+
);

src/speech.js

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import React, { Component } from "react";
2+
import PropTypes from "prop-types";
3+
import update from "immutability-helper";
4+
import style from "./style";
5+
import SpeechSynthesis from "./speechSynthesis";
6+
import Button from "./button";
7+
8+
export default class Speech extends Component {
9+
constructor(props) {
10+
super(props);
11+
this.state = {
12+
styles: this.props.styles || style
13+
};
14+
this.play = this.play.bind(this);
15+
this.pause = this.pause.bind(this);
16+
this.resume = this.resume.bind(this);
17+
this.stop = this.stop.bind(this);
18+
this.onend = this.onend.bind(this);
19+
this.onerror = this.onerror.bind(this);
20+
}
21+
22+
componentDidMount() {
23+
console.log(style);
24+
this.setButtonState("all", "none", "none", "none");
25+
}
26+
27+
setButtonState(play, stop, pause, resume) {
28+
var newState = update(this.state, {
29+
styles: {
30+
play: { button: { pointerEvents: { $set: play } } },
31+
stop: { button: { pointerEvents: { $set: stop } } },
32+
pause: { button: { pointerEvents: { $set: pause } } },
33+
resume: { button: { pointerEvents: { $set: resume } } }
34+
}
35+
});
36+
37+
this.setState(newState);
38+
}
39+
40+
setSpeechSynthesis() {
41+
this.speechSynthesis = new SpeechSynthesis(this.props);
42+
this.speechSynthesis.onend(this.onend);
43+
this.speechSynthesis.onerror(this.onerror);
44+
}
45+
46+
play() {
47+
this.setSpeechSynthesis();
48+
this.speechSynthesis.speak();
49+
this.setButtonState("none", "all", "all", "none");
50+
}
51+
52+
pause() {
53+
this.speechSynthesis.pause();
54+
this.setButtonState("none", "all", "none", "all");
55+
}
56+
57+
resume() {
58+
this.speechSynthesis.resume();
59+
this.setButtonState("none", "all", "all", "none");
60+
}
61+
62+
stop() {
63+
this.speechSynthesis.cancel();
64+
this.setButtonState("all", "none", "none", "none");
65+
}
66+
67+
onend() {
68+
this.stop();
69+
}
70+
71+
onerror() {
72+
this.stop();
73+
}
74+
75+
render() {
76+
if (this.props.disabled || !SpeechSynthesis.supported()) {
77+
return (
78+
<span className="rs-container" style={this.state.styles.container}>
79+
<span className="rs-text" style={this.state.styles.text}>
80+
{this.props.text}
81+
</span>
82+
</span>
83+
);
84+
}
85+
86+
var play;
87+
var stop;
88+
var pause;
89+
var resume;
90+
91+
if (this.props.textAsButton) {
92+
play = (
93+
<Button
94+
className="rs-play"
95+
styles={this.state.styles.play}
96+
onClick={this.play}
97+
>
98+
<span className="rs-text" style={this.state.styles.text}>
99+
{this.props.displayText || this.props.text}
100+
</span>
101+
</Button>
102+
);
103+
} else {
104+
play = (
105+
<Button
106+
className="rs-play"
107+
styles={this.state.styles.play}
108+
onClick={this.play}
109+
>
110+
<svg
111+
xmlns="http://www.w3.org/2000/svg"
112+
width={this.state.styles.play.button.width}
113+
height={this.state.styles.play.button.height}
114+
viewBox="0 0 24 24"
115+
>
116+
<path d="M8 5v14l11-7z" />
117+
<path d="M0 0h24v24H0z" fill="none" />
118+
</svg>
119+
</Button>
120+
);
121+
}
122+
123+
if (this.props.stop) {
124+
stop = (
125+
<Button
126+
className="rs-stop"
127+
styles={this.state.styles.stop}
128+
onClick={this.stop}
129+
>
130+
<svg
131+
xmlns="http://www.w3.org/2000/svg"
132+
width={this.state.styles.stop.width}
133+
height={this.state.styles.stop.height}
134+
viewBox="0 0 24 24"
135+
>
136+
<path d="M0 0h24v24H0z" fill="none" />
137+
<path d="M6 6h12v12H6z" />
138+
</svg>
139+
</Button>
140+
);
141+
}
142+
143+
if (this.props.pause) {
144+
pause = (
145+
<Button
146+
className="rs-pause"
147+
styles={this.state.styles.pause}
148+
onClick={this.pause}
149+
>
150+
<svg
151+
xmlns="http://www.w3.org/2000/svg"
152+
width={this.state.styles.pause.button.width}
153+
height={this.state.styles.pause.button.height}
154+
viewBox="0 0 24 24"
155+
>
156+
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
157+
<path d="M0 0h24v24H0z" fill="none" />
158+
</svg>
159+
</Button>
160+
);
161+
}
162+
163+
if (this.props.resume) {
164+
resume = (
165+
<Button
166+
className="rs-resume"
167+
styles={this.state.styles.resume}
168+
onClick={this.resume}
169+
>
170+
<svg
171+
xmlns="http://www.w3.org/2000/svg"
172+
width={this.state.styles.resume.button.width}
173+
height={this.state.styles.resume.button.height}
174+
viewBox="0 0 24 24"
175+
>
176+
<path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z" />
177+
<path d="M0 0h24v24H0z" fill="none" />
178+
</svg>
179+
</Button>
180+
);
181+
}
182+
183+
return (
184+
<span className="rs-container" style={this.state.styles.container}>
185+
{play} {stop} {pause} {resume}
186+
</span>
187+
);
188+
}
189+
}
190+
191+
Speech.propTypes = {
192+
styles: PropTypes.object,
193+
text: PropTypes.string.isRequired,
194+
pitch: PropTypes.string,
195+
rate: PropTypes.string,
196+
volume: PropTypes.string,
197+
lang: PropTypes.string,
198+
voiceURI: PropTypes.string,
199+
voice: PropTypes.string,
200+
textAsButton: PropTypes.bool,
201+
displayText: PropTypes.string,
202+
disabled: PropTypes.bool,
203+
stop: PropTypes.bool,
204+
pause: PropTypes.bool,
205+
resume: PropTypes.bool
206+
};

0 commit comments

Comments
 (0)