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:
parent
bc7856a23a
commit
18850a5156
@ -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>
|
||||
);
|
||||
};
|
@ -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}
|
||||
|
19
frontend/src/hooks/useKeyboardCopy.ts
Normal file
19
frontend/src/hooks/useKeyboardCopy.ts
Normal 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();
|
||||
}
|
||||
},
|
||||
);
|
Loading…
Reference in New Issue
Block a user