From b49c0b44953f85dd23c930693bccf8e25cbc35f7 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 | 73 ++++++++++++++++++++++++++++++ src/components/index.tsx | 1 + src/stories/Button.stories.ts | 1 + src/stories/InlineForm.stories.tsx | 54 ++++++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 src/components/InlineForm.tsx create mode 100644 src/stories/InlineForm.stories.tsx diff --git a/src/components/InlineForm.tsx b/src/components/InlineForm.tsx new file mode 100644 index 00000000..5d0db48a --- /dev/null +++ b/src/components/InlineForm.tsx @@ -0,0 +1,73 @@ +import React, { useCallback, useEffect, useReducer, useRef } from 'react'; +import styled from 'styled-components'; + +import { KeyCode, useKeyboard } from '../hooks/useKeyboard'; +import { useClickOutside } from '../hooks/useClickOutside'; +import { nullable } from '../utils'; + +import { Form } from './Form'; + +interface RenderTriggerProps { + onClick: () => void; +} + +interface InlineFormProps { + renderTrigger: (props: RenderTriggerProps) => React.ReactNode; + onSubmit: () => Promise; + children: React.ReactNode; + onReset: () => void; + className?: string; +} + +const StyledForm = styled(Form)` + background-color: transparent; +`; + +const StyledWrapper = styled.div` + display: contents; +} +`; + +export const InlineForm: React.FC = ({ renderTrigger, onSubmit, onReset, children, className }) => { + const wrapperRef = useRef(null); + const [visible, toggleVisible] = useReducer((state) => !state, !renderTrigger); + + const [onESC] = useKeyboard( + [KeyCode.Escape], + () => { + if (visible) { + toggleVisible(); + } + }, + { + capture: true, + }, + ); + + useClickOutside(wrapperRef, () => { + if (visible) { + toggleVisible(); + } + }); + + const handleSubmit = useCallback(async () => { + await onSubmit(); + toggleVisible(); + onReset(); + }, [onSubmit, onReset]); + + useEffect(() => { + if (!visible) { + onReset(); + } + }, [visible, onReset]); + + return ( + + {nullable(!visible, () => renderTrigger({ onClick: toggleVisible }))} + {nullable(visible, () => ( + {children} + ))} + + ); +}; diff --git a/src/components/index.tsx b/src/components/index.tsx index 2ce60e8d..257ad40a 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -56,3 +56,4 @@ export * from './Spinner'; export * from './Tabs'; export * from './Checkbox'; export * from './AutoComplete'; +export * from './InlineForm'; diff --git a/src/stories/Button.stories.ts b/src/stories/Button.stories.ts index c9c09b68..04466016 100644 --- a/src/stories/Button.stories.ts +++ b/src/stories/Button.stories.ts @@ -5,6 +5,7 @@ import { Button } from '../components/Button'; const meta: Meta = { title: 'Button', component: Button, + argTypes: { onClick: { action: 'clicked' } }, }; export default meta; diff --git a/src/stories/InlineForm.stories.tsx b/src/stories/InlineForm.stories.tsx new file mode 100644 index 00000000..8795b33c --- /dev/null +++ b/src/stories/InlineForm.stories.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import { Meta, StoryFn } from '@storybook/react'; +import styled from 'styled-components'; +import { IconQuestionCircleOutline } from '@taskany/icons'; + +import { InlineForm } from '../components/InlineForm'; +import { Button } from '../components/Button'; +import { Form } from '../components/Form'; +import { Popup } from '../components/Popup'; +import { FormInput } from '../components/FormInput'; + +export default { + title: 'InlineForm', + component: InlineForm, + argTypes: { + onSubmit: { action: 'form submitted' }, + onClick: { action: 'clecked' }, + onMouseOver: { action: 'onMouseOver' }, + onMouseLeave: { action: 'onMouseLeave' }, + onChange: { action: 'onChange' }, + }, +} as Meta; + +export const InlineFormStories: StoryFn = () => { + return ( + <> +
+ + +