From 4eec6316d8ce6039998ad7a082e0ad8b1c633d51 Mon Sep 17 00:00:00 2001 From: pkolt Date: Sun, 10 Nov 2024 15:14:19 +0400 Subject: [PATCH] Added keyboard for transcription --- src/components/Form/FormTextField/index.tsx | 21 +++++++- .../TranscriptionKeyboard/constants.ts | 51 +++++++++++++++++++ .../TranscriptionKeyboard/index.tsx | 37 ++++++++++++++ src/components/WordFormDialog/WordForm.tsx | 27 +++++++++- src/hooks/useInputCursorPosition.ts | 24 +++++++++ 5 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 src/components/TranscriptionKeyboard/constants.ts create mode 100644 src/components/TranscriptionKeyboard/index.tsx create mode 100644 src/hooks/useInputCursorPosition.ts diff --git a/src/components/Form/FormTextField/index.tsx b/src/components/Form/FormTextField/index.tsx index e71ccc2..422f178 100644 --- a/src/components/Form/FormTextField/index.tsx +++ b/src/components/Form/FormTextField/index.tsx @@ -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 = ({ name, control, ...props }) => { +interface Props { + setRefInput?: (elem: HTMLInputElement | null) => void; +} + +export const FormTextField: FormField = ({ name, control, setRefInput, ...props }) => { return ( { - return ; + return ( + { + field.ref(elem); + if (setRefInput) { + setRefInput(elem); + } + }} + /> + ); }} /> ); diff --git a/src/components/TranscriptionKeyboard/constants.ts b/src/components/TranscriptionKeyboard/constants.ts new file mode 100644 index 0000000..9cfb72b --- /dev/null +++ b/src/components/TranscriptionKeyboard/constants.ts @@ -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', +]; diff --git a/src/components/TranscriptionKeyboard/index.tsx b/src/components/TranscriptionKeyboard/index.tsx new file mode 100644 index 0000000..93f7f7e --- /dev/null +++ b/src/components/TranscriptionKeyboard/index.tsx @@ -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 ( + + }> + + +
Клавиатура транскрипции
+
+
+ + + {TRANSCRIPTION_LIST.map((value, i) => ( + + {value}} + onClick={() => onClick(value)} + /> + + ))} + + +
+ ); +}; diff --git a/src/components/WordFormDialog/WordForm.tsx b/src/components/WordFormDialog/WordForm.tsx index d001dac..b7bd2ac 100644 --- a/src/components/WordFormDialog/WordForm.tsx +++ b/src/components/WordFormDialog/WordForm.tsx @@ -5,7 +5,7 @@ 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'; @@ -13,6 +13,8 @@ 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; @@ -21,6 +23,7 @@ interface Props { export const WordForm = ({ word, onSubmit }: Props) => { const { data: tagList } = useGetTagList(); + const inputCursorPosition = useInputCursorPosition(); const tagChoices = useMemo(() => { return (tagList ?? []).map((it) => ({ label: it.name, value: it.id })); @@ -37,6 +40,7 @@ export const WordForm = ({ word, onSubmit }: Props) => { watch, reset, control, + setValue, } = useForm({ mode: 'onChange', resolver: zodResolver(WordSchema), @@ -44,6 +48,19 @@ export const WordForm = ({ word, onSubmit }: Props) => { }); 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 ( { - + + diff --git a/src/hooks/useInputCursorPosition.ts b/src/hooks/useInputCursorPosition.ts new file mode 100644 index 0000000..6f98272 --- /dev/null +++ b/src/hooks/useInputCursorPosition.ts @@ -0,0 +1,24 @@ +import { useEffect, useState } from 'react'; + +export const useInputCursorPosition = () => { + const [position, setPosition] = useState(0); + const [inputRef, setInputRef] = useState(null); + + useEffect(() => { + if (!inputRef) { + return; + } + + const handle = () => { + setPosition(inputRef.selectionStart ?? 0); + }; + + inputRef.addEventListener('focusout', handle); + + return () => { + inputRef.removeEventListener('focusout', handle); + }; + }, [inputRef]); + + return { position, setInputRef }; +};