diff --git a/src/components/node/Tag/index.tsx b/src/components/node/Tag/index.tsx index 8d339ecb..fe0a6a9f 100644 --- a/src/components/node/Tag/index.tsx +++ b/src/components/node/Tag/index.tsx @@ -1,10 +1,4 @@ -import React, { - FC, - ReactElement, - ChangeEvent, - ChangeEventHandler, - KeyboardEventHandler, -} from 'react'; +import React, { FC, ChangeEventHandler, KeyboardEventHandler, FocusEventHandler } from 'react'; import * as styles from './styles.scss'; import { ITag } from '~/redux/types'; @@ -17,6 +11,7 @@ interface IProps { is_hoverable?: boolean; onInput?: ChangeEventHandler<HTMLInputElement>; onKeyUp?: KeyboardEventHandler; + onBlur?: FocusEventHandler<HTMLInputElement>; } const Tag: FC<IProps> = ({ @@ -26,6 +21,7 @@ const Tag: FC<IProps> = ({ is_hoverable, onInput, onKeyUp, + onBlur, }) => ( <div className={classNames(styles.tag, feature, { is_hoverable, input: !!onInput })}> <div className={styles.hole} /> @@ -40,6 +36,7 @@ const Tag: FC<IProps> = ({ maxLength={24} onChange={onInput} onKeyUp={onKeyUp} + onBlur={onBlur} /> )} </div> diff --git a/src/components/node/Tags/index.tsx b/src/components/node/Tags/index.tsx index 7da014eb..cf2bcdb8 100644 --- a/src/components/node/Tags/index.tsx +++ b/src/components/node/Tags/index.tsx @@ -6,11 +6,20 @@ import React, { useEffect, KeyboardEvent, ChangeEvent, + useRef, } from 'react'; import { TagField } from '~/components/containers/TagField'; import { ITag } from '~/redux/types'; import { Tag } from '~/components/node/Tag'; import uniq from 'ramda/es/uniq'; +import equals from 'ramda/es/equals'; +import { setTimeout } from 'timers'; +import identity from 'ramda/es/identity'; +import countBy from 'ramda/es/countBy'; +import eqBy from 'ramda/es/eqBy'; +import length from 'ramda/es/length'; +import isEmpty from 'ramda/es/isEmpty'; +import symmetricDifference from 'ramda/es/symmetricDifference'; type IProps = HTMLAttributes<HTMLDivElement> & { tags: ITag[]; @@ -18,15 +27,17 @@ type IProps = HTMLAttributes<HTMLDivElement> & { onTagsChange?: (tags: string[]) => void; }; -export const Tags: FC<IProps> = ({ tags, is_editable, onChange, ...props }) => { +export const Tags: FC<IProps> = ({ tags, is_editable, onTagsChange, ...props }) => { const [input, setInput] = useState(''); const [data, setData] = useState(tags); + const timer = useRef(null); const onInput = useCallback( ({ target: { value } }: ChangeEvent<HTMLInputElement>) => { setInput(value); + if (timer) clearTimeout(timer.current); }, - [setInput] + [setInput, timer] ); const onKeyUp = useCallback( @@ -55,7 +66,23 @@ export const Tags: FC<IProps> = ({ tags, is_editable, onChange, ...props }) => { [input, setInput, data, setData] ); + const onSubmit = useCallback(() => { + if (length(tags) === length(data) && isEmpty(symmetricDifference(tags, data))) return; + + onTagsChange(data.map(tag => tag.title)); + }, [tags, data, onTagsChange]); + + const onBlur = useCallback(() => { + clearTimeout(timer.current); + onSubmit(); + }, [onSubmit, timer]); + useEffect(() => setData(tags), [tags]); + useEffect(() => { + timer.current = setTimeout(onSubmit, 3000); + + return () => clearTimeout(timer.current); + }, [data]); return ( <TagField {...props}> @@ -63,7 +90,7 @@ export const Tags: FC<IProps> = ({ tags, is_editable, onChange, ...props }) => { <Tag key={tag.title} title={tag.title} feature={tag.feature} /> ))} - {is_editable && <Tag title={input} onInput={onInput} onKeyUp={onKeyUp} />} + {is_editable && <Tag title={input} onInput={onInput} onKeyUp={onKeyUp} onBlur={onBlur} />} </TagField> ); };