feat: Extend card action shortcuts

This commit is contained in:
Maksim Eltyshev
2025-11-24 15:02:26 +01:00
parent 561d2c77d4
commit c68ab99bfe
5 changed files with 104 additions and 38 deletions

View File

@@ -18,6 +18,7 @@ import { isModifierKeyPressed } from '../../../utils/event-helpers';
import { BoardShortcutsContext } from '../../../contexts';
import Paths from '../../../constants/Paths';
import { BoardMembershipRoles, ListTypes } from '../../../constants/Enums';
import CardActionsStep from '../../cards/CardActionsStep';
const canEditCardName = (boardMembership, list) => {
if (isListArchiveOrTrash(list)) {
@@ -35,6 +36,14 @@ const canArchiveCard = (boardMembership, list) => {
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) => {
if (isListArchiveOrTrash(list)) {
return false;
@@ -50,11 +59,11 @@ const ShortcutsProvider = React.memo(({ children }) => {
const selectedCardRef = useRef(null);
const handleCardMouseEnter = useCallback((id, editName, archive) => {
const handleCardMouseEnter = useCallback((id, editName, openActions) => {
selectedCardRef.current = {
id,
editName,
archive,
openActions,
};
}, []);
@@ -128,7 +137,51 @@ const ShortcutsProvider = React.memo(({ children }) => {
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) => {
@@ -179,6 +232,16 @@ const ShortcutsProvider = React.memo(({ children }) => {
case 'Enter':
handleCardOpen();
break;
case 'KeyL':
event.preventDefault();
handleCardLabels();
break;
case 'KeyM':
event.preventDefault();
handleCardMembers();
break;
case 'KeyT':
event.preventDefault();

View File

@@ -21,7 +21,7 @@ import ProjectContent from './ProjectContent';
import StoryContent from './StoryContent';
import InlineContent from './InlineContent';
import EditName from './EditName';
import ActionsStep from './ActionsStep';
import CardActionsStep from '../CardActionsStep';
import styles from './Card.module.scss';
import globalStyles from '../../../styles.module.scss';
@@ -69,11 +69,11 @@ const Card = React.memo(({ id, isInline }) => {
() => {
setIsEditNameOpened(true);
},
() => {
(step) => {
closePopup();
actionsPopupRef.current.open({
defaultStep: ActionsStep.StepTypes.ARCHIVE,
defaultStep: step,
});
},
);
@@ -98,7 +98,7 @@ const Card = React.memo(({ id, isInline }) => {
setIsEditNameOpened(false);
}, []);
const ActionsPopup = usePopup(ActionsStep);
const CardActionsPopup = usePopup(CardActionsStep);
if (isEditNameOpened) {
return <EditName cardId={id} onClose={handleEditNameClose} />;
@@ -149,11 +149,11 @@ const Card = React.memo(({ id, isInline }) => {
{colorLineNode}
</div>
{canUseActions && (
<ActionsPopup ref={actionsPopupRef} cardId={id} onNameEdit={handleNameEdit}>
<CardActionsPopup ref={actionsPopupRef} cardId={id} onNameEdit={handleNameEdit}>
<Button className={styles.actionsButton}>
<Icon fitted name="pencil" size="small" />
</Button>
</ActionsPopup>
</CardActionsPopup>
)}
</>
) : (

View File

@@ -23,12 +23,12 @@ import ConfirmationStep from '../../common/ConfirmationStep';
import BoardMembershipsStep from '../../board-memberships/BoardMembershipsStep';
import LabelsStep from '../../labels/LabelsStep';
import styles from './ActionsStep.module.scss';
import styles from './CardActionsStep.module.scss';
const StepTypes = {
EDIT_TYPE: 'EDIT_TYPE',
USERS: 'USERS',
MEMBERS: 'MEMBERS',
LABELS: 'LABELS',
EDIT_TYPE: 'EDIT_TYPE',
EDIT_DUE_DATE: 'EDIT_DUE_DATE',
EDIT_STOPWATCH: 'EDIT_STOPWATCH',
MOVE: 'MOVE',
@@ -36,7 +36,7 @@ const StepTypes = {
DELETE: 'DELETE',
};
const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) => {
const CardActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) => {
const selectCardById = useMemo(() => selectors.makeSelectCardById(), []);
const selectListById = useMemo(() => selectors.makeSelectListById(), []);
const selectPrevListById = useMemo(() => selectors.makeSelectListById(), []);
@@ -180,18 +180,18 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
onClose();
}, [onNameEdit, onClose]);
const handleEditTypeClick = useCallback(() => {
openStep(StepTypes.EDIT_TYPE);
}, [openStep]);
const handleUsersClick = useCallback(() => {
openStep(StepTypes.USERS);
const handleMembersClick = useCallback(() => {
openStep(StepTypes.MEMBERS);
}, [openStep]);
const handleLabelsClick = useCallback(() => {
openStep(StepTypes.LABELS);
}, [openStep]);
const handleEditTypeClick = useCallback(() => {
openStep(StepTypes.EDIT_TYPE);
}, [openStep]);
const handleEditDueDateClick = useCallback(() => {
openStep(StepTypes.EDIT_DUE_DATE);
}, [openStep]);
@@ -214,19 +214,7 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
if (step) {
switch (step.type) {
case StepTypes.EDIT_TYPE:
return (
<SelectCardTypeStep
withButton
defaultValue={card.type}
title="common.editType"
buttonContent="action.save"
onSelect={handleTypeSelect}
onBack={handleBack}
onClose={onClose}
/>
);
case StepTypes.USERS:
case StepTypes.MEMBERS:
return (
<BoardMembershipsStep
currentUserIds={userIds}
@@ -245,6 +233,18 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
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:
return <EditDueDateStep cardId={cardId} onBack={handleBack} onClose={onClose} />;
case StepTypes.EDIT_STOPWATCH:
@@ -305,7 +305,7 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
</Menu.Item>
)}
{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} />
{t('common.members', {
context: 'title',
@@ -321,7 +321,7 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
</Menu.Item>
)}
{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} />
{t('common.members', {
context: 'title',
@@ -395,17 +395,17 @@ const ActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) =>
);
});
ActionsStep.propTypes = {
CardActionsStep.propTypes = {
cardId: PropTypes.string.isRequired,
defaultStep: PropTypes.string,
onNameEdit: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
ActionsStep.defaultProps = {
CardActionsStep.defaultProps = {
defaultStep: undefined,
};
ActionsStep.StepTypes = StepTypes;
CardActionsStep.StepTypes = StepTypes;
export default ActionsStep;
export default CardActionsStep;

View File

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