mirror of
https://github.com/plankanban/planka.git
synced 2025-07-18 20:59:44 +02:00
feat: Open card actions on right-click
This commit is contained in:
parent
e659ed4a2d
commit
fdac299fc7
2 changed files with 33 additions and 7 deletions
|
@ -5,13 +5,13 @@
|
||||||
|
|
||||||
import upperFirst from 'lodash/upperFirst';
|
import upperFirst from 'lodash/upperFirst';
|
||||||
import camelCase from 'lodash/camelCase';
|
import camelCase from 'lodash/camelCase';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { Button, Icon } from 'semantic-ui-react';
|
import { Button, Icon } from 'semantic-ui-react';
|
||||||
import { push } from '../../../lib/redux-router';
|
import { push } from '../../../lib/redux-router';
|
||||||
import { usePopup } from '../../../lib/popup';
|
import { closePopup, usePopup } from '../../../lib/popup';
|
||||||
|
|
||||||
import selectors from '../../../selectors';
|
import selectors from '../../../selectors';
|
||||||
import Paths from '../../../constants/Paths';
|
import Paths from '../../../constants/Paths';
|
||||||
|
@ -51,6 +51,8 @@ const Card = React.memo(({ id, isInline }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [isEditNameOpened, setIsEditNameOpened] = useState(false);
|
const [isEditNameOpened, setIsEditNameOpened] = useState(false);
|
||||||
|
|
||||||
|
const actionsPopupRef = useRef(null);
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
if (document.activeElement) {
|
if (document.activeElement) {
|
||||||
document.activeElement.blur();
|
document.activeElement.blur();
|
||||||
|
@ -59,6 +61,17 @@ const Card = React.memo(({ id, isInline }) => {
|
||||||
dispatch(push(Paths.CARDS.replace(':id', id)));
|
dispatch(push(Paths.CARDS.replace(':id', id)));
|
||||||
}, [id, dispatch]);
|
}, [id, dispatch]);
|
||||||
|
|
||||||
|
const handleContextMenu = useCallback((event) => {
|
||||||
|
if (!actionsPopupRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
closePopup();
|
||||||
|
actionsPopupRef.current.open();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleNameEdit = useCallback(() => {
|
const handleNameEdit = useCallback(() => {
|
||||||
setIsEditNameOpened(true);
|
setIsEditNameOpened(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -110,12 +123,13 @@ const Card = React.memo(({ id, isInline }) => {
|
||||||
<div
|
<div
|
||||||
className={classNames(styles.content, card.isClosed && styles.contentDisabled)}
|
className={classNames(styles.content, card.isClosed && styles.contentDisabled)}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
onContextMenu={handleContextMenu}
|
||||||
>
|
>
|
||||||
<Content cardId={id} />
|
<Content cardId={id} />
|
||||||
{colorLineNode}
|
{colorLineNode}
|
||||||
</div>
|
</div>
|
||||||
{canUseActions && (
|
{canUseActions && (
|
||||||
<ActionsPopup cardId={id} onNameEdit={handleNameEdit}>
|
<ActionsPopup 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>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ResizeObserver } from '@juggle/resize-observer';
|
import { ResizeObserver } from '@juggle/resize-observer';
|
||||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Button, Popup as SemanticUIPopup } from 'semantic-ui-react';
|
import { Button, Popup as SemanticUIPopup } from 'semantic-ui-react';
|
||||||
|
|
||||||
|
@ -12,13 +12,13 @@ import styles from './Popup.module.css';
|
||||||
|
|
||||||
export default (Step, { position, onOpen, onClose } = {}) => {
|
export default (Step, { position, onOpen, onClose } = {}) => {
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const Popup = React.memo(({ children, ...stepProps }) => {
|
const Popup = React.forwardRef(({ children, ...stepProps }, ref) => {
|
||||||
const [isOpened, setIsOpened] = useState(false);
|
const [isOpened, setIsOpened] = useState(false);
|
||||||
|
|
||||||
const wrapperRef = useRef(null);
|
const wrapperRef = useRef(null);
|
||||||
const resizeObserverRef = useRef(null);
|
const resizeObserverRef = useRef(null);
|
||||||
|
|
||||||
const handleOpen = useCallback(() => {
|
const open = useCallback(() => {
|
||||||
setIsOpened(true);
|
setIsOpened(true);
|
||||||
|
|
||||||
if (onOpen) {
|
if (onOpen) {
|
||||||
|
@ -26,6 +26,10 @@ export default (Step, { position, onOpen, onClose } = {}) => {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleOpen = useCallback(() => {
|
||||||
|
open();
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setIsOpened(false);
|
setIsOpened(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -73,6 +77,14 @@ export default (Step, { position, onOpen, onClose } = {}) => {
|
||||||
resizeObserverRef.current.observe(element);
|
resizeObserverRef.current.observe(element);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
open,
|
||||||
|
}),
|
||||||
|
[open],
|
||||||
|
);
|
||||||
|
|
||||||
const tigger = React.cloneElement(children, {
|
const tigger = React.cloneElement(children, {
|
||||||
onClick: handleTriggerClick,
|
onClick: handleTriggerClick,
|
||||||
});
|
});
|
||||||
|
@ -116,6 +128,6 @@ export default (Step, { position, onOpen, onClose } = {}) => {
|
||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Popup;
|
return React.memo(Popup);
|
||||||
}, [position, onOpen, onClose]);
|
}, [position, onOpen, onClose]);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue