mirror of
https://github.com/plankanban/planka.git
synced 2025-07-18 12:49:43 +02:00
parent
758c2c3494
commit
800d2d012d
6 changed files with 93 additions and 2 deletions
16
client/package-lock.json
generated
16
client/package-lock.json
generated
|
@ -17,6 +17,8 @@
|
||||||
"initials": "^3.1.2",
|
"initials": "^3.1.2",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
|
"linkify-react": "^4.1.3",
|
||||||
|
"linkifyjs": "^4.1.3",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"nanoid": "^5.0.3",
|
"nanoid": "^5.0.3",
|
||||||
"node-sass": "^9.0.0",
|
"node-sass": "^9.0.0",
|
||||||
|
@ -11911,6 +11913,20 @@
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/linkify-react": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/linkify-react/-/linkify-react-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-rhI3zM/fxn5BfRPHfi4r9N7zgac4vOIxub1wHIWXLA5ENTMs+BGaIaFO1D1PhmxgwhIKmJz3H7uCP0Dg5JwSlA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"linkifyjs": "^4.0.0",
|
||||||
|
"react": ">= 15.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/linkifyjs": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg=="
|
||||||
|
},
|
||||||
"node_modules/loader-runner": {
|
"node_modules/loader-runner": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
||||||
|
|
|
@ -70,6 +70,8 @@
|
||||||
"initials": "^3.1.2",
|
"initials": "^3.1.2",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
|
"linkify-react": "^4.1.3",
|
||||||
|
"linkifyjs": "^4.1.3",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"nanoid": "^5.0.3",
|
"nanoid": "^5.0.3",
|
||||||
"node-sass": "^9.0.0",
|
"node-sass": "^9.0.0",
|
||||||
|
|
|
@ -4,6 +4,8 @@ import classNames from 'classnames';
|
||||||
import { Progress } from 'semantic-ui-react';
|
import { Progress } from 'semantic-ui-react';
|
||||||
import { useToggle } from '../../lib/hooks';
|
import { useToggle } from '../../lib/hooks';
|
||||||
|
|
||||||
|
import Linkify from '../Linkify';
|
||||||
|
|
||||||
import styles from './Tasks.module.scss';
|
import styles from './Tasks.module.scss';
|
||||||
|
|
||||||
const Tasks = React.memo(({ items }) => {
|
const Tasks = React.memo(({ items }) => {
|
||||||
|
@ -48,7 +50,7 @@ const Tasks = React.memo(({ items }) => {
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={classNames(styles.task, item.isCompleted && styles.taskCompleted)}
|
className={classNames(styles.task, item.isCompleted && styles.taskCompleted)}
|
||||||
>
|
>
|
||||||
{item.name}
|
<Linkify linkStopPropagation>{item.name}</Linkify>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -55,8 +55,10 @@
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
|
overflow: hidden;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
padding-left: 14px;
|
padding-left: 14px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "–";
|
content: "–";
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { usePopup } from '../../../lib/popup';
|
||||||
|
|
||||||
import NameEdit from './NameEdit';
|
import NameEdit from './NameEdit';
|
||||||
import ActionsStep from './ActionsStep';
|
import ActionsStep from './ActionsStep';
|
||||||
|
import Linkify from '../../Linkify';
|
||||||
|
|
||||||
import styles from './Item.module.scss';
|
import styles from './Item.module.scss';
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ const Item = React.memo(
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<span className={classNames(styles.task, isCompleted && styles.taskCompleted)}>
|
<span className={classNames(styles.task, isCompleted && styles.taskCompleted)}>
|
||||||
{name}
|
<Linkify linkStopPropagation>{name}</Linkify>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
{isPersisted && canEdit && (
|
{isPersisted && canEdit && (
|
||||||
|
|
68
client/src/components/Linkify.jsx
Normal file
68
client/src/components/Linkify.jsx
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import LinkifyReact from 'linkify-react';
|
||||||
|
|
||||||
|
import history from '../history';
|
||||||
|
|
||||||
|
const Linkify = React.memo(({ children, linkStopPropagation, ...props }) => {
|
||||||
|
const handleLinkClick = useCallback(
|
||||||
|
(event) => {
|
||||||
|
if (linkStopPropagation) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event.target.getAttribute('target')) {
|
||||||
|
event.preventDefault();
|
||||||
|
history.push(event.target.href);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[linkStopPropagation],
|
||||||
|
);
|
||||||
|
|
||||||
|
const linkRenderer = useCallback(
|
||||||
|
({ attributes: { href, ...linkProps }, content }) => {
|
||||||
|
let url;
|
||||||
|
try {
|
||||||
|
url = new URL(href, window.location);
|
||||||
|
} catch (error) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
const isSameSite = !!url && url.origin === window.location.origin;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
{...linkProps} // eslint-disable-line react/jsx-props-no-spreading
|
||||||
|
href={href}
|
||||||
|
target={isSameSite ? undefined : '_blank'}
|
||||||
|
rel={isSameSite ? undefined : 'noreferrer'}
|
||||||
|
onClick={handleLinkClick}
|
||||||
|
>
|
||||||
|
{isSameSite ? url.pathname : content}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[handleLinkClick],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LinkifyReact
|
||||||
|
{...props} // eslint-disable-line react/jsx-props-no-spreading
|
||||||
|
options={{
|
||||||
|
defaultProtocol: 'https',
|
||||||
|
render: linkRenderer,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</LinkifyReact>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Linkify.propTypes = {
|
||||||
|
children: PropTypes.string.isRequired,
|
||||||
|
linkStopPropagation: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
Linkify.defaultProps = {
|
||||||
|
linkStopPropagation: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Linkify;
|
Loading…
Add table
Add a link
Reference in a new issue