From b2f911f57465da088d769bfe41cae50806ab6032 Mon Sep 17 00:00:00 2001 From: Katrinputrina Date: Thu, 31 Aug 2023 11:44:28 +0300 Subject: [PATCH] feat: inlineForm component --- src/components/InlineForm.tsx | 122 ++++++++++++++++++++++++++++++++++ src/components/index.tsx | 1 + 2 files changed, 123 insertions(+) create mode 100644 src/components/InlineForm.tsx diff --git a/src/components/InlineForm.tsx b/src/components/InlineForm.tsx new file mode 100644 index 00000000..cabcfb45 --- /dev/null +++ b/src/components/InlineForm.tsx @@ -0,0 +1,122 @@ +import React, { useEffect, useMemo, useReducer, useRef, useState } from 'react'; +import styled from 'styled-components'; +import { IconQuestionCircleOutline } from '@taskany/icons'; + +import { KeyCode, useKeyboard } from '../hooks/useKeyboard'; +import { useClickOutside } from '../hooks/useClickOutside'; +import { nullable } from '../utils'; + +import { Form } from './Form'; +import { Popup } from './Popup'; + +interface RenderTriggerProps { + onClick: () => void; +} + +interface InlineFormProps { + renderTrigger?: (props: RenderTriggerProps) => React.ReactNode; + onSubmit: () => void; + children: React.ReactNode; + tip?: React.ReactNode; + onReset: () => void; + isSubmitted?: boolean; + className?: string; +} + +const StyledWrapper = styled.div` + display: contents; + + & form { + background-color: transparent; + display: contents; + } +`; + +const StyledFormWrapper = styled.div` + display: contents; + position: relative; +`; + +const StyledQuestionIcon = styled(IconQuestionCircleOutline)` + margin-left: 4px; +`; + +export const InlineForm: React.FC = ({ + renderTrigger, + onSubmit, + onReset, + isSubmitted, + children, + tip, + className, +}) => { + const wrapperRef = useRef(null); + const [visible, toggle] = useReducer((state) => !state, !renderTrigger); + const [hintVisible, toggleHintVisible] = useState(false); + const hintRef = useRef(null); + const trigger = useMemo(() => { + if (typeof renderTrigger === 'function') { + return renderTrigger({ onClick: toggle }); + } + + return null; + }, [renderTrigger]); + + const [onESC] = useKeyboard( + [KeyCode.Escape], + () => { + if (visible) { + toggle(); + } + }, + { + capture: true, + }, + ); + + useClickOutside(wrapperRef, () => { + if (visible) { + toggle(); + } + }); + + useEffect(() => { + if (isSubmitted) { + toggle(); + onReset(); + } + }, [onReset, isSubmitted]); + + useEffect(() => { + if (!visible) { + onReset(); + } + }, [visible, onReset]); + + return ( + + {!visible && trigger} + {visible && ( +
+ + {children} + {nullable(tip, (t: React.ReactNode) => ( + <> + toggleHintVisible(true)} + onMouseLeave={() => toggleHintVisible(false)} + > + + + + {t} + + + ))} + +
+ )} +
+ ); +}; diff --git a/src/components/index.tsx b/src/components/index.tsx index 80826612..69898d4f 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -52,3 +52,4 @@ export * from './UserGroup'; export * from './Popup'; export * from './ListView'; export * from './ErrorPopup'; +export * from './InlineForm';