From 79411687cb6c54ddb1fa7d25df00d579ab470a95 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 | 90 ++++++++++++++++++++++++++++ src/components/index.tsx | 1 + src/stories/Button.stories.ts | 1 + src/stories/InlineForm.stories.tsx | 94 ++++++++++++++++++++++++++++++ 4 files changed, 186 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..13c21bc2 --- /dev/null +++ b/src/components/InlineForm.tsx @@ -0,0 +1,90 @@ +import React, { useCallback, useEffect, useMemo, useReducer, useRef } from 'react'; +import styled from 'styled-components'; + +import { KeyCode, useKeyboard } from '../hooks/useKeyboard'; +import { useClickOutside } from '../hooks/useClickOutside'; + +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; + display: contents; +`; + +const StyledWrapper = styled.div` + display: contents; +} +`; + +const StyledFormWrapper = styled.div` + display: contents; + position: relative; +`; + +export const InlineForm: React.FC = ({ renderTrigger, onSubmit, onReset, children, className }) => { + const wrapperRef = useRef(null); + const [visible, toggleVisible] = useReducer((state) => !state, !renderTrigger); + const trigger = useMemo(() => { + if (typeof renderTrigger === 'function') { + return renderTrigger({ onClick: toggleVisible }); + } + + return null; + }, [renderTrigger]); + + const [onESC] = useKeyboard( + [KeyCode.Escape], + () => { + if (visible) { + toggleVisible(); + } + }, + { + capture: true, + }, + ); + + useClickOutside(wrapperRef, () => { + if (visible) { + toggleVisible(); + } + }); + + const handleSubmit = useCallback( + () => + onSubmit().then(() => { + toggleVisible(); + onReset(); + }), + [], + ); + + useEffect(() => { + if (!visible) { + onReset(); + } + }, [visible, onReset]); + + return ( + + {!visible && trigger} + {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..b843e294 --- /dev/null +++ b/src/stories/InlineForm.stories.tsx @@ -0,0 +1,94 @@ +import React, { useCallback } 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 { gray7 } from '@taskany/colors'; + +import { InlineForm } from '../components/InlineForm'; +import { Button } from '../components/Button'; +import { TableCell } from '../components/Table'; +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' } }, +} as Meta; + +const StyledWrapper = styled.div` + display: contents; + + & form { + background-color: transparent; + display: contents; + } +`; + +const StyledForm = styled(Form)` + background-color: transparent; + display: contents; +`; + +const StyledFormWrapper = styled.div` + display: contents; + position: relative; +`; + +const StyledTableCell = styled(TableCell)` + flex-wrap: nowrap; + display: flex; + align-items: center; +`; + +const StyledFormInput = styled(FormInput)` + font-size: 14px; + font-weight: normal; + padding: 5px 10px; + flex: 1; + + border: 1px solid ${gray7}; + box-sizing: border-box; +`; + +const StyledQuestionIcon = styled.div` + margin: 5px 0 0 4px; +`; + +export const InlineFormComponent: StoryFn = () => { + return ( + + + + + + +