From 3126a40bba3df365eb50dc1629458e9c84e444c8 Mon Sep 17 00:00:00 2001 From: Maksim Eltyshev Date: Wed, 11 Jun 2025 00:14:42 +0200 Subject: [PATCH] feat: Add copy-to-clipboard for custom fields --- .../custom-fields/CustomField/CustomField.jsx | 44 ++++++++++++++----- .../CustomField/CustomField.module.scss | 34 ++++++++++++++ 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/client/src/components/custom-fields/CustomField/CustomField.jsx b/client/src/components/custom-fields/CustomField/CustomField.jsx index e9bd5480..3e811dd3 100644 --- a/client/src/components/custom-fields/CustomField/CustomField.jsx +++ b/client/src/components/custom-fields/CustomField/CustomField.jsx @@ -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 (
{customField.name}
- {canEdit ? ( - - ) : ( -
{customFieldValue ? customFieldValue.content : '\u00A0'}
- )} +
+ {canEdit ? ( + + ) : ( +
+ {customFieldValue ? customFieldValue.content : '\u00A0'} +
+ )} + {customFieldValue && customFieldValue.content && ( + + )} +
); }); diff --git a/client/src/components/custom-fields/CustomField/CustomField.module.scss b/client/src/components/custom-fields/CustomField/CustomField.module.scss index 50972e3b..7d82682b 100644 --- a/client/src/components/custom-fields/CustomField/CustomField.module.scss +++ b/client/src/components/custom-fields/CustomField/CustomField.module.scss @@ -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; + } + } + } }