1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-26 13:48:33 +02:00

feat: move 'copy flag name' button (#9805)

- moved "copy flag name" action next to the flag name
- refactored this component into a separate file
- added "Ctrl+C" shortcut
This commit is contained in:
Tymoteusz Czech 2025-04-22 11:12:37 +02:00 committed by GitHub
parent bc7856a23a
commit 18850a5156
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 93 additions and 17 deletions

View File

@ -0,0 +1,61 @@
import { useState, type FC } from 'react';
import copy from 'copy-to-clipboard';
import useToast from 'hooks/useToast';
import { useKeyboardCopy } from 'hooks/useKeyboardCopy';
import { IconButton, Tooltip } from '@mui/material';
import Check from '@mui/icons-material/Check';
import FileCopyOutlined from '@mui/icons-material/FileCopyOutlined';
const iconSize = 18 / 8;
export const FeatureCopyName: FC<{ name: string }> = ({ name }) => {
const [isFeatureNameCopied, setIsFeatureNameCopied] = useState(false);
const { setToastData } = useToast();
const handleCopyToClipboard = () => {
try {
copy(name);
setIsFeatureNameCopied(true);
const timeout = setTimeout(() => {
setIsFeatureNameCopied(false);
}, 3000);
return () => {
clearTimeout(timeout);
};
} catch (error: unknown) {
setToastData({
type: 'error',
text: 'Could not copy feature name',
});
}
};
const shortcutDescription = useKeyboardCopy(handleCopyToClipboard);
return (
<Tooltip
title={
isFeatureNameCopied
? 'Copied!'
: `Copy name (${shortcutDescription})`
}
arrow
>
<IconButton onClick={handleCopyToClipboard}>
{isFeatureNameCopied ? (
<Check
sx={(theme) => ({
fontSize: theme.spacing(iconSize),
color: theme.palette.success.main,
})}
/>
) : (
<FileCopyOutlined
sx={(theme) => ({ fontSize: theme.spacing(iconSize) })}
/>
)}
</IconButton>
</Tooltip>
);
};

View File

@ -12,7 +12,6 @@ import {
import Archive from '@mui/icons-material/Archive';
import ArchiveOutlined from '@mui/icons-material/ArchiveOutlined';
import FileCopy from '@mui/icons-material/FileCopy';
import FileCopyOutlined from '@mui/icons-material/FileCopyOutlined';
import Label from '@mui/icons-material/Label';
import WatchLater from '@mui/icons-material/WatchLater';
import WatchLaterOutlined from '@mui/icons-material/WatchLaterOutlined';
@ -45,6 +44,7 @@ import { ManageTagsDialog } from './FeatureOverview/ManageTagsDialog/ManageTagsD
import { FeatureStaleDialog } from 'component/common/FeatureStaleDialog/FeatureStaleDialog';
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
import { FeatureArchiveNotAllowedDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveNotAllowedDialog';
import { FeatureCopyName } from './FeatureCopyName/FeatureCopyName';
const NewStyledHeader = styled('div')(({ theme }) => ({
backgroundColor: 'none',
@ -64,7 +64,13 @@ const UpperHeaderRow = styled('div')(({ theme }) => ({
display: 'flex',
flexFlow: 'row wrap',
alignItems: 'center',
columnGap: theme.spacing(2),
columnGap: theme.spacing(1.5),
}));
const StyledTitle = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
columnGap: theme.spacing(0.5),
}));
const LowerHeaderRow = styled(UpperHeaderRow)(({ theme }) => ({
@ -210,8 +216,6 @@ type HeaderActionsProps = {
feature: IFeatureToggle;
showOnNarrowScreens?: boolean;
onFavorite: () => void;
handleCopyToClipboard: () => void;
isFeatureNameCopied: boolean;
openStaleDialog: () => void;
openDeleteDialog: () => void;
};
@ -220,8 +224,6 @@ const HeaderActionsComponent = ({
showOnNarrowScreens,
feature,
onFavorite,
handleCopyToClipboard,
isFeatureNameCopied,
openStaleDialog,
openDeleteDialog,
}: HeaderActionsProps) => (
@ -233,14 +235,6 @@ const HeaderActionsComponent = ({
>
{feature.favorite ? <Star /> : <StarBorder />}
</IconButtonWithTooltip>
<IconButtonWithTooltip
label='Copy flag name'
onClick={handleCopyToClipboard}
data-loading
>
{isFeatureNameCopied ? <Check /> : <FileCopyOutlined />}
</IconButtonWithTooltip>
<PermissionIconButton
permission={CREATE_FEATURE}
projectId={feature.project}
@ -373,8 +367,6 @@ export const FeatureViewHeader: FC<Props> = ({ feature }) => {
showOnNarrowScreens={showOnNarrowScreens}
feature={feature}
onFavorite={onFavorite}
handleCopyToClipboard={handleCopyToClipboard}
isFeatureNameCopied={isFeatureNameCopied}
openStaleDialog={() => setOpenStaleDialog(true)}
openDeleteDialog={() => setShowDelDialog(true)}
/>
@ -386,7 +378,11 @@ export const FeatureViewHeader: FC<Props> = ({ feature }) => {
{flagOverviewRedesign ? (
<NewStyledHeader>
<UpperHeaderRow>
<Typography variant='h1'>{feature.name}</Typography>
<StyledTitle>
<Typography variant='h1'>{feature.name}</Typography>
<FeatureCopyName name={feature.name} />
</StyledTitle>
<FeatureStatusChip stale={true} />
{feature.stale ? (
<FeatureStatusChip stale={true} />
) : null}

View File

@ -0,0 +1,19 @@
import { useKeyboardShortcut } from './useKeyboardShortcut';
export const useKeyboardCopy = (handler: () => void) =>
useKeyboardShortcut(
{
key: 'c',
modifiers: ['ctrl'],
preventDefault: false,
},
() => {
const selection = window.getSelection?.();
if (
selection &&
(selection.type === 'None' || selection.type === 'Caret')
) {
handler();
}
},
);