Skip to content

Commit

Permalink
Added keyboard for transcription
Browse files Browse the repository at this point in the history
  • Loading branch information
pkolt committed Nov 10, 2024
1 parent 5144893 commit 4eec631
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 4 deletions.
21 changes: 19 additions & 2 deletions src/components/Form/FormTextField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,30 @@ import { FormField } from '@/types';
import MUITextField, { TextFieldProps as MUITextFieldProps } from '@mui/material/TextField';
import { Controller } from 'react-hook-form';

export const FormTextField: FormField<MUITextFieldProps> = ({ name, control, ...props }) => {
interface Props {
setRefInput?: (elem: HTMLInputElement | null) => void;
}

export const FormTextField: FormField<Props & MUITextFieldProps> = ({ name, control, setRefInput, ...props }) => {
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => {
return <MUITextField error={!!error} helperText={error?.message} {...props} {...field} />;
return (
<MUITextField
error={!!error}
helperText={error?.message}
{...props}
{...field}
inputRef={(elem: HTMLInputElement | null) => {
field.ref(elem);
if (setRefInput) {
setRefInput(elem);
}
}}
/>
);
}}
/>
);
Expand Down
51 changes: 51 additions & 0 deletions src/components/TranscriptionKeyboard/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export const CONSONANTS = [
'p',
'b',
't',
'd',
'k',
'g',
'f',
'v',
'θ',
'ð',
's',
'z',
'ʃ',
'ʒ',
'h',
'ʧ',
'ʤ',
'm',
'n',
'ŋ',
'w',
'r',
'l',
'j',
];

export const VOWELS = [
'ɪ',
'e',
'æ',
'ɒ',
'ʊ',
'ʌ',
'iː',
'ɑː',
'ɔː',
'uː',
'ɜː',
'eɪ',
'aɪ',
'ɔɪ',
'əʊ',
'aʊ',
'ɪə',
'eə',
'ʊə',
'ə',
'i',
'u',
];
37 changes: 37 additions & 0 deletions src/components/TranscriptionKeyboard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Chip, Stack, Grid2 as Grid, Accordion, AccordionSummary, AccordionDetails, Typography } from '@mui/material';
import { CONSONANTS, VOWELS } from './constants';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import KeyboardRoundedIcon from '@mui/icons-material/KeyboardRounded';

const TRANSCRIPTION_LIST = [...CONSONANTS, ...VOWELS];

interface Props {
onClick: (value: string) => void;
}

export const TranscriptionKeyboard = ({ onClick }: Props) => {
return (
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Stack direction="row" spacing={1}>
<KeyboardRoundedIcon />
<div>Клавиатура транскрипции</div>
</Stack>
</AccordionSummary>
<AccordionDetails>
<Grid container spacing={0.5}>
{TRANSCRIPTION_LIST.map((value, i) => (
<Grid key={i}>
<Chip
variant="outlined"
color={VOWELS.includes(value) ? 'error' : 'info'}
label={<Typography variant="h6">{value}</Typography>}
onClick={() => onClick(value)}
/>
</Grid>
))}
</Grid>
</AccordionDetails>
</Accordion>
);
};
27 changes: 25 additions & 2 deletions src/components/WordFormDialog/WordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import { WordSchema } from '@/services/words/schema';
import { Button, Stack } from '@mui/material';
import { WORD_TYPE_CHOICES } from '@/constants/form';
import { LoadingButton } from '@mui/lab';
import { useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import { FormTextField } from '@/components/Form/FormTextField';
import { FormCheckboxField } from '@/components//Form/FormCheckboxField';
import { FormSelectField } from '@/components//Form/FormSelectField';
import { mergeValues } from '@/utils/form';
import { getWordDefaultValues } from '@/services/words/utils';
import { useGetTagList } from '@/services/tags/hooks';
import { Choice } from '@/types';
import { TranscriptionKeyboard } from '../TranscriptionKeyboard';
import { useInputCursorPosition } from '@/hooks/useInputCursorPosition';

interface Props {
word?: Word;
Expand All @@ -21,6 +23,7 @@ interface Props {

export const WordForm = ({ word, onSubmit }: Props) => {
const { data: tagList } = useGetTagList();
const inputCursorPosition = useInputCursorPosition();

const tagChoices = useMemo<Choice[]>(() => {
return (tagList ?? []).map((it) => ({ label: it.name, value: it.id }));
Expand All @@ -37,13 +40,27 @@ export const WordForm = ({ word, onSubmit }: Props) => {
watch,
reset,
control,
setValue,
} = useForm<Word>({
mode: 'onChange',
resolver: zodResolver(WordSchema),
defaultValues,
});

const example = watch('example');
const transcription = watch('transcription');

const handleClickTransKey = useCallback(
(value: string) => {
const nextValue = transcription.split('').toSpliced(inputCursorPosition.position, 0, value).join('');
setValue('transcription', nextValue, {
shouldDirty: true,
shouldTouch: true,
shouldValidate: true,
});
},
[transcription, inputCursorPosition.position, setValue],
);

return (
<Stack
Expand All @@ -55,7 +72,13 @@ export const WordForm = ({ word, onSubmit }: Props) => {
<FormCheckboxField control={control} name="favorite" label="Избранное" />
<FormTextField control={control} name="word" label="Слово" autoFocus />
<FormTextField control={control} name="translate" label="Перевод" />
<FormTextField control={control} name="transcription" label="Транскрипция" />
<FormTextField
control={control}
name="transcription"
label="Транскрипция"
setRefInput={inputCursorPosition.setInputRef}
/>
<TranscriptionKeyboard onClick={handleClickTransKey} />
<FormSelectField control={control} name="types" label="Тип" choices={WORD_TYPE_CHOICES} multiple />
<FormSelectField control={control} name="tags" label="Теги" choices={tagChoices} multiple />
<FormTextField control={control} name="example" label="Пример" />
Expand Down
24 changes: 24 additions & 0 deletions src/hooks/useInputCursorPosition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEffect, useState } from 'react';

export const useInputCursorPosition = () => {
const [position, setPosition] = useState(0);
const [inputRef, setInputRef] = useState<HTMLInputElement | null>(null);

useEffect(() => {
if (!inputRef) {
return;
}

const handle = () => {
setPosition(inputRef.selectionStart ?? 0);
};

inputRef.addEventListener('focusout', handle);

return () => {
inputRef.removeEventListener('focusout', handle);
};
}, [inputRef]);

return { position, setInputRef };
};

0 comments on commit 4eec631

Please sign in to comment.