From 95e49eafc1060eaaf1623d66271c8d7440d1e75b Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Thu, 14 Aug 2025 12:32:42 +0400 Subject: [PATCH] refactor: in place input division --- .../ui/InPlaceInput/InPlaceInput.tsx | 90 ++----------------- .../ui/InPlaceInput/InPlaceInputDesktop.tsx | 84 +++++++++++++++++ .../ui/InPlaceInput/InPlaceInputMobile.tsx | 32 +++++++ 3 files changed, 123 insertions(+), 83 deletions(-) create mode 100644 src/components/ui/InPlaceInput/InPlaceInputDesktop.tsx create mode 100644 src/components/ui/InPlaceInput/InPlaceInputMobile.tsx diff --git a/src/components/ui/InPlaceInput/InPlaceInput.tsx b/src/components/ui/InPlaceInput/InPlaceInput.tsx index d3abcf6..5684399 100644 --- a/src/components/ui/InPlaceInput/InPlaceInput.tsx +++ b/src/components/ui/InPlaceInput/InPlaceInput.tsx @@ -1,8 +1,8 @@ -import React, { FC, ReactNode, useEffect, useRef, useState } from "react"; -import { TextInput } from "@mantine/core"; +import React, { FC, ReactNode } from "react"; import { Styles } from "@mantine/core/lib/core/styles-api/styles-api.types"; -import { modals } from "@mantine/modals"; import useIsMobile from "@/hooks/useIsMobile"; +import InPlaceInputDesktop from "./InPlaceInputDesktop"; +import InPlaceInputMobile from "./InPlaceInputMobile"; type Props = { defaultValue?: string; @@ -13,90 +13,14 @@ type Props = { modalTitle?: string; }; -const InPlaceInput: FC = ({ - onComplete, - placeholder, - inputStyles, - getChildren, - modalTitle = "", - defaultValue = "", -}) => { - const [isWriting, setIsWriting] = useState(false); - const [value, setValue] = useState(defaultValue); - const inputRef = useRef(null); +const InPlaceInput: FC = (props) => { const isMobile = useIsMobile(); - useEffect(() => { - if (isWriting && inputRef.current) { - inputRef.current.focus(); - } - }, [isWriting]); - - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if ( - isWriting && - inputRef.current && - !inputRef.current.contains(event.target as Node) - ) { - onCompleteCreating(); - } - }; - - document.addEventListener("mousedown", handleClickOutside); - return () => - document.removeEventListener("mousedown", handleClickOutside); - }, [isWriting, value]); - - const onStartCreating = () => { - if (!isMobile) { - setValue(defaultValue); - setIsWriting(true); - return; - } - - modals.openContextModal({ - modal: "enterNameModal", - title: modalTitle, - withCloseButton: true, - innerProps: { - onComplete, - defaultValue, - }, - }); - }; - - const onCancelCreating = () => { - setValue(defaultValue); - setIsWriting(false); - }; - - const onCompleteCreating = () => { - const localValue = value.trim(); - if (localValue) { - onComplete(localValue); - } - setIsWriting(false); - }; - - if (isWriting) { - return ( - setValue(e.target.value)} - onKeyDown={e => { - if (e.key === "Enter") onCompleteCreating(); - if (e.key === "Escape") onCancelCreating(); - }} - styles={inputStyles} - /> - ); + if (isMobile) { + return ; } - return getChildren(onStartCreating); + return ; }; export default InPlaceInput; diff --git a/src/components/ui/InPlaceInput/InPlaceInputDesktop.tsx b/src/components/ui/InPlaceInput/InPlaceInputDesktop.tsx new file mode 100644 index 0000000..560e4f1 --- /dev/null +++ b/src/components/ui/InPlaceInput/InPlaceInputDesktop.tsx @@ -0,0 +1,84 @@ +import React, { FC, ReactNode, useEffect, useRef, useState } from "react"; +import { TextInput } from "@mantine/core"; +import { Styles } from "@mantine/core/lib/core/styles-api/styles-api.types"; + +type Props = { + defaultValue?: string; + onComplete: (value: string) => void; + placeholder?: string; + getChildren: (startEditing: () => void) => ReactNode; + inputStyles?: Styles; +}; + +const InPlaceInputDesktop: FC = ({ + onComplete, + placeholder, + inputStyles, + getChildren, + defaultValue = "", +}) => { + const [isWriting, setIsWriting] = useState(false); + const [value, setValue] = useState(defaultValue); + const inputRef = useRef(null); + + useEffect(() => { + if (isWriting && inputRef.current) { + inputRef.current.focus(); + } + }, [isWriting]); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + isWriting && + inputRef.current && + !inputRef.current.contains(event.target as Node) + ) { + onCompleteCreating(); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => + document.removeEventListener("mousedown", handleClickOutside); + }, [isWriting, value]); + + const onStartCreating = () => { + setValue(defaultValue); + setIsWriting(true); + }; + + const onCancelCreating = () => { + setValue(defaultValue); + setIsWriting(false); + }; + + const onCompleteCreating = () => { + const localValue = value.trim(); + if (localValue) { + onComplete(localValue); + } + setIsWriting(false); + }; + + if (isWriting) { + return ( + setValue(e.target.value)} + onKeyDown={e => { + if (e.key === "Enter") onCompleteCreating(); + if (e.key === "Escape") onCancelCreating(); + }} + styles={inputStyles} + /> + ); + } + + return getChildren(onStartCreating); +}; + +export default InPlaceInputDesktop; diff --git a/src/components/ui/InPlaceInput/InPlaceInputMobile.tsx b/src/components/ui/InPlaceInput/InPlaceInputMobile.tsx new file mode 100644 index 0000000..109de3f --- /dev/null +++ b/src/components/ui/InPlaceInput/InPlaceInputMobile.tsx @@ -0,0 +1,32 @@ +import { FC, ReactNode } from "react"; +import { modals } from "@mantine/modals"; + +type Props = { + defaultValue?: string; + onComplete: (value: string) => void; + getChildren: (startEditing: () => void) => ReactNode; + modalTitle?: string; +}; + +const InPlaceInputMobile: FC = ({ + onComplete, + getChildren, + modalTitle = "", + defaultValue = "", +}) => { + const onStartCreating = () => { + modals.openContextModal({ + modal: "enterNameModal", + title: modalTitle, + withCloseButton: true, + innerProps: { + onComplete, + defaultValue, + }, + }); + }; + + return getChildren(onStartCreating); +}; + +export default InPlaceInputMobile;