1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-22 06:39: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 0f50dbde92
commit 8a46a2e0b9
69 changed files with 309 additions and 332 deletions

View file

@ -1,11 +1,12 @@
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { usePopup } from '../../lib/popup';
import User from '../User';
import Label from '../Label';
import BoardMembershipsPopup from '../BoardMembershipsPopup';
import LabelsPopup from '../LabelsPopup';
import BoardMembershipsStep from '../BoardMembershipsStep';
import LabelsStep from '../LabelsStep';
import styles from './Filters.module.scss';
@ -41,15 +42,16 @@ const Filters = React.memo(
[onLabelRemove],
);
const BoardMembershipsPopup = usePopup(BoardMembershipsStep);
const LabelsPopup = usePopup(LabelsStep);
return (
<>
<span className={styles.filter}>
<BoardMembershipsPopup
items={allBoardMemberships}
currentUserIds={users.map((user) => user.id)}
title={t('common.filterByMembers', {
context: 'title',
})}
title="common.filterByMembers"
onUserSelect={onUserAdd}
onUserDeselect={onUserRemove}
>
@ -73,9 +75,7 @@ const Filters = React.memo(
<LabelsPopup
items={allLabels}
currentIds={labels.map((label) => label.id)}
title={t('common.filterByLabels', {
context: 'title',
})}
title="common.filterByLabels"
canEdit={canEdit}
onSelect={onLabelAdd}
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 { Button, Form, Icon } from 'semantic-ui-react';
import { useDidUpdate, useToggle } from '../../../lib/hooks';
import { withPopup } from '../../../lib/popup';
import { Input, Popup } from '../../../lib/custom-ui';
import { useForm, useSteps } from '../../../hooks';
import ImportStep from './ImportStep';
import styles from './AddPopup.module.scss';
import styles from './AddStep.module.scss';
const StepTypes = {
IMPORT: 'IMPORT',
@ -114,4 +113,4 @@ AddStep.propTypes = {
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 { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
import { Button, Icon } from 'semantic-ui-react';
import { closePopup } from '../../lib/popup';
import { closePopup, usePopup } from '../../lib/popup';
import Paths from '../../constants/Paths';
import DroppableTypes from '../../constants/DroppableTypes';
import AddPopup from './AddPopup';
import EditPopup from './EditPopup';
import AddStep from './AddStep';
import EditStep from './EditStep';
import styles from './Boards.module.scss';
@ -52,6 +52,9 @@ const Boards = React.memo(({ items, currentId, canEdit, onCreate, onUpdate, onMo
[onDelete],
);
const AddPopup = usePopup(AddStep);
const EditPopup = usePopup(EditStep);
const itemsNode = items.map((item, index) => (
<Draggable
key={item.id}

View file

@ -3,13 +3,12 @@ import React, { useCallback, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button, Form } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { Input, Popup } from '../../lib/custom-ui';
import { useForm, useSteps } from '../../hooks';
import DeleteStep from '../DeleteStep';
import styles from './EditPopup.module.scss';
import styles from './EditStep.module.scss';
const StepTypes = {
DELETE: 'DELETE',
@ -56,11 +55,9 @@ const EditStep = React.memo(({ defaultData, onUpdate, onDelete, onClose }) => {
if (step && step.type === StepTypes.DELETE) {
return (
<DeleteStep
title={t('common.deleteBoard', {
context: 'title',
})}
content={t('common.areYouSureYouWantToDeleteThisBoard')}
buttonContent={t('action.deleteBoard')}
title="common.deleteBoard"
content="common.areYouSureYouWantToDeleteThisBoard"
buttonContent="action.deleteBoard"
onConfirm={onDelete}
onBack={handleBack}
/>
@ -104,4 +101,4 @@ EditStep.propTypes = {
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 { useTranslation } from 'react-i18next';
import { Menu } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { Popup } from '../../lib/custom-ui';
import { useSteps } from '../../hooks';
@ -14,7 +13,7 @@ import TimerEditStep from '../TimerEditStep';
import CardMoveStep from '../CardMoveStep';
import DeleteStep from '../DeleteStep';
import styles from './ActionsPopup.module.scss';
import styles from './ActionsStep.module.scss';
const StepTypes = {
USERS: 'USERS',
@ -158,11 +157,9 @@ const ActionsStep = React.memo(
case StepTypes.DELETE:
return (
<DeleteStep
title={t('common.deleteCard', {
context: 'title',
})}
content={t('common.areYouSureYouWantToDeleteThisCard')}
buttonContent={t('action.deleteCard')}
title="common.deleteCard"
content="common.areYouSureYouWantToDeleteThisCard"
buttonContent="action.deleteCard"
onConfirm={onDelete}
onBack={handleBack}
/>
@ -248,4 +245,4 @@ ActionsStep.propTypes = {
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 { Link } from 'react-router-dom';
import { Draggable } from 'react-beautiful-dnd';
import { usePopup } from '../../lib/popup';
import { startTimer, stopTimer } from '../../utils/timer';
import Paths from '../../constants/Paths';
import Tasks from './Tasks';
import NameEdit from './NameEdit';
import ActionsPopup from './ActionsPopup';
import ActionsStep from './ActionsStep';
import User from '../User';
import Label from '../Label';
import DueDate from '../DueDate';
@ -83,6 +84,8 @@ const Card = React.memo(
nameEdit.current.open();
}, []);
const ActionsPopup = usePopup(ActionsStep);
const contentNode = (
<>
{coverUrl && <img src={coverUrl} alt="" className={styles.cover} />}

View file

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

View file

@ -2,10 +2,9 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Menu } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
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 [t] = useTranslation();
@ -51,4 +50,4 @@ AttachmentAddStep.propTypes = {
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 { useTranslation } from 'react-i18next';
import { Button, Form } from 'semantic-ui-react';
import { withPopup } from '../../../lib/popup';
import { Input, Popup } from '../../../lib/custom-ui';
import { useForm, useSteps } from '../../../hooks';
import DeleteStep from '../../DeleteStep';
import styles from './EditPopup.module.scss';
import styles from './EditStep.module.scss';
const StepTypes = {
DELETE: 'DELETE',
@ -56,11 +55,9 @@ const EditStep = React.memo(({ defaultData, onUpdate, onDelete, onClose }) => {
if (step && step.type === StepTypes.DELETE) {
return (
<DeleteStep
title={t('common.deleteAttachment', {
context: 'title',
})}
content={t('common.areYouSureYouWantToDeleteThisAttachment')}
buttonContent={t('action.deleteAttachment')}
title="common.deleteAttachment"
content="common.areYouSureYouWantToDeleteThisAttachment"
buttonContent="action.deleteAttachment"
onConfirm={onDelete}
onBack={handleBack}
/>
@ -104,4 +101,4 @@ EditStep.propTypes = {
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 { useTranslation } from 'react-i18next';
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';
@ -49,6 +50,8 @@ const Item = React.forwardRef(
[isCover, onCoverSelect, onCoverDeselect],
);
const EditPopup = usePopup(EditStep);
if (!isPersisted) {
return (
<div className={classNames(styles.wrapper, styles.wrapperSubmitting)}>

View file

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

View file

@ -2,13 +2,12 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Menu } from 'semantic-ui-react';
import { withPopup } from '../../../lib/popup';
import { Popup } from '../../../lib/custom-ui';
import { useSteps } from '../../../hooks';
import DeleteStep from '../../DeleteStep';
import styles from './ActionsPopup.module.scss';
import styles from './ActionsStep.module.scss';
const StepTypes = {
DELETE: 'DELETE',
@ -30,11 +29,9 @@ const ActionsStep = React.memo(({ onNameEdit, onDelete, onClose }) => {
if (step && step.type === StepTypes.DELETE) {
return (
<DeleteStep
title={t('common.deleteTask', {
context: 'title',
})}
content={t('common.areYouSureYouWantToDeleteThisTask')}
buttonContent={t('action.deleteTask')}
title="common.deleteTask"
content="common.areYouSureYouWantToDeleteThisTask"
buttonContent="action.deleteTask"
onConfirm={onDelete}
onBack={handleBack}
/>
@ -72,4 +69,4 @@ ActionsStep.propTypes = {
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 { Draggable } from 'react-beautiful-dnd';
import { Button, Checkbox, Icon } from 'semantic-ui-react';
import { usePopup } from '../../../lib/popup';
import NameEdit from './NameEdit';
import ActionsPopup from './ActionsPopup';
import ActionsStep from './ActionsStep';
import styles from './Item.module.scss';
@ -39,6 +40,8 @@ const Item = React.memo(
nameEdit.current.open();
}, []);
const ActionsPopup = usePopup(ActionsStep);
return (
<Draggable draggableId={id} index={index} isDragDisabled={!isPersisted || !canEdit}>
{({ 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 PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button } from 'semantic-ui-react';
import { Popup } from '../../lib/custom-ui';
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>
<div className={styles.content}>{content}</div>
<Button fluid negative content={buttonContent} onClick={onConfirm} />
<div className={styles.content}>{t(content)}</div>
<Button fluid negative content={t(buttonContent)} onClick={onConfirm} />
</Popup.Content>
</>
));
);
});
DeleteStep.propTypes = {
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 { Link } from 'react-router-dom';
import { Icon, Menu } from 'semantic-ui-react';
import { usePopup } from '../../lib/popup';
import Paths from '../../constants/Paths';
import NotificationsPopup from './NotificationsPopup';
import UserPopup from '../UserPopup';
import NotificationsStep from './NotificationsStep';
import UserStep from '../UserStep';
import styles from './Header.module.scss';
const POPUP_PROPS = {
position: 'bottom right',
};
const Header = React.memo(
({
project,
@ -30,6 +35,9 @@ const Header = React.memo(
}
}, [canEditProject, onProjectSettingsClick]);
const NotificationsPopup = usePopup(NotificationsStep, POPUP_PROPS);
const UserPopup = usePopup(UserStep, POPUP_PROPS);
return (
<div className={styles.wrapper}>
{!project && (

View file

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

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) {
return (
<DeleteStep
title={t('common.deleteLabel', {
context: 'title',
})}
content={t('common.areYouSureYouWantToDeleteThisLabel')}
buttonContent={t('action.deleteLabel')}
title="common.deleteLabel"
content="common.areYouSureYouWantToDeleteThisLabel"
buttonContent="action.deleteLabel"
onConfirm={onDelete}
onBack={handleBack}
/>

View file

@ -2,13 +2,12 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Menu } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { Popup } from '../../lib/custom-ui';
import { useSteps } from '../../hooks';
import DeleteStep from '../DeleteStep';
import styles from './ActionsPopup.module.scss';
import styles from './ActionsStep.module.scss';
const StepTypes = {
DELETE: 'DELETE',
@ -35,11 +34,9 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
if (step && step.type === StepTypes.DELETE) {
return (
<DeleteStep
title={t('common.deleteList', {
context: 'title',
})}
content={t('common.areYouSureYouWantToDeleteThisList')}
buttonContent={t('action.deleteList')}
title="common.deleteList"
content="common.areYouSureYouWantToDeleteThisList"
buttonContent="action.deleteList"
onConfirm={onDelete}
onBack={handleBack}
/>
@ -83,4 +80,4 @@ ActionsStep.propTypes = {
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 { Draggable, Droppable } from 'react-beautiful-dnd';
import { Button, Icon } from 'semantic-ui-react';
import { usePopup } from '../../lib/popup';
import DroppableTypes from '../../constants/DroppableTypes';
import CardContainer from '../../containers/CardContainer';
import NameEdit from './NameEdit';
import CardAdd from './CardAdd';
import ActionsPopup from './ActionsPopup';
import ActionsStep from './ActionsStep';
import { ReactComponent as PlusMathIcon } from '../../assets/images/plus-math-icon.svg';
import styles from './List.module.scss';
@ -59,6 +60,8 @@ const List = React.memo(
}
}, [cardIds, isAddCardOpened]);
const ActionsPopup = usePopup(ActionsStep);
const cardsNode = (
<Droppable
droppableId={`list:${id}`}

View file

@ -3,13 +3,12 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
import { useSteps } from '../../hooks';
import User from '../User';
import DeleteStep from '../DeleteStep';
import styles from './ActionsPopup.module.scss';
import styles from './ActionsStep.module.scss';
const StepTypes = {
EDIT_PERMISSIONS: 'EDIT_PERMISSIONS',
@ -73,20 +72,15 @@ const ActionsStep = React.memo(
case StepTypes.DELETE:
return (
<DeleteStep
title={t(
membership.user.isCurrent ? leaveConfirmationTitle : deleteConfirmationTitle,
{
context: 'title',
},
)}
content={t(
membership.user.isCurrent ? leaveConfirmationContent : deleteConfirmationContent,
)}
buttonContent={t(
title={membership.user.isCurrent ? leaveConfirmationTitle : deleteConfirmationTitle}
content={
membership.user.isCurrent ? leaveConfirmationContent : deleteConfirmationContent
}
buttonContent={
membership.user.isCurrent
? leaveConfirmationButtonContent
: deleteConfirmationButtonContent,
)}
: deleteConfirmationButtonContent
}
onConfirm={onDelete}
onBack={handleBack}
/>
@ -165,4 +159,4 @@ ActionsStep.defaultProps = {
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 PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { withPopup } from '../../../lib/popup';
import { Input, Popup } from '../../../lib/custom-ui';
import { useField, useSteps } from '../../../hooks';
import UserItem from './UserItem';
import styles from './AddPopup.module.scss';
import styles from './AddStep.module.scss';
const StepTypes = {
SELECT_PERMISSIONS: 'SELECT_PERMISSIONS',
@ -143,4 +142,4 @@ AddStep.defaultProps = {
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 PropTypes from 'prop-types';
import { Button } from 'semantic-ui-react';
import { usePopup } from '../../lib/popup';
import AddPopup from './AddPopup';
import ActionsPopup from './ActionsPopup';
import AddStep from './AddStep';
import ActionsStep from './ActionsStep';
import User from '../User';
import styles from './Memberships.module.scss';
@ -28,6 +29,9 @@ const Memberships = React.memo(
onUpdate,
onDelete,
}) => {
const AddPopup = usePopup(AddStep);
const ActionsPopup = usePopup(ActionsStep);
return (
<>
<span className={styles.users}>

View file

@ -2,15 +2,18 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button, Divider, Header, Tab } from 'semantic-ui-react';
import { usePopup } from '../../../lib/popup';
import InformationEdit from './InformationEdit';
import DeletePopup from '../../DeletePopup';
import DeleteStep from '../../DeleteStep';
import styles from './GeneralPane.module.scss';
const GeneralPane = React.memo(({ name, onUpdate, onDelete }) => {
const [t] = useTranslation();
const DeletePopup = usePopup(DeleteStep);
return (
<Tab.Pane attached={false} className={styles.wrapper}>
<InformationEdit
@ -28,11 +31,9 @@ const GeneralPane = React.memo(({ name, onUpdate, onDelete }) => {
</Divider>
<div className={styles.action}>
<DeletePopup
title={t('common.deleteProject', {
context: 'title',
})}
content={t('common.areYouSureYouWantToDeleteThisProject')}
buttonContent={t('action.deleteProject')}
title="common.deleteProject"
content="common.areYouSureYouWantToDeleteThisProject"
buttonContent="action.deleteProject"
onConfirm={onDelete}
>
<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 { Button, Form, Message } from 'semantic-ui-react';
import { usePrevious } from '../../lib/hooks';
import { withPopup } from '../../lib/popup';
import { Input, Popup } from '../../lib/custom-ui';
import { useForm } from '../../hooks';
import { isPassword, isUsername } from '../../utils/validator';
import styles from './UserAddPopup.module.scss';
import styles from './UserAddStep.module.scss';
const createMessage = (error) => {
if (!error) {
@ -206,4 +205,4 @@ UserAddStep.defaultProps = {
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 { useTranslation } from 'react-i18next';
import { Button, Divider, Dropdown, Header, Tab } from 'semantic-ui-react';
import { usePopup } from '../../../lib/popup';
import locales from '../../../locales';
import AvatarEditPopup from './AvatarEditPopup';
import AvatarEditStep from './AvatarEditStep';
import User from '../../User';
import UserInformationEdit from '../../UserInformationEdit';
import UserUsernameEditPopup from '../../UserUsernameEditPopup';
import UserEmailEditPopup from '../../UserEmailEditPopup';
import UserPasswordEditPopup from '../../UserPasswordEditPopup';
import UserUsernameEditStep from '../../UserUsernameEditStep';
import UserEmailEditStep from '../../UserEmailEditStep';
import UserPasswordEditStep from '../../UserPasswordEditStep';
import styles from './AccountPane.module.scss';
@ -51,6 +52,11 @@ const AccountPane = React.memo(
[onLanguageUpdate],
);
const AvatarEditPopup = usePopup(AvatarEditStep);
const UserUsernameEditPopup = usePopup(UserUsernameEditStep);
const UserEmailEditPopup = usePopup(UserEmailEditStep);
const UserPasswordEditPopup = usePopup(UserPasswordEditStep);
return (
<Tab.Pane attached={false} className={styles.wrapper}>
<AvatarEditPopup

View file

@ -2,10 +2,9 @@ import React, { useCallback, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button } from 'semantic-ui-react';
import { withPopup } from '../../../lib/popup';
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 [t] = useTranslation();
@ -68,4 +67,4 @@ AvatarEditStep.defaultProps = {
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 { useTranslation } from 'react-i18next';
import { Button, Menu } from 'semantic-ui-react';
import { withPopup } from '../../lib/popup';
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 [t] = useTranslation();
@ -62,6 +61,4 @@ UserStep.propTypes = {
onClose: PropTypes.func.isRequired,
};
export default withPopup(UserStep, {
position: 'bottom right',
});
export default UserStep;

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

View file

@ -1,8 +1,9 @@
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
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 styles from './Item.module.scss';
@ -34,6 +35,8 @@ const Item = React.memo(
});
}, [isAdmin, onUpdate]);
const ActionsPopup = usePopup(ActionsStep);
return (
<Table.Row>
<Table.Cell>

View file

@ -2,8 +2,9 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
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';
const UsersModal = React.memo(
@ -77,6 +78,8 @@ const UsersModal = React.memo(
[onDelete],
);
const UserAddPopupContainer = usePopup(UserAddStepContainer);
return (
<Modal open closeIcon size="large" centered={false} onClose={onClose}>
<Modal.Header>

View file

@ -2,7 +2,7 @@ import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import entryActions from '../entry-actions';
import UserAddPopup from '../components/UserAddPopup';
import UserAddStep from '../components/UserAddStep';
const mapStateToProps = ({
ui: {
@ -23,4 +23,4 @@ const mapDispatchToProps = (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';
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;
};