1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-18 20:59:44 +02:00

ref: Creating popups with hook, fix translation keys passing

This commit is contained in:
Maksim Eltyshev 2023-01-24 18:53:13 +01:00
parent 6fd42e3b62
commit d975b2d07a
69 changed files with 309 additions and 332 deletions

View file

@ -1,11 +1,12 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { usePopup } from '../../lib/popup';
import User from '../User'; import User from '../User';
import Label from '../Label'; import Label from '../Label';
import BoardMembershipsPopup from '../BoardMembershipsPopup'; import BoardMembershipsStep from '../BoardMembershipsStep';
import LabelsPopup from '../LabelsPopup'; import LabelsStep from '../LabelsStep';
import styles from './Filters.module.scss'; import styles from './Filters.module.scss';
@ -41,15 +42,16 @@ const Filters = React.memo(
[onLabelRemove], [onLabelRemove],
); );
const BoardMembershipsPopup = usePopup(BoardMembershipsStep);
const LabelsPopup = usePopup(LabelsStep);
return ( return (
<> <>
<span className={styles.filter}> <span className={styles.filter}>
<BoardMembershipsPopup <BoardMembershipsPopup
items={allBoardMemberships} items={allBoardMemberships}
currentUserIds={users.map((user) => user.id)} currentUserIds={users.map((user) => user.id)}
title={t('common.filterByMembers', { title="common.filterByMembers"
context: 'title',
})}
onUserSelect={onUserAdd} onUserSelect={onUserAdd}
onUserDeselect={onUserRemove} onUserDeselect={onUserRemove}
> >
@ -73,9 +75,7 @@ const Filters = React.memo(
<LabelsPopup <LabelsPopup
items={allLabels} items={allLabels}
currentIds={labels.map((label) => label.id)} currentIds={labels.map((label) => label.id)}
title={t('common.filterByLabels', { title="common.filterByLabels"
context: 'title',
})}
canEdit={canEdit} canEdit={canEdit}
onSelect={onLabelAdd} onSelect={onLabelAdd}
onDeselect={onLabelRemove} onDeselect={onLabelRemove}

View file

@ -1,5 +0,0 @@
import { withPopup } from '../lib/popup';
import BoardMembershipsStep from './BoardMembershipsStep';
export default withPopup(BoardMembershipsStep);

View file

@ -1,3 +0,0 @@
import AddPopup from './AddPopup';
export default AddPopup;

View file

@ -3,13 +3,12 @@ import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Form, Icon } from 'semantic-ui-react'; import { Button, Form, Icon } from 'semantic-ui-react';
import { useDidUpdate, useToggle } from '../../../lib/hooks'; import { useDidUpdate, useToggle } from '../../../lib/hooks';
import { withPopup } from '../../../lib/popup';
import { Input, Popup } from '../../../lib/custom-ui'; import { Input, Popup } from '../../../lib/custom-ui';
import { useForm, useSteps } from '../../../hooks'; import { useForm, useSteps } from '../../../hooks';
import ImportStep from './ImportStep'; import ImportStep from './ImportStep';
import styles from './AddPopup.module.scss'; import styles from './AddStep.module.scss';
const StepTypes = { const StepTypes = {
IMPORT: 'IMPORT', IMPORT: 'IMPORT',
@ -114,4 +113,4 @@ AddStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(AddStep); export default AddStep;

View file

@ -0,0 +1,3 @@
import AddStep from './AddStep';
export default AddStep;

View file

@ -5,12 +5,12 @@ import classNames from 'classnames';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
import { Button, Icon } from 'semantic-ui-react'; import { Button, Icon } from 'semantic-ui-react';
import { closePopup } from '../../lib/popup'; import { closePopup, usePopup } from '../../lib/popup';
import Paths from '../../constants/Paths'; import Paths from '../../constants/Paths';
import DroppableTypes from '../../constants/DroppableTypes'; import DroppableTypes from '../../constants/DroppableTypes';
import AddPopup from './AddPopup'; import AddStep from './AddStep';
import EditPopup from './EditPopup'; import EditStep from './EditStep';
import styles from './Boards.module.scss'; import styles from './Boards.module.scss';
@ -52,6 +52,9 @@ const Boards = React.memo(({ items, currentId, canEdit, onCreate, onUpdate, onMo
[onDelete], [onDelete],
); );
const AddPopup = usePopup(AddStep);
const EditPopup = usePopup(EditStep);
const itemsNode = items.map((item, index) => ( const itemsNode = items.map((item, index) => (
<Draggable <Draggable
key={item.id} key={item.id}

View file

@ -3,13 +3,12 @@ import React, { useCallback, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Form } from 'semantic-ui-react'; import { Button, Form } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { Input, Popup } from '../../lib/custom-ui'; import { Input, Popup } from '../../lib/custom-ui';
import { useForm, useSteps } from '../../hooks'; import { useForm, useSteps } from '../../hooks';
import DeleteStep from '../DeleteStep'; import DeleteStep from '../DeleteStep';
import styles from './EditPopup.module.scss'; import styles from './EditStep.module.scss';
const StepTypes = { const StepTypes = {
DELETE: 'DELETE', DELETE: 'DELETE',
@ -56,11 +55,9 @@ const EditStep = React.memo(({ defaultData, onUpdate, onDelete, onClose }) => {
if (step && step.type === StepTypes.DELETE) { if (step && step.type === StepTypes.DELETE) {
return ( return (
<DeleteStep <DeleteStep
title={t('common.deleteBoard', { title="common.deleteBoard"
context: 'title', content="common.areYouSureYouWantToDeleteThisBoard"
})} buttonContent="action.deleteBoard"
content={t('common.areYouSureYouWantToDeleteThisBoard')}
buttonContent={t('action.deleteBoard')}
onConfirm={onDelete} onConfirm={onDelete}
onBack={handleBack} onBack={handleBack}
/> />
@ -104,4 +101,4 @@ EditStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(EditStep); export default EditStep;

View file

@ -3,7 +3,6 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Menu } from 'semantic-ui-react'; import { Menu } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { Popup } from '../../lib/custom-ui'; import { Popup } from '../../lib/custom-ui';
import { useSteps } from '../../hooks'; import { useSteps } from '../../hooks';
@ -14,7 +13,7 @@ import TimerEditStep from '../TimerEditStep';
import CardMoveStep from '../CardMoveStep'; import CardMoveStep from '../CardMoveStep';
import DeleteStep from '../DeleteStep'; import DeleteStep from '../DeleteStep';
import styles from './ActionsPopup.module.scss'; import styles from './ActionsStep.module.scss';
const StepTypes = { const StepTypes = {
USERS: 'USERS', USERS: 'USERS',
@ -158,11 +157,9 @@ const ActionsStep = React.memo(
case StepTypes.DELETE: case StepTypes.DELETE:
return ( return (
<DeleteStep <DeleteStep
title={t('common.deleteCard', { title="common.deleteCard"
context: 'title', content="common.areYouSureYouWantToDeleteThisCard"
})} buttonContent="action.deleteCard"
content={t('common.areYouSureYouWantToDeleteThisCard')}
buttonContent={t('action.deleteCard')}
onConfirm={onDelete} onConfirm={onDelete}
onBack={handleBack} onBack={handleBack}
/> />
@ -248,4 +245,4 @@ ActionsStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(ActionsStep); export default ActionsStep;

View file

@ -4,12 +4,13 @@ import classNames from 'classnames';
import { Button, Icon } from 'semantic-ui-react'; import { Button, Icon } from 'semantic-ui-react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Draggable } from 'react-beautiful-dnd'; import { Draggable } from 'react-beautiful-dnd';
import { usePopup } from '../../lib/popup';
import { startTimer, stopTimer } from '../../utils/timer'; import { startTimer, stopTimer } from '../../utils/timer';
import Paths from '../../constants/Paths'; import Paths from '../../constants/Paths';
import Tasks from './Tasks'; import Tasks from './Tasks';
import NameEdit from './NameEdit'; import NameEdit from './NameEdit';
import ActionsPopup from './ActionsPopup'; import ActionsStep from './ActionsStep';
import User from '../User'; import User from '../User';
import Label from '../Label'; import Label from '../Label';
import DueDate from '../DueDate'; import DueDate from '../DueDate';
@ -83,6 +84,8 @@ const Card = React.memo(
nameEdit.current.open(); nameEdit.current.open();
}, []); }, []);
const ActionsPopup = usePopup(ActionsStep);
const contentNode = ( const contentNode = (
<> <>
{coverUrl && <img src={coverUrl} alt="" className={styles.cover} />} {coverUrl && <img src={coverUrl} alt="" className={styles.cover} />}

View file

@ -3,11 +3,12 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Comment } from 'semantic-ui-react'; import { Comment } from 'semantic-ui-react';
import { usePopup } from '../../../lib/popup';
import { Markdown } from '../../../lib/custom-ui'; import { Markdown } from '../../../lib/custom-ui';
import CommentEdit from './CommentEdit'; import CommentEdit from './CommentEdit';
import User from '../../User'; import User from '../../User';
import DeletePopup from '../../DeletePopup'; import DeleteStep from '../../DeleteStep';
import styles from './ItemComment.module.scss'; import styles from './ItemComment.module.scss';
@ -21,6 +22,8 @@ const ItemComment = React.memo(
commentEdit.current.open(); commentEdit.current.open();
}, []); }, []);
const DeletePopup = usePopup(DeleteStep);
return ( return (
<Comment> <Comment>
<span className={styles.user}> <span className={styles.user}>
@ -50,11 +53,9 @@ const ItemComment = React.memo(
onClick={handleEditClick} onClick={handleEditClick}
/> />
<DeletePopup <DeletePopup
title={t('common.deleteComment', { title="common.deleteComment"
context: 'title', content="common.areYouSureYouWantToDeleteThisComment"
})} buttonContent="action.deleteComment"
content={t('common.areYouSureYouWantToDeleteThisComment')}
buttonContent={t('action.deleteComment')}
onConfirm={onDelete} onConfirm={onDelete}
> >
<Comment.Action <Comment.Action

View file

@ -2,10 +2,9 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Menu } from 'semantic-ui-react'; import { Menu } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { FilePicker, Popup } from '../../lib/custom-ui'; import { FilePicker, Popup } from '../../lib/custom-ui';
import styles from './AttachmentAddPopup.module.scss'; import styles from './AttachmentAddStep.module.scss';
const AttachmentAddStep = React.memo(({ onCreate, onClose }) => { const AttachmentAddStep = React.memo(({ onCreate, onClose }) => {
const [t] = useTranslation(); const [t] = useTranslation();
@ -51,4 +50,4 @@ AttachmentAddStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(AttachmentAddStep); export default AttachmentAddStep;

View file

@ -3,13 +3,12 @@ import React, { useCallback, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Form } from 'semantic-ui-react'; import { Button, Form } from 'semantic-ui-react';
import { withPopup } from '../../../lib/popup';
import { Input, Popup } from '../../../lib/custom-ui'; import { Input, Popup } from '../../../lib/custom-ui';
import { useForm, useSteps } from '../../../hooks'; import { useForm, useSteps } from '../../../hooks';
import DeleteStep from '../../DeleteStep'; import DeleteStep from '../../DeleteStep';
import styles from './EditPopup.module.scss'; import styles from './EditStep.module.scss';
const StepTypes = { const StepTypes = {
DELETE: 'DELETE', DELETE: 'DELETE',
@ -56,11 +55,9 @@ const EditStep = React.memo(({ defaultData, onUpdate, onDelete, onClose }) => {
if (step && step.type === StepTypes.DELETE) { if (step && step.type === StepTypes.DELETE) {
return ( return (
<DeleteStep <DeleteStep
title={t('common.deleteAttachment', { title="common.deleteAttachment"
context: 'title', content="common.areYouSureYouWantToDeleteThisAttachment"
})} buttonContent="action.deleteAttachment"
content={t('common.areYouSureYouWantToDeleteThisAttachment')}
buttonContent={t('action.deleteAttachment')}
onConfirm={onDelete} onConfirm={onDelete}
onBack={handleBack} onBack={handleBack}
/> />
@ -104,4 +101,4 @@ EditStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(EditStep); export default EditStep;

View file

@ -3,8 +3,9 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Icon, Label, Loader } from 'semantic-ui-react'; import { Button, Icon, Label, Loader } from 'semantic-ui-react';
import { usePopup } from '../../../lib/popup';
import EditPopup from './EditPopup'; import EditStep from './EditStep';
import styles from './Item.module.scss'; import styles from './Item.module.scss';
@ -49,6 +50,8 @@ const Item = React.forwardRef(
[isCover, onCoverSelect, onCoverDeselect], [isCover, onCoverSelect, onCoverDeselect],
); );
const EditPopup = usePopup(EditStep);
if (!isPersisted) { if (!isPersisted) {
return ( return (
<div className={classNames(styles.wrapper, styles.wrapperSubmitting)}> <div className={classNames(styles.wrapper, styles.wrapperSubmitting)}>

View file

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Grid, Icon, Modal } from 'semantic-ui-react'; import { Button, Grid, Icon, Modal } from 'semantic-ui-react';
import { usePopup } from '../../lib/popup';
import { Markdown } from '../../lib/custom-ui'; import { Markdown } from '../../lib/custom-ui';
import { startTimer, stopTimer } from '../../utils/timer'; import { startTimer, stopTimer } from '../../utils/timer';
@ -11,18 +12,18 @@ import DescriptionEdit from './DescriptionEdit';
import Tasks from './Tasks'; import Tasks from './Tasks';
import Attachments from './Attachments'; import Attachments from './Attachments';
import AttachmentAddZone from './AttachmentAddZone'; import AttachmentAddZone from './AttachmentAddZone';
import AttachmentAddPopup from './AttachmentAddPopup'; import AttachmentAddStep from './AttachmentAddStep';
import Activities from './Activities'; import Activities from './Activities';
import User from '../User'; import User from '../User';
import Label from '../Label'; import Label from '../Label';
import DueDate from '../DueDate'; import DueDate from '../DueDate';
import Timer from '../Timer'; import Timer from '../Timer';
import BoardMembershipsPopup from '../BoardMembershipsPopup'; import BoardMembershipsStep from '../BoardMembershipsStep';
import LabelsPopup from '../LabelsPopup'; import LabelsStep from '../LabelsStep';
import DueDateEditPopup from '../DueDateEditPopup'; import DueDateEditStep from '../DueDateEditStep';
import TimerEditPopup from '../TimerEditPopup'; import TimerEditStep from '../TimerEditStep';
import CardMovePopup from '../CardMovePopup'; import CardMoveStep from '../CardMoveStep';
import DeletePopup from '../DeletePopup'; import DeleteStep from '../DeleteStep';
import styles from './CardModal.module.scss'; import styles from './CardModal.module.scss';
@ -155,6 +156,14 @@ const CardModal = React.memo(
onClose(); onClose();
}, [onClose]); }, [onClose]);
const AttachmentAddPopup = usePopup(AttachmentAddStep);
const BoardMembershipsPopup = usePopup(BoardMembershipsStep);
const LabelsPopup = usePopup(LabelsStep);
const DueDateEditPopup = usePopup(DueDateEditStep);
const TimerEditPopup = usePopup(TimerEditStep);
const CardMovePopup = usePopup(CardMoveStep);
const DeletePopup = usePopup(DeleteStep);
const userIds = users.map((user) => user.id); const userIds = users.map((user) => user.id);
const labelIds = labels.map((label) => label.id); const labelIds = labels.map((label) => label.id);
@ -482,11 +491,9 @@ const CardModal = React.memo(
</Button> </Button>
</CardMovePopup> </CardMovePopup>
<DeletePopup <DeletePopup
title={t('common.deleteCard', { title="common.deleteCard"
context: 'title', content="common.areYouSureYouWantToDeleteThisCard"
})} buttonContent="action.deleteCard"
content={t('common.areYouSureYouWantToDeleteThisCard')}
buttonContent={t('action.deleteCard')}
onConfirm={onDelete} onConfirm={onDelete}
> >
<Button fluid className={styles.actionButton}> <Button fluid className={styles.actionButton}>

View file

@ -2,13 +2,12 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Menu } from 'semantic-ui-react'; import { Menu } from 'semantic-ui-react';
import { withPopup } from '../../../lib/popup';
import { Popup } from '../../../lib/custom-ui'; import { Popup } from '../../../lib/custom-ui';
import { useSteps } from '../../../hooks'; import { useSteps } from '../../../hooks';
import DeleteStep from '../../DeleteStep'; import DeleteStep from '../../DeleteStep';
import styles from './ActionsPopup.module.scss'; import styles from './ActionsStep.module.scss';
const StepTypes = { const StepTypes = {
DELETE: 'DELETE', DELETE: 'DELETE',
@ -30,11 +29,9 @@ const ActionsStep = React.memo(({ onNameEdit, onDelete, onClose }) => {
if (step && step.type === StepTypes.DELETE) { if (step && step.type === StepTypes.DELETE) {
return ( return (
<DeleteStep <DeleteStep
title={t('common.deleteTask', { title="common.deleteTask"
context: 'title', content="common.areYouSureYouWantToDeleteThisTask"
})} buttonContent="action.deleteTask"
content={t('common.areYouSureYouWantToDeleteThisTask')}
buttonContent={t('action.deleteTask')}
onConfirm={onDelete} onConfirm={onDelete}
onBack={handleBack} onBack={handleBack}
/> />
@ -72,4 +69,4 @@ ActionsStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(ActionsStep); export default ActionsStep;

View file

@ -4,9 +4,10 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { Draggable } from 'react-beautiful-dnd'; import { Draggable } from 'react-beautiful-dnd';
import { Button, Checkbox, Icon } from 'semantic-ui-react'; import { Button, Checkbox, Icon } from 'semantic-ui-react';
import { usePopup } from '../../../lib/popup';
import NameEdit from './NameEdit'; import NameEdit from './NameEdit';
import ActionsPopup from './ActionsPopup'; import ActionsStep from './ActionsStep';
import styles from './Item.module.scss'; import styles from './Item.module.scss';
@ -39,6 +40,8 @@ const Item = React.memo(
nameEdit.current.open(); nameEdit.current.open();
}, []); }, []);
const ActionsPopup = usePopup(ActionsStep);
return ( return (
<Draggable draggableId={id} index={index} isDragDisabled={!isPersisted || !canEdit}> <Draggable draggableId={id} index={index} isDragDisabled={!isPersisted || !canEdit}>
{({ innerRef, draggableProps, dragHandleProps }, { isDragging }) => { {({ innerRef, draggableProps, dragHandleProps }, { isDragging }) => {

View file

@ -1,5 +0,0 @@
import { withPopup } from '../lib/popup';
import CardMoveStep from './CardMoveStep';
export default withPopup(CardMoveStep);

View file

@ -1,5 +0,0 @@
import { withPopup } from '../lib/popup';
import DeleteStep from './DeleteStep';
export default withPopup(DeleteStep);

View file

@ -1,19 +1,28 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button } from 'semantic-ui-react'; import { Button } from 'semantic-ui-react';
import { Popup } from '../../lib/custom-ui'; import { Popup } from '../../lib/custom-ui';
import styles from './DeleteStep.module.scss'; import styles from './DeleteStep.module.scss';
const DeleteStep = React.memo(({ title, content, buttonContent, onConfirm, onBack }) => ( const DeleteStep = React.memo(({ title, content, buttonContent, onConfirm, onBack }) => {
const [t] = useTranslation();
return (
<> <>
<Popup.Header onBack={onBack}>{title}</Popup.Header> <Popup.Header onBack={onBack}>
{t(title, {
context: 'title',
})}
</Popup.Header>
<Popup.Content> <Popup.Content>
<div className={styles.content}>{content}</div> <div className={styles.content}>{t(content)}</div>
<Button fluid negative content={buttonContent} onClick={onConfirm} /> <Button fluid negative content={t(buttonContent)} onClick={onConfirm} />
</Popup.Content> </Popup.Content>
</> </>
)); );
});
DeleteStep.propTypes = { DeleteStep.propTypes = {
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,

View file

@ -1,5 +0,0 @@
import { withPopup } from '../lib/popup';
import DueDateEditStep from './DueDateEditStep';
export default withPopup(DueDateEditStep);

View file

@ -3,13 +3,18 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Icon, Menu } from 'semantic-ui-react'; import { Icon, Menu } from 'semantic-ui-react';
import { usePopup } from '../../lib/popup';
import Paths from '../../constants/Paths'; import Paths from '../../constants/Paths';
import NotificationsPopup from './NotificationsPopup'; import NotificationsStep from './NotificationsStep';
import UserPopup from '../UserPopup'; import UserStep from '../UserStep';
import styles from './Header.module.scss'; import styles from './Header.module.scss';
const POPUP_PROPS = {
position: 'bottom right',
};
const Header = React.memo( const Header = React.memo(
({ ({
project, project,
@ -30,6 +35,9 @@ const Header = React.memo(
} }
}, [canEditProject, onProjectSettingsClick]); }, [canEditProject, onProjectSettingsClick]);
const NotificationsPopup = usePopup(NotificationsStep, POPUP_PROPS);
const UserPopup = usePopup(UserStep, POPUP_PROPS);
return ( return (
<div className={styles.wrapper}> <div className={styles.wrapper}>
{!project && ( {!project && (

View file

@ -4,14 +4,13 @@ import PropTypes from 'prop-types';
import { useTranslation, Trans } from 'react-i18next'; import { useTranslation, Trans } from 'react-i18next';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Button } from 'semantic-ui-react'; import { Button } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { Popup } from '../../lib/custom-ui'; import { Popup } from '../../lib/custom-ui';
import Paths from '../../constants/Paths'; import Paths from '../../constants/Paths';
import { ActivityTypes } from '../../constants/Enums'; import { ActivityTypes } from '../../constants/Enums';
import User from '../User'; import User from '../User';
import styles from './NotificationsPopup.module.scss'; import styles from './NotificationsStep.module.scss';
const NotificationsStep = React.memo(({ items, onDelete, onClose }) => { const NotificationsStep = React.memo(({ items, onDelete, onClose }) => {
const [t] = useTranslation(); const [t] = useTranslation();
@ -123,6 +122,4 @@ NotificationsStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(NotificationsStep, { export default NotificationsStep;
position: 'bottom right',
});

View file

@ -1,5 +0,0 @@
import { withPopup } from '../lib/popup';
import LabelsStep from './LabelsStep';
export default withPopup(LabelsStep);

View file

@ -47,11 +47,9 @@ const EditStep = React.memo(({ defaultData, onUpdate, onDelete, onBack }) => {
if (step && step.type === StepTypes.DELETE) { if (step && step.type === StepTypes.DELETE) {
return ( return (
<DeleteStep <DeleteStep
title={t('common.deleteLabel', { title="common.deleteLabel"
context: 'title', content="common.areYouSureYouWantToDeleteThisLabel"
})} buttonContent="action.deleteLabel"
content={t('common.areYouSureYouWantToDeleteThisLabel')}
buttonContent={t('action.deleteLabel')}
onConfirm={onDelete} onConfirm={onDelete}
onBack={handleBack} onBack={handleBack}
/> />

View file

@ -2,13 +2,12 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Menu } from 'semantic-ui-react'; import { Menu } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { Popup } from '../../lib/custom-ui'; import { Popup } from '../../lib/custom-ui';
import { useSteps } from '../../hooks'; import { useSteps } from '../../hooks';
import DeleteStep from '../DeleteStep'; import DeleteStep from '../DeleteStep';
import styles from './ActionsPopup.module.scss'; import styles from './ActionsStep.module.scss';
const StepTypes = { const StepTypes = {
DELETE: 'DELETE', DELETE: 'DELETE',
@ -35,11 +34,9 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
if (step && step.type === StepTypes.DELETE) { if (step && step.type === StepTypes.DELETE) {
return ( return (
<DeleteStep <DeleteStep
title={t('common.deleteList', { title="common.deleteList"
context: 'title', content="common.areYouSureYouWantToDeleteThisList"
})} buttonContent="action.deleteList"
content={t('common.areYouSureYouWantToDeleteThisList')}
buttonContent={t('action.deleteList')}
onConfirm={onDelete} onConfirm={onDelete}
onBack={handleBack} onBack={handleBack}
/> />
@ -83,4 +80,4 @@ ActionsStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(ActionsStep); export default ActionsStep;

View file

@ -4,12 +4,13 @@ import classNames from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Draggable, Droppable } from 'react-beautiful-dnd'; import { Draggable, Droppable } from 'react-beautiful-dnd';
import { Button, Icon } from 'semantic-ui-react'; import { Button, Icon } from 'semantic-ui-react';
import { usePopup } from '../../lib/popup';
import DroppableTypes from '../../constants/DroppableTypes'; import DroppableTypes from '../../constants/DroppableTypes';
import CardContainer from '../../containers/CardContainer'; import CardContainer from '../../containers/CardContainer';
import NameEdit from './NameEdit'; import NameEdit from './NameEdit';
import CardAdd from './CardAdd'; import CardAdd from './CardAdd';
import ActionsPopup from './ActionsPopup'; import ActionsStep from './ActionsStep';
import { ReactComponent as PlusMathIcon } from '../../assets/images/plus-math-icon.svg'; import { ReactComponent as PlusMathIcon } from '../../assets/images/plus-math-icon.svg';
import styles from './List.module.scss'; import styles from './List.module.scss';
@ -59,6 +60,8 @@ const List = React.memo(
} }
}, [cardIds, isAddCardOpened]); }, [cardIds, isAddCardOpened]);
const ActionsPopup = usePopup(ActionsStep);
const cardsNode = ( const cardsNode = (
<Droppable <Droppable
droppableId={`list:${id}`} droppableId={`list:${id}`}

View file

@ -3,13 +3,12 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button } from 'semantic-ui-react'; import { Button } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { useSteps } from '../../hooks'; import { useSteps } from '../../hooks';
import User from '../User'; import User from '../User';
import DeleteStep from '../DeleteStep'; import DeleteStep from '../DeleteStep';
import styles from './ActionsPopup.module.scss'; import styles from './ActionsStep.module.scss';
const StepTypes = { const StepTypes = {
EDIT_PERMISSIONS: 'EDIT_PERMISSIONS', EDIT_PERMISSIONS: 'EDIT_PERMISSIONS',
@ -73,20 +72,15 @@ const ActionsStep = React.memo(
case StepTypes.DELETE: case StepTypes.DELETE:
return ( return (
<DeleteStep <DeleteStep
title={t( title={membership.user.isCurrent ? leaveConfirmationTitle : deleteConfirmationTitle}
membership.user.isCurrent ? leaveConfirmationTitle : deleteConfirmationTitle, content={
{ membership.user.isCurrent ? leaveConfirmationContent : deleteConfirmationContent
context: 'title', }
}, buttonContent={
)}
content={t(
membership.user.isCurrent ? leaveConfirmationContent : deleteConfirmationContent,
)}
buttonContent={t(
membership.user.isCurrent membership.user.isCurrent
? leaveConfirmationButtonContent ? leaveConfirmationButtonContent
: deleteConfirmationButtonContent, : deleteConfirmationButtonContent
)} }
onConfirm={onDelete} onConfirm={onDelete}
onBack={handleBack} onBack={handleBack}
/> />
@ -165,4 +159,4 @@ ActionsStep.defaultProps = {
onUpdate: undefined, onUpdate: undefined,
}; };
export default withPopup(ActionsStep); export default ActionsStep;

View file

@ -1,3 +0,0 @@
import AddPopup from './AddPopup';
export default AddPopup;

View file

@ -1,13 +1,12 @@
import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { withPopup } from '../../../lib/popup';
import { Input, Popup } from '../../../lib/custom-ui'; import { Input, Popup } from '../../../lib/custom-ui';
import { useField, useSteps } from '../../../hooks'; import { useField, useSteps } from '../../../hooks';
import UserItem from './UserItem'; import UserItem from './UserItem';
import styles from './AddPopup.module.scss'; import styles from './AddStep.module.scss';
const StepTypes = { const StepTypes = {
SELECT_PERMISSIONS: 'SELECT_PERMISSIONS', SELECT_PERMISSIONS: 'SELECT_PERMISSIONS',
@ -143,4 +142,4 @@ AddStep.defaultProps = {
title: 'common.addMember', title: 'common.addMember',
}; };
export default withPopup(AddStep); export default AddStep;

View file

@ -0,0 +1,3 @@
import AddStep from './AddStep';
export default AddStep;

View file

@ -1,9 +1,10 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Button } from 'semantic-ui-react'; import { Button } from 'semantic-ui-react';
import { usePopup } from '../../lib/popup';
import AddPopup from './AddPopup'; import AddStep from './AddStep';
import ActionsPopup from './ActionsPopup'; import ActionsStep from './ActionsStep';
import User from '../User'; import User from '../User';
import styles from './Memberships.module.scss'; import styles from './Memberships.module.scss';
@ -28,6 +29,9 @@ const Memberships = React.memo(
onUpdate, onUpdate,
onDelete, onDelete,
}) => { }) => {
const AddPopup = usePopup(AddStep);
const ActionsPopup = usePopup(ActionsStep);
return ( return (
<> <>
<span className={styles.users}> <span className={styles.users}>

View file

@ -2,15 +2,18 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Divider, Header, Tab } from 'semantic-ui-react'; import { Button, Divider, Header, Tab } from 'semantic-ui-react';
import { usePopup } from '../../../lib/popup';
import InformationEdit from './InformationEdit'; import InformationEdit from './InformationEdit';
import DeletePopup from '../../DeletePopup'; import DeleteStep from '../../DeleteStep';
import styles from './GeneralPane.module.scss'; import styles from './GeneralPane.module.scss';
const GeneralPane = React.memo(({ name, onUpdate, onDelete }) => { const GeneralPane = React.memo(({ name, onUpdate, onDelete }) => {
const [t] = useTranslation(); const [t] = useTranslation();
const DeletePopup = usePopup(DeleteStep);
return ( return (
<Tab.Pane attached={false} className={styles.wrapper}> <Tab.Pane attached={false} className={styles.wrapper}>
<InformationEdit <InformationEdit
@ -28,11 +31,9 @@ const GeneralPane = React.memo(({ name, onUpdate, onDelete }) => {
</Divider> </Divider>
<div className={styles.action}> <div className={styles.action}>
<DeletePopup <DeletePopup
title={t('common.deleteProject', { title="common.deleteProject"
context: 'title', content="common.areYouSureYouWantToDeleteThisProject"
})} buttonContent="action.deleteProject"
content={t('common.areYouSureYouWantToDeleteThisProject')}
buttonContent={t('action.deleteProject')}
onConfirm={onDelete} onConfirm={onDelete}
> >
<Button className={styles.actionButton}> <Button className={styles.actionButton}>

View file

@ -1,5 +0,0 @@
import { withPopup } from '../lib/popup';
import TimerEditStep from './TimerEditStep';
export default withPopup(TimerEditStep);

View file

@ -1,3 +0,0 @@
import UserAddPopup from './UserAddPopup';
export default UserAddPopup;

View file

@ -4,13 +4,12 @@ import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Form, Message } from 'semantic-ui-react'; import { Button, Form, Message } from 'semantic-ui-react';
import { usePrevious } from '../../lib/hooks'; import { usePrevious } from '../../lib/hooks';
import { withPopup } from '../../lib/popup';
import { Input, Popup } from '../../lib/custom-ui'; import { Input, Popup } from '../../lib/custom-ui';
import { useForm } from '../../hooks'; import { useForm } from '../../hooks';
import { isPassword, isUsername } from '../../utils/validator'; import { isPassword, isUsername } from '../../utils/validator';
import styles from './UserAddPopup.module.scss'; import styles from './UserAddStep.module.scss';
const createMessage = (error) => { const createMessage = (error) => {
if (!error) { if (!error) {
@ -206,4 +205,4 @@ UserAddStep.defaultProps = {
error: undefined, error: undefined,
}; };
export default withPopup(UserAddStep); export default UserAddStep;

View file

@ -0,0 +1,3 @@
import UserAddStep from './UserAddStep';
export default UserAddStep;

View file

@ -1,5 +0,0 @@
import { withPopup } from '../lib/popup';
import UserEmailEditStep from './UserEmailEditStep';
export default withPopup(UserEmailEditStep);

View file

@ -1,5 +0,0 @@
import { withPopup } from '../lib/popup';
import UserPasswordEditStep from './UserPasswordEditStep';
export default withPopup(UserPasswordEditStep);

View file

@ -1,3 +0,0 @@
import UserPopup from './UserPopup';
export default UserPopup;

View file

@ -2,14 +2,15 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Divider, Dropdown, Header, Tab } from 'semantic-ui-react'; import { Button, Divider, Dropdown, Header, Tab } from 'semantic-ui-react';
import { usePopup } from '../../../lib/popup';
import locales from '../../../locales'; import locales from '../../../locales';
import AvatarEditPopup from './AvatarEditPopup'; import AvatarEditStep from './AvatarEditStep';
import User from '../../User'; import User from '../../User';
import UserInformationEdit from '../../UserInformationEdit'; import UserInformationEdit from '../../UserInformationEdit';
import UserUsernameEditPopup from '../../UserUsernameEditPopup'; import UserUsernameEditStep from '../../UserUsernameEditStep';
import UserEmailEditPopup from '../../UserEmailEditPopup'; import UserEmailEditStep from '../../UserEmailEditStep';
import UserPasswordEditPopup from '../../UserPasswordEditPopup'; import UserPasswordEditStep from '../../UserPasswordEditStep';
import styles from './AccountPane.module.scss'; import styles from './AccountPane.module.scss';
@ -51,6 +52,11 @@ const AccountPane = React.memo(
[onLanguageUpdate], [onLanguageUpdate],
); );
const AvatarEditPopup = usePopup(AvatarEditStep);
const UserUsernameEditPopup = usePopup(UserUsernameEditStep);
const UserEmailEditPopup = usePopup(UserEmailEditStep);
const UserPasswordEditPopup = usePopup(UserPasswordEditStep);
return ( return (
<Tab.Pane attached={false} className={styles.wrapper}> <Tab.Pane attached={false} className={styles.wrapper}>
<AvatarEditPopup <AvatarEditPopup

View file

@ -2,10 +2,9 @@ import React, { useCallback, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button } from 'semantic-ui-react'; import { Button } from 'semantic-ui-react';
import { withPopup } from '../../../lib/popup';
import { FilePicker, Popup } from '../../../lib/custom-ui'; import { FilePicker, Popup } from '../../../lib/custom-ui';
import styles from './AvatarEditPopup.module.scss'; import styles from './AvatarEditStep.module.scss';
const AvatarEditStep = React.memo(({ defaultValue, onUpdate, onDelete, onClose }) => { const AvatarEditStep = React.memo(({ defaultValue, onUpdate, onDelete, onClose }) => {
const [t] = useTranslation(); const [t] = useTranslation();
@ -68,4 +67,4 @@ AvatarEditStep.defaultProps = {
defaultValue: undefined, defaultValue: undefined,
}; };
export default withPopup(AvatarEditStep); export default AvatarEditStep;

View file

@ -2,10 +2,9 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Menu } from 'semantic-ui-react'; import { Button, Menu } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { Popup } from '../../lib/custom-ui'; import { Popup } from '../../lib/custom-ui';
import styles from './UserPopup.module.scss'; import styles from './UserStep.module.scss';
const UserStep = React.memo(({ isLogouting, onSettingsClick, onLogout, onClose }) => { const UserStep = React.memo(({ isLogouting, onSettingsClick, onLogout, onClose }) => {
const [t] = useTranslation(); const [t] = useTranslation();
@ -62,6 +61,4 @@ UserStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(UserStep, { export default UserStep;
position: 'bottom right',
});

View file

@ -0,0 +1,3 @@
import UserStep from './UserStep';
export default UserStep;

View file

@ -1,5 +0,0 @@
import { withPopup } from '../lib/popup';
import UserUsernameEditStep from './UserUsernameEditStep';
export default withPopup(UserUsernameEditStep);

View file

@ -3,7 +3,6 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Menu } from 'semantic-ui-react'; import { Menu } from 'semantic-ui-react';
import { withPopup } from '../../../lib/popup';
import { Popup } from '../../../lib/custom-ui'; import { Popup } from '../../../lib/custom-ui';
import { useSteps } from '../../../hooks'; import { useSteps } from '../../../hooks';
@ -13,7 +12,7 @@ import UserEmailEditStep from '../../UserEmailEditStep';
import UserPasswordEditStep from '../../UserPasswordEditStep'; import UserPasswordEditStep from '../../UserPasswordEditStep';
import DeleteStep from '../../DeleteStep'; import DeleteStep from '../../DeleteStep';
import styles from './ActionsPopup.module.scss'; import styles from './ActionsStep.module.scss';
const StepTypes = { const StepTypes = {
EDIT_INFORMATION: 'EDIT_INFORMATION', EDIT_INFORMATION: 'EDIT_INFORMATION',
@ -111,11 +110,9 @@ const ActionsStep = React.memo(
case StepTypes.DELETE: case StepTypes.DELETE:
return ( return (
<DeleteStep <DeleteStep
title={t('common.deleteUser', { title="common.deleteUser"
context: 'title', content="common.areYouSureYouWantToDeleteThisUser"
})} buttonContent="action.deleteUser"
content={t('common.areYouSureYouWantToDeleteThisUser')}
buttonContent={t('action.deleteUser')}
onConfirm={onDelete} onConfirm={onDelete}
onBack={handleBack} onBack={handleBack}
/> />
@ -178,4 +175,4 @@ ActionsStep.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default withPopup(ActionsStep); export default ActionsStep;

View file

@ -1,8 +1,9 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Button, Icon, Radio, Table } from 'semantic-ui-react'; import { Button, Icon, Radio, Table } from 'semantic-ui-react';
import { usePopup } from '../../../lib/popup';
import ActionsPopup from './ActionsPopup'; import ActionsStep from './ActionsStep';
import User from '../../User'; import User from '../../User';
import styles from './Item.module.scss'; import styles from './Item.module.scss';
@ -34,6 +35,8 @@ const Item = React.memo(
}); });
}, [isAdmin, onUpdate]); }, [isAdmin, onUpdate]);
const ActionsPopup = usePopup(ActionsStep);
return ( return (
<Table.Row> <Table.Row>
<Table.Cell> <Table.Cell>

View file

@ -2,8 +2,9 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Modal, Table } from 'semantic-ui-react'; import { Button, Modal, Table } from 'semantic-ui-react';
import { usePopup } from '../../lib/popup';
import UserAddPopupContainer from '../../containers/UserAddPopupContainer'; import UserAddStepContainer from '../../containers/UserAddStepContainer';
import Item from './Item'; import Item from './Item';
const UsersModal = React.memo( const UsersModal = React.memo(
@ -77,6 +78,8 @@ const UsersModal = React.memo(
[onDelete], [onDelete],
); );
const UserAddPopupContainer = usePopup(UserAddStepContainer);
return ( return (
<Modal open closeIcon size="large" centered={false} onClose={onClose}> <Modal open closeIcon size="large" centered={false} onClose={onClose}>
<Modal.Header> <Modal.Header>

View file

@ -2,7 +2,7 @@ import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import entryActions from '../entry-actions'; import entryActions from '../entry-actions';
import UserAddPopup from '../components/UserAddPopup'; import UserAddStep from '../components/UserAddStep';
const mapStateToProps = ({ const mapStateToProps = ({
ui: { ui: {
@ -23,4 +23,4 @@ const mapDispatchToProps = (dispatch) =>
dispatch, dispatch,
); );
export default connect(mapStateToProps, mapDispatchToProps)(UserAddPopup); export default connect(mapStateToProps, mapDispatchToProps)(UserAddStep);

View file

@ -1,4 +1,4 @@
import withPopup from './with-popup'; import usePopup from './use-popup';
import closePopup from './close-popup'; import closePopup from './close-popup';
export { withPopup, closePopup }; export { usePopup, closePopup };

View file

@ -0,0 +1,122 @@
import { ResizeObserver } from '@juggle/resize-observer';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { Button, Popup as SemanticUIPopup } from 'semantic-ui-react';
import styles from './Popup.module.css';
export default (Step, props) => {
return useMemo(() => {
const Popup = React.memo(({ children, onClose, ...stepProps }) => {
const [isOpened, setIsOpened] = useState(false);
const wrapper = useRef(null);
const resizeObserver = useRef(null);
const handleOpen = useCallback(() => {
setIsOpened(true);
}, []);
const handleClose = useCallback(() => {
setIsOpened(false);
if (onClose) {
onClose();
}
}, [onClose]);
const handleMouseDown = useCallback((event) => {
event.stopPropagation();
}, []);
const handleClick = useCallback((event) => {
event.stopPropagation();
}, []);
const handleTriggerClick = useCallback(
(event) => {
event.stopPropagation();
const { onClick } = children;
if (onClick) {
onClick(event);
}
},
[children],
);
const handleContentRef = useCallback((element) => {
if (resizeObserver.current) {
resizeObserver.current.disconnect();
}
if (!element) {
resizeObserver.current = null;
return;
}
resizeObserver.current = new ResizeObserver(() => {
if (resizeObserver.current.isInitial) {
resizeObserver.current.isInitial = false;
return;
}
wrapper.current.positionUpdate();
});
resizeObserver.current.isInitial = true;
resizeObserver.current.observe(element);
}, []);
const tigger = React.cloneElement(children, {
onClick: handleTriggerClick,
});
return (
<SemanticUIPopup
basic
wide
ref={wrapper}
trigger={tigger}
on="click"
open={isOpened}
position="bottom left"
popperModifiers={[
{
name: 'preventOverflow',
enabled: true,
options: {
altAxis: true,
padding: 20,
},
},
]}
className={styles.wrapper}
onOpen={handleOpen}
onClose={handleClose}
onMouseDown={handleMouseDown}
onClick={handleClick}
{...props} // eslint-disable-line react/jsx-props-no-spreading
>
<div ref={handleContentRef}>
<Button icon="close" onClick={handleClose} className={styles.closeButton} />
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<Step {...stepProps} onClose={handleClose} />
</div>
</SemanticUIPopup>
);
});
Popup.propTypes = {
children: PropTypes.node.isRequired,
onClose: PropTypes.func,
};
Popup.defaultProps = {
onClose: undefined,
};
return Popup;
}, [props]);
};

View file

@ -1,120 +0,0 @@
import { ResizeObserver } from '@juggle/resize-observer';
import React, { useCallback, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { Button, Popup as SemanticUIPopup } from 'semantic-ui-react';
import styles from './Popup.module.css';
export default (WrappedComponent, defaultProps) => {
const Popup = React.memo(({ children, onClose, ...props }) => {
const [isOpened, setIsOpened] = useState(false);
const wrapper = useRef(null);
const resizeObserver = useRef(null);
const handleOpen = useCallback(() => {
setIsOpened(true);
}, []);
const handleClose = useCallback(() => {
setIsOpened(false);
if (onClose) {
onClose();
}
}, [onClose]);
const handleMouseDown = useCallback((event) => {
event.stopPropagation();
}, []);
const handleClick = useCallback((event) => {
event.stopPropagation();
}, []);
const handleTriggerClick = useCallback(
(event) => {
event.stopPropagation();
const { onClick } = children;
if (onClick) {
onClick(event);
}
},
[children],
);
const handleContentRef = useCallback((element) => {
if (resizeObserver.current) {
resizeObserver.current.disconnect();
}
if (!element) {
resizeObserver.current = null;
return;
}
resizeObserver.current = new ResizeObserver(() => {
if (resizeObserver.current.isInitial) {
resizeObserver.current.isInitial = false;
return;
}
wrapper.current.positionUpdate();
});
resizeObserver.current.isInitial = true;
resizeObserver.current.observe(element);
}, []);
const tigger = React.cloneElement(children, {
onClick: handleTriggerClick,
});
return (
<SemanticUIPopup
basic
wide
ref={wrapper}
trigger={tigger}
on="click"
open={isOpened}
position="bottom left"
popperModifiers={[
{
name: 'preventOverflow',
enabled: true,
options: {
altAxis: true,
padding: 20,
},
},
]}
className={styles.wrapper}
onOpen={handleOpen}
onClose={handleClose}
onMouseDown={handleMouseDown}
onClick={handleClick}
{...defaultProps} // eslint-disable-line react/jsx-props-no-spreading
>
<div ref={handleContentRef}>
<Button icon="close" onClick={handleClose} className={styles.closeButton} />
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<WrappedComponent {...props} onClose={handleClose} />
</div>
</SemanticUIPopup>
);
});
Popup.propTypes = {
children: PropTypes.node.isRequired,
onClose: PropTypes.func,
};
Popup.defaultProps = {
onClose: undefined,
};
return Popup;
};