2909
parent
0b927c2204
commit
5dd170683f
|
|
@ -43,4 +43,5 @@
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
114
src/App.tsx
114
src/App.tsx
|
|
@ -2,82 +2,107 @@ import { useState } from 'react'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
import Accordion from 'react-bootstrap/Accordion';
|
import Accordion from 'react-bootstrap/Accordion';
|
||||||
import Form from 'react-bootstrap/Form';
|
import Form from 'react-bootstrap/Form';
|
||||||
import { Button, Modal } from 'react-bootstrap';
|
import { Badge, Button, Modal } from 'react-bootstrap';
|
||||||
import { initials, finales, syllables } from './Data';
|
import { initials, finales, syllables, tones } from './Data';
|
||||||
import { Btn, BtnSimple } from './Btn';
|
|
||||||
import { isEnabled, toggle } from './Utils';
|
import { isEnabled, toggle } from './Utils';
|
||||||
import { Syllable } from './Types';
|
import { Found, Syllable, Tone } from './Types';
|
||||||
|
import { strings } from './Strings';
|
||||||
|
|
||||||
|
const defaultFoundState:Found = {
|
||||||
|
allfinales: false,
|
||||||
|
allInitiales: false,
|
||||||
|
finales: [],
|
||||||
|
initiales: [],
|
||||||
|
syllables: [],
|
||||||
|
tones: []
|
||||||
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
const [ count, setCount ] = useState(10)
|
const [ count, setCount ] = useState(10)
|
||||||
const [ pause, setPause ] = useState(3)
|
const [ pause, setPause ] = useState(3)
|
||||||
const [ allInitiales, setAllInitiales] = useState(false)
|
|
||||||
const [ allFinales, setAllFinales] = useState(false)
|
|
||||||
|
|
||||||
const [ enabledInitials, setInitialsState ] = useState([] as String[])
|
const [ found, setFound ] = useState(defaultFoundState)
|
||||||
const toggleInitialsState = (caption: String) => setInitialsState(toggle(enabledInitials,caption))
|
|
||||||
|
|
||||||
const [ enabledFinals, setFinalsState ] = useState([] as String[])
|
|
||||||
const toggleFinalsState = (caption: String) => setFinalsState(toggle(enabledFinals,caption))
|
|
||||||
|
|
||||||
const onchangepause = (e: React.ChangeEvent<HTMLInputElement> ) => setPause( Number(e.target.value) )
|
const onchangepause = (e: React.ChangeEvent<HTMLInputElement> ) => setPause( Number(e.target.value) )
|
||||||
const onchangecount = (e: React.ChangeEvent<HTMLInputElement> ) => setCount( Number(e.target.value) )
|
const onchangecount = (e: React.ChangeEvent<HTMLInputElement> ) => setCount( Number(e.target.value) )
|
||||||
|
//---------------- to found state
|
||||||
const toggleAllInitiales = () => {
|
const toggleAllInitiales = () => {
|
||||||
setAllInitiales( !allInitiales )
|
setFound({...found,
|
||||||
if (!allInitiales) setInitialsState(initials)
|
allInitiales: !found.allInitiales,
|
||||||
else setInitialsState([])
|
initiales: found.allInitiales ? [] : initials
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
const toggleInitialsState = (caption: String) => setFound({...found,
|
||||||
|
initiales: toggle(found.initiales,caption)
|
||||||
|
})
|
||||||
|
const toggleInitiales = (captions: String[]) => setFound({...found,
|
||||||
|
initiales: [...found.initiales.filter( f => !captions.includes(f)),
|
||||||
|
...captions.filter( c => !found.initiales.includes(c))]
|
||||||
|
})
|
||||||
|
// let arr3 = [...arr1, ...arr2];
|
||||||
|
//uniqueItems = [...new Set(items)]
|
||||||
const toggleAllFinales = () => {
|
const toggleAllFinales = () => {
|
||||||
setAllFinales( !allFinales )
|
setFound({...found,
|
||||||
if (!allFinales) setFinalsState(finales.map((f)=>f.finale))
|
allfinales: !found.allfinales,
|
||||||
else setFinalsState([])
|
finales: found.allfinales ? [] : finales.map( f => f.finale)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
const toggleFinalsState = (caption: String) => setFound({...found,
|
||||||
|
finales: toggle(found.finales,caption)
|
||||||
|
})
|
||||||
|
//----------------------
|
||||||
|
|
||||||
const foundSyllables = ():Syllable[] => syllables.filter( (syl) => ( enabledInitials.some( (i) => i == syl.initiale ) )
|
const foundSyllables = ():Syllable[] => syllables.filter( (syl) => ( found.initiales.some( (i) => i == syl.initiale ) )
|
||||||
&& ( enabledFinals.some( (f) => f == syl.finale ) ) )
|
&& ( found.finales.some( (f) => f == syl.finale ) ) )
|
||||||
|
|
||||||
const found = ():boolean => foundSyllables().length > 0
|
const isFound = ():boolean => foundSyllables().length > 0
|
||||||
|
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
|
|
||||||
const handleClose = () => setShow(false);
|
const handleClose = () => setShow(false);
|
||||||
const handleShow = () => setShow(true);
|
const handleShow = () => setShow(true);
|
||||||
|
|
||||||
|
const foundTones = ():Tone[] => tones.filter( (t) => foundSyllables().some( (syl) => syl.tones.some( (st) => st===t.tone) ) )
|
||||||
|
// const randomTones = ( length:number, fromTones:Tone[]):Tone[] => {
|
||||||
|
// const ftones = foundTones();
|
||||||
|
// return ftones
|
||||||
|
// }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1>Диктант pīnyīn</h1>
|
<h1>Диктант pīnyīn</h1>
|
||||||
<Accordion defaultActiveKey="0">
|
<Accordion defaultActiveKey="0">
|
||||||
<Accordion.Item eventKey="0">
|
<Accordion.Item eventKey="0">
|
||||||
<Accordion.Header>Выберите инициали</Accordion.Header>
|
<Accordion.Header>{strings.selectInitiales}</Accordion.Header>
|
||||||
<Accordion.Body>
|
<Accordion.Body>
|
||||||
<Btn type="primary" text="Выбрать все" altText="Снять все" onclick={toggleAllInitiales}/>
|
<Button variant={found.allInitiales ? "primary" : "outline-primary"}
|
||||||
{initials.map( (text, i) => <BtnSimple
|
onClick= {()=>toggleInitiales( found.allInitiales ? [] : initials )}>
|
||||||
key={i}
|
{found.allInitiales ? strings.unselectAll : strings.selectAll}
|
||||||
type="primary"
|
</Button>
|
||||||
text={text}
|
{initials.map( (text, i) => <Button variant={isEnabled(found.initiales, text) ? "primary" : "outline-primary"}
|
||||||
enabled={isEnabled(enabledInitials, text)}
|
key={i} onClick={()=>toggleInitiales([text])}>
|
||||||
onclick={toggleInitialsState}/>
|
{text}
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Accordion.Body>
|
</Accordion.Body>
|
||||||
</Accordion.Item>
|
</Accordion.Item>
|
||||||
<Accordion.Item eventKey="1">
|
<Accordion.Item eventKey="1">
|
||||||
<Accordion.Header>Выберите финали</Accordion.Header>
|
<Accordion.Header>{strings.selectFinales}</Accordion.Header>
|
||||||
<Accordion.Body>
|
<Accordion.Body>
|
||||||
<Btn type="success" text="Выбрать все" altText="Снять все" onclick={toggleAllFinales}/>
|
<Button variant={found.allfinales ? "success" : "outline-success"}
|
||||||
{finales.map( (fin, i) => <BtnSimple
|
onClick= {toggleAllFinales}>
|
||||||
key={i}
|
{found.allfinales ? strings.unselectAll : strings.selectAll}
|
||||||
type="success"
|
</Button>
|
||||||
text={fin.caption}
|
{finales.map( (fin, i) => <Button variant={isEnabled(found.finales, fin.finale) ? "success" : "outline-success"}
|
||||||
enabled={isEnabled(enabledFinals, fin.finale)}
|
key={i} onClick={()=>toggleFinalsState(fin.finale)}>
|
||||||
onclick={toggleFinalsState}
|
{fin.caption}
|
||||||
search={fin.finale}/>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Accordion.Body>
|
</Accordion.Body>
|
||||||
</Accordion.Item>
|
</Accordion.Item>
|
||||||
<Accordion.Item eventKey="2">
|
<Accordion.Item eventKey="2">
|
||||||
<Accordion.Header>Параметры</Accordion.Header>
|
<Accordion.Header>{strings.params}</Accordion.Header>
|
||||||
<Accordion.Body>
|
<Accordion.Body>
|
||||||
<Form.Label>Количество слогов {count}</Form.Label>
|
<Form.Label>Количество слогов {count}</Form.Label>
|
||||||
<Form.Range value={count} min={5} max={50} step={5} onChange={onchangecount}/>
|
<Form.Range value={count} min={5} max={50} step={5} onChange={onchangecount}/>
|
||||||
|
|
@ -87,21 +112,24 @@ function App() {
|
||||||
</Accordion.Item>
|
</Accordion.Item>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
Выбрано {enabledInitials.length} инициалей, {enabledFinals.length} финалей, количество слогов {count}, пауза между слогами {pause} секунд.
|
Выбрано {found.initiales.length} инициалей, {found.finales.length} финалей, количество слогов {count}, пауза между слогами {pause} секунд.
|
||||||
<br/>
|
<br/>
|
||||||
Найдено {foundSyllables().length} слогов, { foundSyllables().reduce(
|
Найдено {foundSyllables().length} слогов, { foundSyllables().reduce(
|
||||||
( p, c ) => p + c.tones.length , 0
|
( p, c ) => p + c.tones.length , 0
|
||||||
) } тонов
|
) } тонов
|
||||||
<br/>
|
<br/>
|
||||||
<Button variant={found() ? "success" : "secondary"} size="lg" onClick={handleShow} disabled={!found()}>
|
<Button variant={isFound() ? "success" : "secondary"} size="lg" onClick={handleShow} disabled={!isFound()}>
|
||||||
{found() ? "Поехали!" : "Выберите инициали и финали" }
|
{isFound() ? "Поехали!" : "Выберите инициали и финали" }
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Modal show={show} onHide={handleClose}>
|
<Modal show={show} onHide={handleClose}>
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title>Modal heading</Modal.Title>
|
<Modal.Title>Modal heading</Modal.Title>
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Modal.Body>Woohoo, you are reading this text in a modal!</Modal.Body>
|
|
||||||
|
<Modal.Body>
|
||||||
|
{foundTones().map( (t, i) => <><Badge key={i} bg="success" pill>{t.caption}</Badge>{' '}</>)}
|
||||||
|
</Modal.Body>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<Button variant="secondary" onClick={handleClose}>
|
<Button variant="secondary" onClick={handleClose}>
|
||||||
Close
|
Close
|
||||||
|
|
|
||||||
40
src/Btn.tsx
40
src/Btn.tsx
|
|
@ -1,40 +0,0 @@
|
||||||
import { ReactElement, useState } from 'react'
|
|
||||||
import { Button } from 'react-bootstrap';
|
|
||||||
import { justproc, proc } from './Types';
|
|
||||||
|
|
||||||
interface IBtnProps {
|
|
||||||
type: String,
|
|
||||||
text: String,
|
|
||||||
altText?: String,
|
|
||||||
onclick?: justproc
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IBtnSimpleProps {
|
|
||||||
type: String,
|
|
||||||
text: String,
|
|
||||||
enabled: Boolean,
|
|
||||||
onclick?: proc,
|
|
||||||
search?: String
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Btn ({ type, text, altText, onclick }: IBtnProps): ReactElement {
|
|
||||||
const [enabled, toggleState] = useState(false)
|
|
||||||
const alttext = altText ?? text;
|
|
||||||
const onclicklocal = () => {
|
|
||||||
toggleState(!enabled)
|
|
||||||
if (onclick) onclick!()
|
|
||||||
}
|
|
||||||
return <><Button
|
|
||||||
variant={`${enabled ? "" : "outline-"}${type}`}
|
|
||||||
onClick={ onclicklocal }>{enabled ? alttext : text}
|
|
||||||
</Button>{' '}</>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BtnSimple ({ type, text, enabled, onclick, search }: IBtnSimpleProps): ReactElement {
|
|
||||||
const onclickdefault: proc = (caption: String) => {console.log(caption)}
|
|
||||||
const onclickinternal: proc = onclick ?? onclickdefault
|
|
||||||
return <>
|
|
||||||
<Button variant={`${enabled ? "" : "outline-"}${type}`} onClick={()=>onclickinternal(search ?? text)}>{text}</Button>
|
|
||||||
{' '}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import { ReactElement, useState } from 'react'
|
|
||||||
import { Btn, BtnSimple } from './Btn'
|
|
||||||
import { isEnabled, toggle } from './Utils'
|
|
||||||
|
|
||||||
function BtnSet(captions: String[], type: String): ReactElement {
|
|
||||||
|
|
||||||
const [ enabledButtons, setButtonsState ] = useState([] as String[])
|
|
||||||
const [ selectedAll, toggleSelectAll ] = useState(false)
|
|
||||||
|
|
||||||
const toggleState = (caption: String) => setButtonsState(toggle(enabledButtons,caption))
|
|
||||||
const toggleAll = () => {
|
|
||||||
toggleSelectAll( !selectedAll )
|
|
||||||
if (!selectedAll) setButtonsState([... captions])
|
|
||||||
else setButtonsState([])
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<Btn type={type} text="Выбрать все" altText="Снять все" onclick={toggleAll}/>
|
|
||||||
{captions.map( (text) => <BtnSimple
|
|
||||||
type={type}
|
|
||||||
text={text}
|
|
||||||
enabled={isEnabled(enabledButtons, text)}
|
|
||||||
onclick={toggleState}/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BtnSet
|
|
||||||
1270
src/Data.ts
1270
src/Data.ts
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const strings = {
|
||||||
|
selectInitiales: 'Выберите инициали',
|
||||||
|
selectFinales: 'Выберите финали',
|
||||||
|
selectAll: 'Выбрать все',
|
||||||
|
unselectAll: 'Снять все',
|
||||||
|
params: 'Параметры'
|
||||||
|
}
|
||||||
14
src/Types.ts
14
src/Types.ts
|
|
@ -12,3 +12,17 @@ export type Finale = {
|
||||||
caption: String,
|
caption: String,
|
||||||
finale: String
|
finale: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Tone = {
|
||||||
|
tone: String,
|
||||||
|
caption: String
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Found = {
|
||||||
|
initiales: String[],
|
||||||
|
finales: String[],
|
||||||
|
allInitiales: Boolean,
|
||||||
|
allfinales: Boolean,
|
||||||
|
syllables: Syllable[],
|
||||||
|
tones: Tone[]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue