on master help sign in
Merge pull request #258 from urbit/feat/tooltip-copy

feat: copy tooltip

commit92141adbde32859dcdd996fabe1b29831eef8c69 authorMatt Condon <mattgcondon@gmail.com> dateTue 27 Aug 2019, 17:38 (7 years ago) parent555fac954369ca
6 files changed+73−27
src/assets/copy.svg+7 src/components/CopiableAddress.js+3−18 src/components/CopiableWithTooltip.js+29 src/components/WithTooltip.js+23 src/views/Admin/AdminSetProxy.js+6−1 src/views/Points.js+5−8
src/assets/copy.svgnew file+7
@@ -0,0 +1,7 @@
1+<svg class="svg-icon" viewBox="0 0 20 20">
2+ <path fill="black" d="M18.378,1.062H3.855c-0.309,0-0.559,0.25-0.559,0.559c0,0.309,0.25,0.559,0.559,0.559h13.964v13.964
3+ c0,0.309,0.25,0.559,0.559,0.559c0.31,0,0.56-0.25,0.56-0.559V1.621C18.938,1.312,18.688,1.062,18.378,1.062z M16.144,3.296H1.621
4+ c-0.309,0-0.559,0.25-0.559,0.559v14.523c0,0.31,0.25,0.56,0.559,0.56h14.523c0.309,0,0.559-0.25,0.559-0.56V3.855
5+ C16.702,3.546,16.452,3.296,16.144,3.296z M15.586,17.262c0,0.31-0.25,0.558-0.56,0.558H2.738c-0.309,0-0.559-0.248-0.559-0.558
6+ V4.972c0-0.309,0.25-0.559,0.559-0.559h12.289c0.31,0,0.56,0.25,0.56,0.559V17.262z"></path>
7+</svg>
src/components/CopiableAddress.js+3−18
@@ -1,23 +1,8 @@
11import React from 'react';
22import cn from 'classnames';
33
4−import useCopiable from 'lib/useCopiable';
4+import CopiableWithTooltip from './CopiableWithTooltip';
55
6−export default function CopiableAddress({
7− as: As = 'span',
8− children,
9− className,
10−}) {
11− const [doCopy, didCopy] = useCopiable(children);
12−
13− return (
14− <As
15− onClick={doCopy}
16− className={cn(className, 'mono', {
17− pointer: !didCopy,
18− green3: didCopy,
19− })}>
20− {children}
21− </As>
22− );
6+export default function CopiableAddress({ className, ...rest }) {
7+ return <CopiableWithTooltip className={cn(className, 'mono')} {...rest} />;
238}
src/components/CopiableWithTooltip.jsnew file+29
@@ -0,0 +1,29 @@
1+import React from 'react';
2+import cn from 'classnames';
3+import { ReactComponent as Copy } from 'assets/copy.svg';
4+
5+import useCopiable from 'lib/useCopiable';
6+import WithTooltip from 'components/WithTooltip';
7+
8+export default function CopiableWithTooltip({
9+ as: As = 'span',
10+ text,
11+ children,
12+ className,
13+ ...rest
14+}) {
15+ const [doCopy, didCopy] = useCopiable(text || children);
16+
17+ return (
18+ <As className={cn(className, 'nowrap')} {...rest}>
19+ {children}
20+ <WithTooltip content={didCopy ? 'Copied!' : 'Copy'}>
21+ <Copy
22+ style={{ height: '1em', width: '1em' }}
23+ className="ml1 pointer"
24+ onClick={doCopy}
25+ />
26+ </WithTooltip>
27+ </As>
28+ );
29+}
src/components/WithTooltip.jsnew file+23
@@ -0,0 +1,23 @@
1+import React, { useState, useCallback } from 'react';
2+
3+export default function WithTooltip({ content, children }) {
4+ const [isHovered, setHovered] = useState(false);
5+ const onMouseEnter = useCallback(() => setHovered(true), []);
6+ const onMouseLeave = useCallback(() => setHovered(false), []);
7+
8+ return (
9+ <div
10+ className="rel nowrap inline-block"
11+ onMouseEnter={onMouseEnter}
12+ onMouseLeave={onMouseLeave}>
13+ {children}
14+ {isHovered && (
15+ <div
16+ className="abs mb1 bg-black white ph4 pv2 r4"
17+ style={{ bottom: '100%' }}>
18+ {content}
19+ </div>
20+ )}
21+ </div>
22+ );
23+}
src/views/Admin/AdminSetProxy.js+6−1
@@ -29,6 +29,7 @@ import {
2929} from 'form/validators';
3030import BridgeForm from 'form/BridgeForm';
3131import FormError from 'form/FormError';
32+import CopiableAddress from 'components/CopiableAddress';
3233
3334const proxyFromDetails = (details, contracts, proxyType) => {
3435 switch (proxyType) {
@@ -191,7 +192,11 @@ export default function AdminSetProxy() {
191192 gray4: !completed && !isProxySet,
192193 green3: completed,
193194 })}>
194− {isProxySet ? proxyAddress : 'Unset'}
195+ {isProxySet ? (
196+ <CopiableAddress>{proxyAddress}</CopiableAddress>
197+ ) : (
198+ 'Unset'
199+ )}
195200 </Flex.Item>
196201 {!completed && isProxySet && (
197202 <Flex.Item
src/views/Points.js+5−8
@@ -14,13 +14,13 @@ import useIsEclipticOwner from 'lib/useIsEclipticOwner';
1414import { useSyncKnownPoints } from 'lib/useSyncPoints';
1515import useRejectedIncomingPointTransfers from 'lib/useRejectedIncomingPointTransfers';
1616import pluralize from 'lib/pluralize';
17−import useCopiable from 'lib/useCopiable';
1817
1918import View from 'components/View';
2019import Blinky from 'components/Blinky';
2120import Passport from 'components/Passport';
2221import Footer from 'components/Footer';
2322import { ForwardButton } from 'components/Buttons';
23+import CopiableAddress from 'components/CopiableAddress';
2424
2525const maybeGetResult = (obj, key, defaultValue) =>
2626 obj.matchWith({
@@ -163,7 +163,6 @@ export default function Points() {
163163 ]);
164164
165165 const address = need.addressFromWallet(wallet);
166− const [doCopy, didCopy] = useCopiable(address);
167166
168167 const loading = Nothing.hasInstance(controlledPoints);
169168
@@ -210,12 +209,10 @@ export default function Points() {
210209 return (
211210 <View pop={pop} inset>
212211 <Grid>
213− <Grid.Item
214− full
215− as={H1}
216− className="f6 mono gray4 mb4 us-none clickable"
217− onClick={doCopy}>
218− {`${abbreviateAddress(address)}${didCopy ? ' (copied!)' : ''}`}
212+ <Grid.Item full as={H1} className="f6 mono gray4 mb4 us-none pointer">
213+ <CopiableAddress text={address}>
214+ {abbreviateAddress(address)}
215+ </CopiableAddress>
219216 </Grid.Item>
220217
221218 {loading && (