1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-19 05:09:43 +02:00

feat: Add copy-to-clipboard for custom fields

This commit is contained in:
Maksim Eltyshev 2025-06-11 00:14:42 +02:00
parent 99a06ce1ae
commit 3126a40bba
2 changed files with 68 additions and 10 deletions

View file

@ -3,9 +3,10 @@
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { Button, Icon } from 'semantic-ui-react';
import selectors from '../../../selectors';
import entryActions from '../../../entry-actions';
@ -48,6 +49,7 @@ const CustomField = React.memo(({ id, customFieldGroupId }) => {
});
const dispatch = useDispatch();
const [isCopied, setIsCopied] = useState(false);
const handleValueUpdate = useCallback(
(content) => {
@ -64,18 +66,40 @@ const CustomField = React.memo(({ id, customFieldGroupId }) => {
[id, customFieldGroupId, cardId, dispatch],
);
const handleCopyClick = useCallback(() => {
if (isCopied) {
return;
}
navigator.clipboard.writeText(customFieldValue.content);
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1000);
}, [customFieldValue, isCopied]);
return (
<div>
<div className={styles.name}>{customField.name}</div>
{canEdit ? (
<ValueField
defaultValue={customFieldValue && customFieldValue.content}
disabled={!customField.isPersisted}
onUpdate={handleValueUpdate}
/>
) : (
<div className={styles.value}>{customFieldValue ? customFieldValue.content : '\u00A0'}</div>
)}
<div className={styles.valueWrapper}>
{canEdit ? (
<ValueField
defaultValue={customFieldValue && customFieldValue.content}
disabled={!customField.isPersisted}
onUpdate={handleValueUpdate}
/>
) : (
<div className={styles.value}>
{customFieldValue ? customFieldValue.content : '\u00A0'}
</div>
)}
{customFieldValue && customFieldValue.content && (
<Button className={styles.copyButton} onClick={handleCopyClick}>
<Icon fitted name={isCopied ? 'check' : 'copy'} />
</Button>
)}
</div>
</div>
);
});

View file

@ -4,6 +4,30 @@
*/
:global(#app) {
.copyButton {
background: #ebeef0;
box-shadow: none;
border-radius: 3px;
box-sizing: content-box;
color: #516b7a;
display: none;
height: 30px;
margin: 0;
min-height: auto;
outline: none;
padding: 4px;
position: absolute;
right: 0;
top: 0;
transition: background 85ms ease;
width: 20px;
&:hover {
background: #dfe3e6;
color: #4c4c4c;
}
}
.name {
color: #6b808c;
font-size: 13px;
@ -25,4 +49,14 @@
padding: 8px 12px;
text-overflow: ellipsis;
}
.valueWrapper {
position: relative;
&:hover:not(:has(input:focus)) {
.copyButton {
display: block;
}
}
}
}