split app into more components
This commit is contained in:
parent
46a1f7e2bb
commit
577f982e28
13
src/components/AnswerList/AnswerList.scss
Normal file
13
src/components/AnswerList/AnswerList.scss
Normal file
@ -0,0 +1,13 @@
|
||||
.AnswerList {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 20px;
|
||||
margin-left: 22px;
|
||||
|
||||
p {
|
||||
font-family: monospace;
|
||||
font-size: 18px;
|
||||
margin: 4px 22px 4px 0;
|
||||
}
|
||||
}
|
16
src/components/AnswerList/AnswerList.tsx
Normal file
16
src/components/AnswerList/AnswerList.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import './AnswerList.scss';
|
||||
|
||||
interface IAnswerListProps {
|
||||
answers: Array<string>;
|
||||
}
|
||||
|
||||
export default function AnswerList(props: IAnswerListProps): JSX.Element {
|
||||
|
||||
const answers = props.answers.map(a => <p key={a}>{a}</p>);
|
||||
|
||||
return (
|
||||
<div className="AnswerList">
|
||||
{answers}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
max-height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: 1fr 1fr 1/2fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 0;
|
||||
grid-row-gap: 0;
|
||||
}
|
||||
@ -11,58 +11,3 @@
|
||||
.Answer {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
|
||||
.fileUploader {
|
||||
width: 180px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.fileContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background: #fff;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
|
||||
p {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.UploadSection {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ConfirmButton {
|
||||
width: 180px;
|
||||
height: 30px;
|
||||
|
||||
padding: 6px 23px;
|
||||
background: #3f4257;
|
||||
border-radius: 30px;
|
||||
color: white;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
margin: 10px 10px;
|
||||
transition: all 0.2s ease-in;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
@ -1,22 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import ImageUploader from 'react-images-upload';
|
||||
import MultiCrops from 'react-multi-crops'
|
||||
|
||||
// @ts-ignore
|
||||
import Logo from '../Logo/Logo';
|
||||
// @ts-ignore
|
||||
import AnswerList from "../AnswerList/AnswerList";
|
||||
import Board from '../Board/Board';
|
||||
import Logo from "../Logo/Logo";
|
||||
import Menu from '../Menu/Menu';
|
||||
import Randomizer from "../Randomizer/Randomizer";
|
||||
import Recognizer from "../Recognizer/Recognizer";
|
||||
import Title from "../Title/Title";
|
||||
|
||||
import './App.scss';
|
||||
// @ts-ignore
|
||||
import {findTextRegions} from "../../helpers/findTextRegions";
|
||||
// @ts-ignore
|
||||
import {
|
||||
recognizeTextOnImage,
|
||||
// @ts-ignore
|
||||
recognizeTextOnImageGrid
|
||||
} from "../../helpers/recognizeTextOnImage";
|
||||
|
||||
export type Point = {
|
||||
x: number;
|
||||
@ -33,194 +25,127 @@ export type Solution = {
|
||||
key: string;
|
||||
}
|
||||
|
||||
type Game = {
|
||||
export type Game = {
|
||||
cells: Array<Array<string>>;
|
||||
solutions: Array<Solution>;
|
||||
title: string;
|
||||
description: string;
|
||||
catchword: string;
|
||||
cells: Array<Array<string>>;
|
||||
solutions: Array<Solution>;
|
||||
}
|
||||
|
||||
interface IAppProps {
|
||||
}
|
||||
|
||||
interface IAppState {
|
||||
image: HTMLImageElement | null,
|
||||
selections: Array<any>;
|
||||
cells: Array<Array<string>>;
|
||||
solutions: Array<Solution>;
|
||||
export interface IAppState {
|
||||
game: Game;
|
||||
mode: string;
|
||||
ready: boolean;
|
||||
}
|
||||
|
||||
export default class App extends React.Component<IAppProps, IAppState> {
|
||||
private game: Game;
|
||||
|
||||
constructor(props: IAppProps) {
|
||||
super(props);
|
||||
|
||||
const gameId = (Math.trunc(Math.random() * 100) % 2) + 1;
|
||||
this.game = require(`../../constants/${gameId}.json`);
|
||||
|
||||
this.state = {
|
||||
image: null,
|
||||
selections: [],
|
||||
game: {
|
||||
cells: [],
|
||||
solutions: [],
|
||||
title: "",
|
||||
description: "",
|
||||
catchword: "",
|
||||
},
|
||||
mode: "",
|
||||
ready: false,
|
||||
}
|
||||
}
|
||||
|
||||
handleTakePhoto(pictures: any[], _: any[]): void {
|
||||
const image = document.createElement('img');
|
||||
image.src = URL.createObjectURL(pictures[0]);
|
||||
image.onload = () => this.setState({...this.state, image});
|
||||
handleGameModeSelected(mode: string) {
|
||||
const ready = mode === "reset" ? false : this.state.ready;
|
||||
this.setState({...this.state, mode, ready});
|
||||
}
|
||||
|
||||
handleChangeCoordinate(_: any, __: any, selections: any) {
|
||||
this.setState({...this.state, selections});
|
||||
handleRecognitionFinished(game: Game) {
|
||||
this.setState({...this.state, game, ready: true});
|
||||
}
|
||||
|
||||
handleConfirmClick() {
|
||||
if (!this.state.image || !this.state.selections.length)
|
||||
return;
|
||||
renderTitle(): JSX.Element {
|
||||
if (!this.state.ready) return <div />;
|
||||
|
||||
const mainCanvas = document.createElement('canvas');
|
||||
const mainContext = mainCanvas.getContext('2d');
|
||||
mainCanvas.width = this.state.image.width;
|
||||
mainCanvas.height = this.state.image.height;
|
||||
mainContext?.drawImage(this.state.image, 0, 0);
|
||||
|
||||
const areas = this.state.selections.map(s => s.width * s.height);
|
||||
const gridIndex = areas.indexOf(Math.max(...areas));
|
||||
const gridSelection = this.state.selections.splice(gridIndex, 1)[0];
|
||||
const gridImage = mainContext?.getImageData(
|
||||
gridSelection.x,
|
||||
gridSelection.y,
|
||||
gridSelection.width,
|
||||
gridSelection.height
|
||||
return (
|
||||
<Title
|
||||
title={this.state.game.title}
|
||||
description={this.state.game.description}
|
||||
/>
|
||||
);
|
||||
|
||||
if (!gridImage) {
|
||||
console.log("gridImage is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
const gridTextRegions = findTextRegions(gridImage);
|
||||
if (!gridTextRegions || !gridTextRegions.grid) {
|
||||
console.log("gridRegions is empty");
|
||||
return;
|
||||
renderLogo(): JSX.Element {
|
||||
return <Logo />;
|
||||
}
|
||||
|
||||
let reads = [];
|
||||
reads.push(this.handleGridTextRegionsReadyToRead(gridTextRegions.grid));
|
||||
|
||||
for (let s of this.state.selections) {
|
||||
const tempData = mainContext?.getImageData(s.x, s.y, s.width, s.height);
|
||||
if (!tempData) {
|
||||
console.log("tempData is empty");
|
||||
continue;
|
||||
renderMenu(): JSX.Element {
|
||||
return (
|
||||
<Menu
|
||||
onGameModeSelected={this.handleGameModeSelected.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
reads.push(this.handleStateSelectionsReadyToRead(tempData, s))
|
||||
renderBoard(): JSX.Element {
|
||||
if (!this.state.ready) return <div />;
|
||||
|
||||
return (
|
||||
<Board
|
||||
cells={this.state.game.cells}
|
||||
solutions={this.state.game.solutions}
|
||||
cellSize={60}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Promise.all(reads).then(() => {
|
||||
// free resources
|
||||
if (this.state.image) {
|
||||
URL.revokeObjectURL(this.state.image.src);
|
||||
mainCanvas.width = 0;
|
||||
mainCanvas.height = 0;
|
||||
renderRandomizer(): JSX.Element {
|
||||
if (!(!this.state.ready && this.state.mode === "randomize")) return <div />;
|
||||
|
||||
return (
|
||||
<Randomizer
|
||||
onRecognitionFinished={this.handleRecognitionFinished.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
...this.state,
|
||||
image: null,
|
||||
selections: [],
|
||||
});
|
||||
});
|
||||
renderRecognizer(): JSX.Element {
|
||||
if (!(!this.state.ready && this.state.mode === "recognize")) return <div />;
|
||||
|
||||
return (
|
||||
<Recognizer
|
||||
onRecognitionFinished={this.handleRecognitionFinished.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
async handleGridTextRegionsReadyToRead(grid: any) {
|
||||
const textGrid = await recognizeTextOnImageGrid(grid);
|
||||
let cells = [];
|
||||
renderAnswerList(): JSX.Element {
|
||||
if (!this.state.ready) return <></>;
|
||||
|
||||
for (let row of textGrid) {
|
||||
let line = [];
|
||||
const answers = this.state.game.solutions.map(s => s.key);
|
||||
|
||||
for (let letter of row) {
|
||||
line.push(letter.text);
|
||||
}
|
||||
|
||||
cells.push(line);
|
||||
}
|
||||
|
||||
this.setState({...this.state, cells});
|
||||
}
|
||||
|
||||
async handleStateSelectionsReadyToRead(data: ImageData, s: any) {
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
const tempContext = tempCanvas.getContext('2d');
|
||||
tempCanvas.width = s.width;
|
||||
tempCanvas.height = s.height;
|
||||
tempContext?.putImageData(data, 0, 0);
|
||||
|
||||
const text = await recognizeTextOnImage(tempCanvas);
|
||||
let solutions = this.state.solutions.slice();
|
||||
|
||||
for (let key of text.split("\n")) {
|
||||
solutions.push({key, selection: {start: {x: 0, y: 0}, end: {x: 0, y: 0}}});
|
||||
}
|
||||
|
||||
this.setState({...this.state, solutions});
|
||||
}
|
||||
|
||||
renderAnswers(): Array<JSX.Element> {
|
||||
return this.game.solutions.map(s => s.key).map(k =>
|
||||
<p key={k} className="Answer">{k}</p>);
|
||||
return (
|
||||
<AnswerList
|
||||
answers={answers}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="App">
|
||||
<div />
|
||||
<div>
|
||||
<Logo />
|
||||
<div>
|
||||
<p>{this.game.title}</p>
|
||||
<p>{this.game.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div style={{paddingLeft: '300px'}}>
|
||||
<div className="UploadSection">
|
||||
<ImageUploader
|
||||
withIcon={false}
|
||||
buttonText='Wrzuć zdjęcie!'
|
||||
onChange={this.handleTakePhoto.bind(this)}
|
||||
imgExtension={['.jpg', '.jpeg', '.png']}
|
||||
maxFileSize={5242880 * 5} // 5MB * 5
|
||||
/>
|
||||
<button
|
||||
className="ConfirmButton"
|
||||
onClick={this.handleConfirmClick.bind(this)}
|
||||
style={{visibility: this.state.image ? 'visible' : 'hidden'}}
|
||||
>Potwierdź</button>
|
||||
</div>
|
||||
<MultiCrops
|
||||
src={this.state.image?.src || ''}
|
||||
width={this.state.image?.width}
|
||||
coordinates={this.state.selections}
|
||||
onChange={this.handleChangeCoordinate.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Board
|
||||
cells={this.game.cells}
|
||||
solutions={this.game.solutions}
|
||||
cellSize={60}
|
||||
/>
|
||||
</div>
|
||||
<div style={{paddingLeft: '30px'}}>
|
||||
{this.renderAnswers()}
|
||||
</div>
|
||||
<div /> {this.renderLogo()} <div />
|
||||
<div /> {this.renderMenu()} <div />
|
||||
<div /> {this.renderTitle()} <div />
|
||||
<div /> {this.renderBoard()} <div />
|
||||
<div /> {this.renderRecognizer()} <div />
|
||||
<div /> {this.renderRandomizer()} <div />
|
||||
<div /> {this.renderAnswerList()} <div />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -127,7 +127,9 @@ export default class Board extends React.Component<IBoardProps, IBoardState> {
|
||||
let lines = this.state.answers.map((s, n) =>
|
||||
this.renderLine(s, n));
|
||||
|
||||
const gridTemplateColumns = `repeat(${this.props.cells[0].length}, ${this.props.cellSize}px)`;
|
||||
const x = this.props.cells[0].length;
|
||||
const y = this.props.cellSize;
|
||||
const gridTemplateColumns = `repeat(${x}, ${y}px)`;
|
||||
|
||||
return (
|
||||
<div className="Container">
|
||||
|
@ -21,6 +21,7 @@ interface ILineProps {
|
||||
}
|
||||
|
||||
export default function Line(props: ILineProps): JSX.Element {
|
||||
|
||||
const [m1, sqrt2] = [props.cellSize, Math.sqrt(2)];
|
||||
const [m2, m4, m8] = [m1 / 2, m1 / 4, m1 / 8];
|
||||
const [startX, endX] = [m1 * props.selection.start.x, m1 * props.selection.end.x];
|
||||
|
@ -1,4 +1,5 @@
|
||||
.Logo {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 20px 0;
|
||||
|
@ -2,6 +2,7 @@ import Wide from './crosski_wide.svg'
|
||||
import './Logo.scss';
|
||||
|
||||
export default function Logo(): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="Logo">
|
||||
<img alt="Logo" src={Wide}/>
|
||||
|
9
src/components/Menu/Menu.scss
Normal file
9
src/components/Menu/Menu.scss
Normal file
@ -0,0 +1,9 @@
|
||||
.Menu {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
|
||||
button {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
20
src/components/Menu/Menu.tsx
Normal file
20
src/components/Menu/Menu.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import './Menu.scss';
|
||||
|
||||
interface IMenuProps {
|
||||
onGameModeSelected: (mode: string) => void;
|
||||
}
|
||||
|
||||
export default function Menu(props: IMenuProps): JSX.Element {
|
||||
|
||||
const randomize = () => props.onGameModeSelected('randomize');
|
||||
const recognize = () => props.onGameModeSelected('recognize');
|
||||
const reset = () => props.onGameModeSelected('reset');
|
||||
|
||||
return (
|
||||
<div className="Menu">
|
||||
<button onClick={randomize}>randomize</button>
|
||||
<button onClick={reset}>reset</button>
|
||||
<button onClick={recognize}>recognize</button>
|
||||
</div>
|
||||
);
|
||||
}
|
14
src/components/Randomizer/Randomizer.tsx
Normal file
14
src/components/Randomizer/Randomizer.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import {Game} from "../App/App";
|
||||
|
||||
interface IRandomizerProps {
|
||||
onRecognitionFinished: (game: Game) => void;
|
||||
}
|
||||
|
||||
export default function Randomizer(props: IRandomizerProps): JSX.Element {
|
||||
|
||||
const gameId = (Math.trunc(Math.random() * 100) % 2) + 1;
|
||||
const game = require(`../../constants/${gameId}.json`) as Game;
|
||||
props.onRecognitionFinished(game);
|
||||
|
||||
return <div />;
|
||||
}
|
55
src/components/Recognizer/Recognizer.scss
Normal file
55
src/components/Recognizer/Recognizer.scss
Normal file
@ -0,0 +1,55 @@
|
||||
.Recognizer {
|
||||
display: flex;
|
||||
padding-top: 20px;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.fileUploader {
|
||||
width: 180px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.fileContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background: #fff;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
|
||||
p {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ConfirmButton {
|
||||
width: 180px;
|
||||
height: 30px;
|
||||
|
||||
padding: 6px 23px;
|
||||
background: #3f4257;
|
||||
border-radius: 30px;
|
||||
color: white;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
margin: 10px 10px;
|
||||
transition: all 0.2s ease-in;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
173
src/components/Recognizer/Recognizer.tsx
Normal file
173
src/components/Recognizer/Recognizer.tsx
Normal file
@ -0,0 +1,173 @@
|
||||
import React from "react";
|
||||
import ImageUploader from "react-images-upload";
|
||||
import MultiCrops from "react-multi-crops";
|
||||
import {findTextRegions} from "../../helpers/findTextRegions";
|
||||
import {
|
||||
recognizeTextOnImage,
|
||||
recognizeTextOnImageGrid
|
||||
} from "../../helpers/recognizeTextOnImage";
|
||||
|
||||
import {Game, Solution} from "../App/App";
|
||||
|
||||
import "./Recognizer.scss";
|
||||
|
||||
interface IRecognizerProps {
|
||||
onRecognitionFinished: (game: Game) => void;
|
||||
}
|
||||
|
||||
interface IRecognizerState {
|
||||
image: HTMLImageElement | null,
|
||||
cells: Array<Array<string>>;
|
||||
solutions: Array<Solution>;
|
||||
coordinates: Array<any>;
|
||||
}
|
||||
|
||||
export default class Recognizer extends React.Component<IRecognizerProps, IRecognizerState> {
|
||||
|
||||
constructor(props: IRecognizerProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
image: null,
|
||||
cells: [],
|
||||
solutions: [],
|
||||
coordinates: [],
|
||||
}
|
||||
}
|
||||
|
||||
handleTakePhoto(files: any[], _: any[]): void {
|
||||
const image = document.createElement('img');
|
||||
image.src = URL.createObjectURL(files[0]);
|
||||
image.onload = () => this.setState({...this.state, image});
|
||||
}
|
||||
|
||||
handleChangeCoordinate(_: any, __: any, selections: any) {
|
||||
this.setState({...this.state, coordinates: selections});
|
||||
}
|
||||
|
||||
handleConfirmClick() {
|
||||
if (!this.state.image || !this.state.coordinates.length)
|
||||
return;
|
||||
|
||||
const mainCanvas = document.createElement('canvas');
|
||||
const mainContext = mainCanvas.getContext('2d');
|
||||
mainCanvas.width = this.state.image.width;
|
||||
mainCanvas.height = this.state.image.height;
|
||||
mainContext?.drawImage(this.state.image, 0, 0);
|
||||
|
||||
const areas = this.state.coordinates.map(s => s.width * s.height);
|
||||
const gridIndex = areas.indexOf(Math.max(...areas));
|
||||
const gridSelection = this.state.coordinates.splice(gridIndex, 1)[0];
|
||||
const gridImage = mainContext?.getImageData(
|
||||
gridSelection.x,
|
||||
gridSelection.y,
|
||||
gridSelection.width,
|
||||
gridSelection.height
|
||||
);
|
||||
|
||||
if (!gridImage) {
|
||||
console.log("gridImage is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
const gridTextRegions = findTextRegions(gridImage);
|
||||
if (!gridTextRegions || !gridTextRegions.grid) {
|
||||
console.log("gridRegions is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
let reads = [];
|
||||
reads.push(this.handleGridTextRegionsReadyToRead(gridTextRegions.grid));
|
||||
|
||||
for (let s of this.state.coordinates) {
|
||||
const tempData = mainContext?.getImageData(s.x, s.y, s.width, s.height);
|
||||
if (!tempData) {
|
||||
console.log("tempData is empty");
|
||||
continue;
|
||||
}
|
||||
|
||||
reads.push(this.handleStateSelectionsReadyToRead(tempData, s))
|
||||
}
|
||||
|
||||
Promise.all(reads).then(() => {
|
||||
// free resources
|
||||
if (this.state.image) {
|
||||
URL.revokeObjectURL(this.state.image.src);
|
||||
mainCanvas.width = 0;
|
||||
mainCanvas.height = 0;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
...this.state,
|
||||
image: null,
|
||||
coordinates: [],
|
||||
});
|
||||
|
||||
this.props.onRecognitionFinished({
|
||||
...this.state,
|
||||
title: "Loaded game",
|
||||
description: "Good luck!",
|
||||
catchword: "",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async handleGridTextRegionsReadyToRead(grid: any) {
|
||||
const textGrid = await recognizeTextOnImageGrid(grid);
|
||||
let cells = [];
|
||||
|
||||
for (let row of textGrid) {
|
||||
let line = [];
|
||||
|
||||
for (let letter of row) {
|
||||
line.push(letter.text);
|
||||
}
|
||||
|
||||
cells.push(line);
|
||||
}
|
||||
|
||||
this.setState({...this.state, cells});
|
||||
}
|
||||
|
||||
async handleStateSelectionsReadyToRead(data: ImageData, s: any) {
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
const tempContext = tempCanvas.getContext('2d');
|
||||
tempCanvas.width = s.width;
|
||||
tempCanvas.height = s.height;
|
||||
tempContext?.putImageData(data, 0, 0);
|
||||
|
||||
const text = await recognizeTextOnImage(tempCanvas);
|
||||
let solutions = this.state.solutions.slice();
|
||||
|
||||
for (let key of text.split("\n")) {
|
||||
solutions.push({key, selection: {start: {x: 0, y: 0}, end: {x: 0, y: 0}}});
|
||||
}
|
||||
|
||||
this.setState({...this.state, solutions});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="Recognizer">
|
||||
<ImageUploader
|
||||
withIcon={false}
|
||||
buttonText='Wybierz zdjęcie'
|
||||
onChange={this.handleTakePhoto.bind(this)}
|
||||
imgExtension={['.jpg', '.jpeg', '.png']}
|
||||
maxFileSize={5242880 * 5} // 5MB * 5
|
||||
/>
|
||||
<button
|
||||
className="ConfirmButton"
|
||||
onClick={this.handleConfirmClick.bind(this)}
|
||||
style={{visibility: this.state.image ? 'visible' : 'hidden'}}
|
||||
>Potwierdź</button>
|
||||
<MultiCrops
|
||||
src={this.state.image?.src || ''}
|
||||
width={this.state.image?.width}
|
||||
coordinates={this.state.coordinates}
|
||||
onChange={this.handleChangeCoordinate.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
8
src/components/Title/Title.scss
Normal file
8
src/components/Title/Title.scss
Normal file
@ -0,0 +1,8 @@
|
||||
.Title {
|
||||
text-align: center;
|
||||
margin: 15px 80px;
|
||||
|
||||
p {
|
||||
margin: 4px;
|
||||
}
|
||||
}
|
16
src/components/Title/Title.tsx
Normal file
16
src/components/Title/Title.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import './Title.scss';
|
||||
|
||||
interface ITitleProps {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export default function Title(props: ITitleProps): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="Title">
|
||||
<p>{props.title}</p>
|
||||
<span>{props.description}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -11,4 +11,8 @@ html, body {
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
background: #FFEFBA; /* fallback for old browsers */
|
||||
background: -webkit-linear-gradient(to right, #FFFFFF, #FFEFBA); /* Chrome 10-25, Safari 5.1-6 */
|
||||
background: linear-gradient(to right, #FFFFFF, #FFEFBA); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user