feat: Extend card action shortcuts
This commit is contained in:
@@ -18,6 +18,7 @@ import { isModifierKeyPressed } from '../../../utils/event-helpers';
|
|||||||
import { BoardShortcutsContext } from '../../../contexts';
|
import { BoardShortcutsContext } from '../../../contexts';
|
||||||
import Paths from '../../../constants/Paths';
|
import Paths from '../../../constants/Paths';
|
||||||
import { BoardMembershipRoles, ListTypes } from '../../../constants/Enums';
|
import { BoardMembershipRoles, ListTypes } from '../../../constants/Enums';
|
||||||
|
import CardActionsStep from '../../cards/CardActionsStep';
|
||||||
|
|
||||||
const canEditCardName = (boardMembership, list) => {
|
const canEditCardName = (boardMembership, list) => {
|
||||||
if (isListArchiveOrTrash(list)) {
|
if (isListArchiveOrTrash(list)) {
|
||||||
@@ -35,6 +36,14 @@ const canArchiveCard = (boardMembership, list) => {
|
|||||||
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const canUseCardMembers = (boardMembership, list) => {
|
||||||
|
if (isListArchiveOrTrash(list)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
|
||||||
|
};
|
||||||
|
|
||||||
const canUseCardLabels = (boardMembership, list) => {
|
const canUseCardLabels = (boardMembership, list) => {
|
||||||
if (isListArchiveOrTrash(list)) {
|
if (isListArchiveOrTrash(list)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -50,11 +59,11 @@ const ShortcutsProvider = React.memo(({ children }) => {
|
|||||||
|
|
||||||
const selectedCardRef = useRef(null);
|
const selectedCardRef = useRef(null);
|
||||||
|
|
||||||
const handleCardMouseEnter = useCallback((id, editName, archive) => {
|
const handleCardMouseEnter = useCallback((id, editName, openActions) => {
|
||||||
selectedCardRef.current = {
|
selectedCardRef.current = {
|
||||||
id,
|
id,
|
||||||
editName,
|
editName,
|
||||||
archive,
|
openActions,
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -128,7 +137,51 @@ const ShortcutsProvider = React.memo(({ children }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedCardRef.current.archive();
|
selectedCardRef.current.openActions(CardActionsStep.StepTypes.ARCHIVE);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCardMembers = () => {
|
||||||
|
if (!selectedCardRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = store.getState();
|
||||||
|
const card = selectors.selectCardById(state, selectedCardRef.current.id);
|
||||||
|
|
||||||
|
if (!card || !card.isPersisted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
|
const list = selectors.selectListById(state, card.listId);
|
||||||
|
|
||||||
|
if (!canUseCardMembers(boardMembership, list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedCardRef.current.openActions(CardActionsStep.StepTypes.MEMBERS);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCardLabels = () => {
|
||||||
|
if (!selectedCardRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = store.getState();
|
||||||
|
const card = selectors.selectCardById(state, selectedCardRef.current.id);
|
||||||
|
|
||||||
|
if (!card || !card.isPersisted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
|
const list = selectors.selectListById(state, card.listId);
|
||||||
|
|
||||||
|
if (!canUseCardLabels(boardMembership, list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedCardRef.current.openActions(CardActionsStep.StepTypes.LABELS);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLabelToCardAdd = (index) => {
|
const handleLabelToCardAdd = (index) => {
|
||||||
@@ -179,6 +232,16 @@ const ShortcutsProvider = React.memo(({ children }) => {
|
|||||||
case 'Enter':
|
case 'Enter':
|
||||||
handleCardOpen();
|
handleCardOpen();
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'KeyL':
|
||||||
|
event.preventDefault();
|
||||||
|
handleCardLabels();
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'KeyM':
|
||||||
|
event.preventDefault();
|
||||||
|
handleCardMembers();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'KeyT':
|
case 'KeyT':
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import ProjectContent from './ProjectContent';
|
|||||||
import StoryContent from './StoryContent';
|
import StoryContent from './StoryContent';
|
||||||
import InlineContent from './InlineContent';
|
import InlineContent from './InlineContent';
|
||||||
import EditName from './EditName';
|
import EditName from './EditName';
|
||||||
import ActionsStep from './ActionsStep';
|
import CardActionsStep from '../CardActionsStep';
|
||||||
|
|
||||||
import styles from './Card.module.scss';
|
import styles from './Card.module.scss';
|
||||||
import globalStyles from '../../../styles.module.scss';
|
import globalStyles from '../../../styles.module.scss';
|
||||||
@@ -69,11 +69,11 @@ const Card = React.memo(({ id, isInline }) => {
|
|||||||
() => {
|
() => {
|
||||||
setIsEditNameOpened(true);
|
setIsEditNameOpened(true);
|
||||||
},
|
},
|
||||||
() => {
|
(step) => {
|
||||||
closePopup();
|
closePopup();
|
||||||
|
|
||||||
actionsPopupRef.current.open({
|
actionsPopupRef.current.open({
|
||||||
defaultStep: ActionsStep.StepTypes.ARCHIVE,
|
defaultStep: step,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -98,7 +98,7 @@ const Card = React.memo(({ id, isInline }) => {
|
|||||||
setIsEditNameOpened(false);
|
setIsEditNameOpened(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const ActionsPopup = usePopup(ActionsStep);
|
const CardActionsPopup = usePopup(CardActionsStep);
|
||||||
|
|
||||||
if (isEditNameOpened) {
|
if (isEditNameOpened) {
|
||||||
return <EditName cardId={id} onClose={handleEditNameClose} />;
|
return <EditName cardId={id} onClose={handleEditNameClose} />;
|
||||||
@@ -149,11 +149,11 @@ const Card = React.memo(({ id, isInline }) => {
|
|||||||
{colorLineNode}
|
{colorLineNode}
|
||||||
</div>
|
</div>
|
||||||
{canUseActions && (
|
{canUseActions && (
|
||||||
<ActionsPopup ref={actionsPopupRef} cardId={id} onNameEdit={handleNameEdit}>
|
<CardActionsPopup ref={actionsPopupRef} cardId={id} onNameEdit={handleNameEdit}>
|
||||||
<Button className={styles.actionsButton}>
|
<Button className={styles.actionsButton}>
|
||||||
<Icon fitted name="pencil" size="small" />
|
<Icon fitted name="pencil" size="small" />
|
||||||
</Button>
|
</Button>
|
||||||
</ActionsPopup>
|
</CardActionsPopup>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
+29
-29
@@ -23,12 +23,12 @@ import ConfirmationStep from '../../common/ConfirmationStep';
|
|||||||
import BoardMembershipsStep from '../../board-memberships/BoardMembershipsStep';
|
import BoardMembershipsStep from '../../board-memberships/BoardMembershipsStep';
|
||||||
import LabelsStep from '../../labels/LabelsStep';
|
import LabelsStep from '../../labels/LabelsStep';
|
||||||
|
|
||||||
import styles from './ActionsStep.module.scss';
|
import styles from './CardActionsStep.module.scss';
|
||||||
|
|
||||||
const StepTypes = {
|
const StepTypes = {
|
||||||
EDIT_TYPE: 'EDIT_TYPE',
|
MEMBERS: 'MEMBERS',
|
||||||
USERS: 'USERS',
|
|
||||||
LABELS: 'LABELS',
|
LABELS: 'LABELS',
|
||||||
|
EDIT_TYPE: 'EDIT_TYPE',
|
||||||
EDIT_DUE_DATE: 'EDIT_DUE_DATE',
|
EDIT_DUE_DATE: 'EDIT_DUE_DATE',
|
||||||
EDIT_STOPWATCH: 'EDIT_STOPWATCH',
|
EDIT_STOPWATCH: 'EDIT_STOPWATCH',
|
||||||
MOVE: 'MOVE',
|
MOVE: 'MOVE',
|
||||||
@@ -36,7 +36,7 @@ const StepTypes = {
|
|||||||
DELETE: 'DELETE',
|
DELETE: 'DELETE',
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) => {
|
const CardActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) => {
|
||||||
const selectCardById = useMemo(() => selectors.makeSelectCardById(), []);
|
const selectCardById = useMemo(() => selectors.makeSelectCardById(), []);
|
||||||
const selectListById = useMemo(() => selectors.makeSelectListById(), []);
|
const selectListById = useMemo(() => selectors.makeSelectListById(), []);
|
||||||
const selectPrevListById = useMemo(() => selectors.makeSelectListById(), []);
|
const selectPrevListById = useMemo(() => selectors.makeSelectListById(), []);
|
||||||
@@ -180,18 +180,18 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
|
|||||||
onClose();
|
onClose();
|
||||||
}, [onNameEdit, onClose]);
|
}, [onNameEdit, onClose]);
|
||||||
|
|
||||||
const handleEditTypeClick = useCallback(() => {
|
const handleMembersClick = useCallback(() => {
|
||||||
openStep(StepTypes.EDIT_TYPE);
|
openStep(StepTypes.MEMBERS);
|
||||||
}, [openStep]);
|
|
||||||
|
|
||||||
const handleUsersClick = useCallback(() => {
|
|
||||||
openStep(StepTypes.USERS);
|
|
||||||
}, [openStep]);
|
}, [openStep]);
|
||||||
|
|
||||||
const handleLabelsClick = useCallback(() => {
|
const handleLabelsClick = useCallback(() => {
|
||||||
openStep(StepTypes.LABELS);
|
openStep(StepTypes.LABELS);
|
||||||
}, [openStep]);
|
}, [openStep]);
|
||||||
|
|
||||||
|
const handleEditTypeClick = useCallback(() => {
|
||||||
|
openStep(StepTypes.EDIT_TYPE);
|
||||||
|
}, [openStep]);
|
||||||
|
|
||||||
const handleEditDueDateClick = useCallback(() => {
|
const handleEditDueDateClick = useCallback(() => {
|
||||||
openStep(StepTypes.EDIT_DUE_DATE);
|
openStep(StepTypes.EDIT_DUE_DATE);
|
||||||
}, [openStep]);
|
}, [openStep]);
|
||||||
@@ -214,19 +214,7 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
|
|||||||
|
|
||||||
if (step) {
|
if (step) {
|
||||||
switch (step.type) {
|
switch (step.type) {
|
||||||
case StepTypes.EDIT_TYPE:
|
case StepTypes.MEMBERS:
|
||||||
return (
|
|
||||||
<SelectCardTypeStep
|
|
||||||
withButton
|
|
||||||
defaultValue={card.type}
|
|
||||||
title="common.editType"
|
|
||||||
buttonContent="action.save"
|
|
||||||
onSelect={handleTypeSelect}
|
|
||||||
onBack={handleBack}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case StepTypes.USERS:
|
|
||||||
return (
|
return (
|
||||||
<BoardMembershipsStep
|
<BoardMembershipsStep
|
||||||
currentUserIds={userIds}
|
currentUserIds={userIds}
|
||||||
@@ -245,6 +233,18 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
|
|||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case StepTypes.EDIT_TYPE:
|
||||||
|
return (
|
||||||
|
<SelectCardTypeStep
|
||||||
|
withButton
|
||||||
|
defaultValue={card.type}
|
||||||
|
title="common.editType"
|
||||||
|
buttonContent="action.save"
|
||||||
|
onSelect={handleTypeSelect}
|
||||||
|
onBack={handleBack}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case StepTypes.EDIT_DUE_DATE:
|
case StepTypes.EDIT_DUE_DATE:
|
||||||
return <EditDueDateStep cardId={cardId} onBack={handleBack} onClose={onClose} />;
|
return <EditDueDateStep cardId={cardId} onBack={handleBack} onClose={onClose} />;
|
||||||
case StepTypes.EDIT_STOPWATCH:
|
case StepTypes.EDIT_STOPWATCH:
|
||||||
@@ -305,7 +305,7 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
{card.type === CardTypes.PROJECT && canUseMembers && (
|
{card.type === CardTypes.PROJECT && canUseMembers && (
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleUsersClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleMembersClick}>
|
||||||
<Icon name="user outline" className={styles.menuItemIcon} />
|
<Icon name="user outline" className={styles.menuItemIcon} />
|
||||||
{t('common.members', {
|
{t('common.members', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
@@ -321,7 +321,7 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
{card.type === CardTypes.STORY && canUseMembers && (
|
{card.type === CardTypes.STORY && canUseMembers && (
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleUsersClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleMembersClick}>
|
||||||
<Icon name="user outline" className={styles.menuItemIcon} />
|
<Icon name="user outline" className={styles.menuItemIcon} />
|
||||||
{t('common.members', {
|
{t('common.members', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
@@ -395,17 +395,17 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ActionsStep.propTypes = {
|
CardActionsStep.propTypes = {
|
||||||
cardId: PropTypes.string.isRequired,
|
cardId: PropTypes.string.isRequired,
|
||||||
defaultStep: PropTypes.string,
|
defaultStep: PropTypes.string,
|
||||||
onNameEdit: PropTypes.func.isRequired,
|
onNameEdit: PropTypes.func.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
ActionsStep.defaultProps = {
|
CardActionsStep.defaultProps = {
|
||||||
defaultStep: undefined,
|
defaultStep: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
ActionsStep.StepTypes = StepTypes;
|
CardActionsStep.StepTypes = StepTypes;
|
||||||
|
|
||||||
export default ActionsStep;
|
export default CardActionsStep;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import CardActionsStep from './CardActionsStep';
|
||||||
|
|
||||||
|
export default CardActionsStep;
|
||||||
Reference in New Issue
Block a user