diff --git a/package.json b/package.json index 100fa0c..73fad4b 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "pinyindictation", - "author": {"email": "rurik19@yandex.ru", "name": "Yuriy Evdokimov"}, + "author": {"email": "rurik19@yandex.ru", "name": "Юрий Евдокимов"}, "private": true, - "version": "1.0.2", + "version": "1.1.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.tsx b/src/App.tsx index 789f056..fa065c2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,7 @@ import { RenderTones } from './rendertones'; import { getAudio } from './audio'; import { Copyright } from './copyright'; import { SelectTones } from './selecttones'; +import { FormatString } from './utils'; function App() { @@ -68,6 +69,13 @@ const playPlayList = () => { playlist[0].play() } +const info = () => FormatString(strings.infoFormat, + state.initiales!.length.toString(), + state.finales!.length.toString(), + state.toneChecks!.length.toString(), + state.foundSyllables!.length.toString(), + state.foundTones!.length.toString()) + return ( <>

{strings.mainHeader}

@@ -96,14 +104,14 @@ return ( { status == Status.params && <> - Выбрано {state.initiales!.length} инициалей, {state.finales!.length} финалей, найдено {state.foundSyllables!.length} слогов, { state.foundTones!.length } тонов , + {info()}
} diff --git a/src/Strings.ts b/src/Strings.ts index c7972f1..fc94967 100644 --- a/src/Strings.ts +++ b/src/Strings.ts @@ -5,13 +5,21 @@ export const strings = { selectAll: 'Выбрать все', unselectAll: 'Снять все', params: 'Параметры', - sylCount: 'Количество слогов', + sylCount: 'Количество слогов: ', beginDictation: "Начать диктант!", - selectInitAndFin: "Выберите инициали и финали", + selectInitFinAndTone: "Выберите инициали, финали и тоны", showSyllables: "Показать слоги", playAgain: "Повторить", toBegin: "В начало", version: "Версия", cpr: "©", - selectTones: "Выберите тоны" + selectTones: "Выберите тоны", + first: "Первый", + second: "Второй", + third: "Третий", + fourth: "Четвёртый", + zeroth: "Нулевой", + infoFormat: "Выбрано {0} инициалей, {1} финалей, {2} тонов, найдено {3} слогов, {4} тонов", + pauseFormat: "Пауза между слогами, в секундах: {0}", + mail: "емайл:" } \ No newline at end of file diff --git a/src/Utils.ts b/src/Utils.ts index 17cda21..94818c0 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -3,13 +3,36 @@ import { BtnColor, SylPart, Syllable } from "./types" export const isEnabled = (arr: SylPart[], find: string): boolean => arr.some((str) => str.index==find) -export const toggle = ( arr: SylPart[], part: SylPart ):SylPart[] => { +export const toggleSylPart = ( arr: SylPart[], part: SylPart ):SylPart[] => { if ( arr.some((btn) => btn.index==part.index) ) return arr.filter((el)=>el.index!==part.index) else return [ ...arr, part] } +type TComparer = (index: any) => (el: any) => boolean +const SylPartComparer:TComparer = (index: string) => (el: SylPart) => el.index === index +const SylPartFilter:TComparer = (index: SylPart) => (el: SylPart) => el.index !== index.index + +const defToggleComparer:TComparer = (index: any) => (el: any) => el === index +const defToggleFilter:TComparer = (index: any) => (el: any) => el !== index + +export const toggleSylPartG = ( arr: SylPart[], part: SylPart ):SylPart[] => + toggle(arr, part, SylPartComparer, SylPartFilter) + +export const included = ( arr: T[], part: T, comparer:TComparer = defToggleComparer) => + arr.some( comparer(part) ) + +const exclude = ( arr: T[], part: T, + filter:TComparer = defToggleFilter) => arr.filter( filter(part) ) + +const include = ( arr: T[], part: T):T[] => [ ...arr, part] + +export const toggle = ( arr: T[], part: T, + comparer:TComparer = defToggleComparer, + filter:TComparer = defToggleFilter):T[] => + included(arr,part, comparer) ? exclude(arr,part, filter ) : include(arr, part) + export const genrateRandomNumber = (min: number, max: number):number => { min = Math.ceil(min) max = Math.floor(max) @@ -36,3 +59,10 @@ export const GetSyllablesByInitAndFin = (initiales:SylPart[], finales: SylPart[] } export const Outlined = (color: BtnColor):string => `outline-${color}` + +export function FormatString(str: string, ...val: string[]) { + for (let index = 0; index < val.length; index++) { + str = str.replace(`{${index}}`, val[index]); + } + return str; +} \ No newline at end of file diff --git a/src/copyright.tsx b/src/copyright.tsx index 42a8ec7..c5cb4e9 100644 --- a/src/copyright.tsx +++ b/src/copyright.tsx @@ -2,4 +2,5 @@ import { ReactElement } from "react"; import { strings } from "./strings"; import { author } from '../package.json' -export const Copyright = ():ReactElement =>
{strings.version} {import.meta.env.VITE_REACT_APP_VERSION} {strings.cpr}{author.name}
\ No newline at end of file +export const Copyright = ():ReactElement => +
{strings.version} {import.meta.env.VITE_REACT_APP_VERSION} {strings.cpr}{author.name} {strings.mail}{author.email}
\ No newline at end of file diff --git a/src/params.tsx b/src/params.tsx index 2ab7c22..71edab4 100644 --- a/src/params.tsx +++ b/src/params.tsx @@ -3,6 +3,7 @@ import { useStateContext } from "./store"; import { Form } from "react-bootstrap"; import { ActionType } from "./reducer"; import { strings } from "./strings"; +import { FormatString } from "./utils"; export const Params = (): ReactElement => { @@ -16,7 +17,7 @@ export const Params = (): ReactElement => { return <> {strings.sylCount} {state.sylCount} - Пауза между слогами {state.sylPause} секунд + { FormatString( strings.pauseFormat , state.sylPause!.toString() )} diff --git a/src/reducer.ts b/src/reducer.ts index 53e691a..8f5a0ec 100644 --- a/src/reducer.ts +++ b/src/reducer.ts @@ -1,10 +1,10 @@ import { finales, initials, tones } from "./pinyin"; import { IState } from "./store"; import { SylPart, Syllable, Tone } from "./types"; -import { GetSyllablesByInitAndFin, getRandomArray, toggle } from "./utils"; +import { GetSyllablesByInitAndFin, getRandomArray, toggleSylPart, toggle, included } from "./utils"; export enum ActionType { - toggleOne, toggleAll, refresh, setPause, setCount, setStatus, playing + toggleOne, toggleAll, refresh, setPause, setCount, setStatus, playing, toneSelect } export enum ToggleType { init, fin } @@ -12,28 +12,30 @@ export type TogglePayload = { type: ToggleType, part: SylPart } export type Action = { type: ActionType, payload?: number | ToggleType | TogglePayload }; -interface IFounds { foundSyllables:Syllable[], foundTones: Tone[], randomTones: Tone[] } +interface IFounds { + foundSyllables:Syllable[], + foundTones: Tone[], + randomTones: Tone[] +} -interface IInitResult { +interface IInitResult extends IFounds { allInitiales?:boolean, - initiales: SylPart[], - foundSyllables:Syllable[], - foundTones: Tone[], - randomTones: Tone[] + initiales: SylPart[] } -interface IFinResult { +interface IFinResult extends IFounds { allfinales?:boolean, - finales: SylPart[], - foundSyllables:Syllable[], - foundTones: Tone[], - randomTones: Tone[] + finales: SylPart[] } -const proceedFounds = ( initiales: SylPart[], finales:SylPart[], count: number):IFounds=> +interface IToneSelectResult extends IFounds { + toneChecks: number[] +} + +const proceedFounds = ( initiales: SylPart[], finales:SylPart[], tonesCheck:number[], count: number):IFounds=> { const foundSyllables:Syllable[] = GetSyllablesByInitAndFin( initiales, finales ) - const foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) ) + const foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone && included(tonesCheck, t.num) ) ) ) const randomTones = getRandomArray(foundTones, count) return { foundSyllables, foundTones, randomTones } } @@ -44,7 +46,7 @@ const ProceedAllInitials = (state: IState):IInitResult => return { allInitiales: !state.allInitiales, initiales: toggled, - ...proceedFounds(toggled, state.finales, state.sylCount) + ...proceedFounds(toggled, state.finales, state.toneChecks, state.sylCount) } } @@ -54,25 +56,32 @@ const ProceedAllFinales = (state: IState):IFinResult => return { allfinales: !state.allfinales, finales: toggled, - ...proceedFounds(state.initiales, toggled, state.sylCount) + ...proceedFounds(state.initiales, toggled, state.toneChecks, state.sylCount) } } const ProceedInitiale = (state: IState, index: SylPart):IInitResult => { - const toggled = toggle(state.initiales,index) + const toggled = toggleSylPart(state.initiales,index) return { initiales: toggled, - ...proceedFounds(toggled, state.finales, state.sylCount) + ...proceedFounds(toggled, state.finales, state.toneChecks, state.sylCount) } } const ProceedFinale = (state: IState, index: SylPart):IFinResult => { - const toggled = toggle(state.finales,index) + const toggled = toggleSylPart(state.finales,index) return { finales: toggled, - ...proceedFounds(state.initiales, toggled, state.sylCount) + ...proceedFounds(state.initiales, toggled, state.toneChecks, state.sylCount) + } +} + +const ProceedToneSelect = (state: IState, index: number):IToneSelectResult => { + const toggled = toggle(state.toneChecks, index) + return { toneChecks: toggled, + ...proceedFounds(state.initiales, state.finales, toggled, state.sylCount) } } @@ -95,6 +104,8 @@ export const reducer = (state:IState, action:Action):IState => { return { ...state, ...ProceedFinale(state, (action.payload as TogglePayload).part ) } return state } + case ActionType.toneSelect: return { ...state, ...ProceedToneSelect(state, (action.payload as number) ) } + default: return state } } \ No newline at end of file diff --git a/src/selecttones.tsx b/src/selecttones.tsx index 016d2eb..a5b5781 100644 --- a/src/selecttones.tsx +++ b/src/selecttones.tsx @@ -1,34 +1,53 @@ import { useStateContext } from "./store" import { Form } from "react-bootstrap"; +import { strings } from "./strings"; +import { ActionType } from "./reducer"; export const SelectTones = () => { - // const { state, dispatch } = useStateContext() + const { state, dispatch } = useStateContext() + const onToneChange = ( toneNo: number ) => + dispatch({ type:ActionType.toneSelect, payload: toneNo }) return <>
onToneChange(1) } /> onToneChange(2) } /> -
onToneChange(3) } /> onToneChange(4) } + /> + onToneChange(5) } /> diff --git a/src/store.ts b/src/store.ts index c45a408..6e4a8af 100644 --- a/src/store.ts +++ b/src/store.ts @@ -13,9 +13,11 @@ export interface IState { foundSyllables: Syllable[], foundTones: Tone[], randomTones: Tone[], + toneChecks: number[], allEnabled: (type: ToggleType) => false, isEnabled: (type: ToggleType, index: string) => false, - isFound: () => false + isFound: () => false, + isToneChecked: (toneNo: number) => false } export interface IStore { @@ -33,6 +35,7 @@ export const defaultState:object = { foundSyllables: [], foundTones: [], randomTones: [], + toneChecks: [ 1, 2, 3, 4, 5 ], allEnabled: function(type: ToggleType) { if ( type === ToggleType.init ) return (this as IState).allInitiales if ( type === ToggleType.fin ) return (this as IState).allfinales @@ -43,7 +46,9 @@ export const defaultState:object = { if ( type === ToggleType.fin ) return isEnabled((this as IState).finales!, index) return false }, - isFound: function():boolean { return (this as IState).foundSyllables!.length > 0 } + isFound: function():boolean { return (this as IState).foundSyllables!.length > 0 && (this as IState).toneChecks!.length > 0} + , + isToneChecked: function(toneNo: number):boolean{ return (this as IState).toneChecks.includes(toneNo) } } export const AppContext:Context = createContext({ state: defaultState as IState, dispatch: () => null })