1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
import { useCallback } from 'react';
import styled from 'styled-components';
import { colors } from '../../config.json';
import { messages } from '../../shared/gettext';
import log from '../../shared/logging';
import { useScheduler } from '../../shared/scheduler';
import { useBoolean } from '../lib/utilityHooks';
import ImageView from './ImageView';
const COPIED_ICON_DURATION = 2000;
interface IProps {
value: string;
displayValue?: string;
obscureValue?: boolean;
message?: string;
className?: string;
}
const StyledLabelContainer = styled.div({
display: 'flex',
flex: 1,
height: '19px',
alignItems: 'center',
});
const StyledLabel = styled.span({
flex: 1,
});
const StyledButton = styled.button({
cursor: 'default',
padding: 0,
marginLeft: '20px',
backgroundColor: 'transparent',
border: 'none',
});
const StyledCopyButton = styled(StyledButton)({
width: '24px',
});
export default function ClipboardLabel(props: IProps) {
const [obscured, , , toggleObscured] = useBoolean(props.obscureValue ?? true);
const [justCopied, setJustCopied, resetJustCopied] = useBoolean(false);
const copiedScheduler = useScheduler();
const onCopy = useCallback(async () => {
try {
await navigator.clipboard.writeText(props.value);
copiedScheduler.schedule(resetJustCopied, COPIED_ICON_DURATION);
setJustCopied();
} catch (e) {
const error = e as Error;
log.error(`Failed to copy to clipboard: ${error.message}`);
}
}, [props.value, copiedScheduler, setJustCopied, resetJustCopied]);
const value = props.displayValue ?? props.value;
return (
<StyledLabelContainer>
<StyledLabel className={props.className} aria-hidden={obscured}>
{obscured ? '●●●● ●●●● ●●●● ●●●●' : value}
</StyledLabel>
{props.obscureValue !== false && (
<StyledButton
onClick={toggleObscured}
aria-label={
obscured
? // This line is here to prevent the following one to be moved up here by prettier
// TRANSLATORS: Provided to accessibility tools such as screenreaders to describe
// TRANSLATORS: the button which unobscures the account number.
messages.pgettext('accessibility', 'Show account number')
: // This line is here to prevent the following one to be moved up here by prettier
// TRANSLATORS: Provided to accessibility tools such as screenreaders to describe
// TRANSLATORS: the button which obscures the account number.
messages.pgettext('accessibility', 'Hide account number')
}>
<ImageView
source={obscured ? 'icon-unobscure' : 'icon-obscure'}
tintColor={colors.white}
tintHoverColor={colors.white80}
width={24}
/>
</StyledButton>
)}
<StyledCopyButton
onClick={onCopy}
aria-label={
// TRANSLATORS: Provided to accessibility tools such as screenreaders to describe a button
// TRANSLATORS: which copies the account number to the clipboard.
messages.pgettext('accessibility', 'Copy account number')
}>
<ImageView
source={justCopied ? 'icon-tick' : 'icon-copy'}
tintColor={justCopied ? colors.green : colors.white}
tintHoverColor={justCopied ? colors.green : colors.white80}
width={justCopied ? 22 : 24}
/>
</StyledCopyButton>
</StyledLabelContainer>
);
}
|