1
0
Fork 0
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:
HannesOberreiter 2024-04-09 11:00:02 +02:00
parent b46fb43e6f
commit f2072acfc0
3 changed files with 31 additions and 8 deletions

View file

@ -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",

View file

@ -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",

View file

@ -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}>