mirror of
https://github.com/plankanban/planka.git
synced 2025-08-10 07:55:27 +02:00
feat: ✨ add a link parser to tasks
This commit is contained in:
parent
b46fb43e6f
commit
f2072acfc0
3 changed files with 31 additions and 8 deletions
6
client/package-lock.json
generated
6
client/package-lock.json
generated
|
@ -10,6 +10,7 @@
|
|||
"classnames": "^2.3.2",
|
||||
"date-fns": "^2.30.0",
|
||||
"dequal": "^2.0.3",
|
||||
"dompurify": "^3.1.0",
|
||||
"easymde": "^2.18.0",
|
||||
"history": "^5.3.0",
|
||||
"i18next": "^23.7.6",
|
||||
|
@ -7016,6 +7017,11 @@
|
|||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.0.tgz",
|
||||
"integrity": "sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA=="
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
"classnames": "^2.3.2",
|
||||
"date-fns": "^2.30.0",
|
||||
"dequal": "^2.0.3",
|
||||
"dompurify": "^3.1.0",
|
||||
"easymde": "^2.18.0",
|
||||
"history": "^5.3.0",
|
||||
"i18next": "^23.7.6",
|
||||
|
|
|
@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
|||
import classNames from 'classnames';
|
||||
import { Draggable } from 'react-beautiful-dnd';
|
||||
import { Button, Checkbox, Icon } from 'semantic-ui-react';
|
||||
import { sanitize } from 'dompurify';
|
||||
import { usePopup } from '../../../lib/popup';
|
||||
|
||||
import NameEdit from './NameEdit';
|
||||
|
@ -15,11 +16,14 @@ const Item = React.memo(
|
|||
({ id, index, name, isCompleted, isPersisted, canEdit, onUpdate, onDelete }) => {
|
||||
const nameEdit = useRef(null);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (isPersisted && canEdit) {
|
||||
nameEdit.current.open();
|
||||
}
|
||||
}, [isPersisted, canEdit]);
|
||||
const handleClick = useCallback(
|
||||
(event) => {
|
||||
if (!event.target.closest('a') && isPersisted && canEdit) {
|
||||
nameEdit.current.open();
|
||||
}
|
||||
},
|
||||
[isPersisted, canEdit],
|
||||
);
|
||||
|
||||
const handleNameUpdate = useCallback(
|
||||
(newName) => {
|
||||
|
@ -40,6 +44,16 @@ const Item = React.memo(
|
|||
nameEdit.current.open();
|
||||
}, []);
|
||||
|
||||
const parseLinks = (text) => {
|
||||
const regex = /(http[s]?:\/\/[^\s]+)/g;
|
||||
return sanitize(text).replace(regex, (match) => {
|
||||
const url = new URL(match);
|
||||
return `<a href="${match}" target="_blank">${
|
||||
url.hostname === window.location.hostname ? url.pathname : match
|
||||
}</a>`;
|
||||
});
|
||||
};
|
||||
|
||||
const ActionsPopup = usePopup(ActionsStep);
|
||||
|
||||
return (
|
||||
|
@ -64,9 +78,11 @@ const Item = React.memo(
|
|||
className={classNames(styles.text, canEdit && styles.textEditable)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<span className={classNames(styles.task, isCompleted && styles.taskCompleted)}>
|
||||
{name}
|
||||
</span>
|
||||
<span
|
||||
className={classNames(styles.task, isCompleted && styles.taskCompleted)}
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{ __html: parseLinks(name) }}
|
||||
/>
|
||||
</span>
|
||||
{isPersisted && canEdit && (
|
||||
<ActionsPopup onNameEdit={handleNameEdit} onDelete={onDelete}>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue