Skip to content

Commit 8a6db7f

Browse files
Create a CopyToClipboardButton component and use it for all copy to clipboard buttons to have a feedback with a tooltip after click (#845)
1 parent 01b7d43 commit 8a6db7f

File tree

4 files changed

+81
-30
lines changed

4 files changed

+81
-30
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { faClipboard } from '@fortawesome/free-solid-svg-icons';
4+
import { AwesomeIcon } from './AwesomeIcon';
5+
import copy from 'clipboard-copy';
6+
7+
export default class CopyToClipboardButton extends React.Component {
8+
static propTypes = {
9+
text: PropTypes.func.isRequired,
10+
title: PropTypes.string.isRequired,
11+
message: PropTypes.string.isRequired
12+
};
13+
14+
state = { copied: false };
15+
buttonRef = React.createRef();
16+
17+
onClick = (event) => {
18+
event.preventDefault();
19+
event.stopPropagation();
20+
copy(this.props.text());
21+
this.setState({ copied: true });
22+
setTimeout(() => {
23+
this.setState({ copied: false });
24+
}, 2000);
25+
};
26+
27+
render() {
28+
let tooltipStyle = null;
29+
if (this.state.copied && this.buttonRef.current) {
30+
const rect = this.buttonRef.current.getBoundingClientRect();
31+
const centerX = rect.left + rect.width / 2;
32+
const isRightHalf = centerX > window.innerWidth / 2;
33+
tooltipStyle = {
34+
position: 'fixed',
35+
top: rect.bottom + 5 + 'px'
36+
};
37+
if (isRightHalf) {
38+
tooltipStyle.right = window.innerWidth - rect.right + 'px';
39+
} else {
40+
tooltipStyle.left = rect.left + 'px';
41+
}
42+
}
43+
44+
return (
45+
<span ref={this.buttonRef}>
46+
<a title={this.props.title} className="button" onClick={this.onClick}>
47+
<AwesomeIcon icon={faClipboard} />
48+
</a>
49+
{this.state.copied && (
50+
<div className="copy-tooltip" style={tooltipStyle}>
51+
{this.props.message}
52+
</div>
53+
)}
54+
</span>
55+
);
56+
}
57+
}

src/components/components/CommonComponents.js

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import { faClipboard } from '@fortawesome/free-solid-svg-icons';
4-
import { AwesomeIcon } from '../AwesomeIcon';
3+
import CopyToClipboardButton from '../CopyToClipboardButton';
54
import { InputWidget } from '../widgets';
65
import DEFAULT_COMPONENTS from './DefaultComponents';
76
import PropertyRow from './PropertyRow';
@@ -10,7 +9,6 @@ import Mixins from './Mixins';
109
import { getEntityClipboardRepresentation } from '../../lib/entity';
1110
import EntityRepresentation from '../EntityRepresentation';
1211
import Events from '../../lib/Events';
13-
import copy from 'clipboard-copy';
1412
import { saveBlob } from '../../lib/utils';
1513
import GLTFIcon from '../../../assets/gltf.svg';
1614

@@ -107,17 +105,11 @@ export default class CommonComponents extends React.Component {
107105
>
108106
<GLTFIcon />
109107
</a>
110-
<a
108+
<CopyToClipboardButton
111109
title="Copy entity HTML to clipboard"
112-
className="button"
113-
onClick={(event) => {
114-
event.preventDefault();
115-
event.stopPropagation();
116-
copy(getEntityClipboardRepresentation(this.props.entity));
117-
}}
118-
>
119-
<AwesomeIcon icon={faClipboard} />
120-
</a>
110+
message="Copied entity HTML to clipboard"
111+
text={() => getEntityClipboardRepresentation(this.props.entity)}
112+
/>
121113
</div>
122114
);
123115

src/components/components/Component.js

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import { faClipboard, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
3+
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
44
import { AwesomeIcon } from '../AwesomeIcon';
55
import PropertyRow from './PropertyRow';
66
import Collapsible from '../Collapsible';
7-
import copy from 'clipboard-copy';
7+
import CopyToClipboardButton from '../CopyToClipboardButton';
88
import { getComponentClipboardRepresentation } from '../../lib/entity';
99
import { shouldShowProperty } from '../../lib/utils';
1010
import Events from '../../lib/Events';
@@ -119,22 +119,16 @@ export default class Component extends React.Component {
119119
<span>{componentName}</span>
120120
</span>
121121
<div className="componentHeaderActions">
122-
<a
122+
<CopyToClipboardButton
123123
title="Copy to clipboard"
124-
className="button"
125-
onClick={(event) => {
126-
event.preventDefault();
127-
event.stopPropagation();
128-
copy(
129-
getComponentClipboardRepresentation(
130-
this.state.entity,
131-
componentName.toLowerCase()
132-
)
133-
);
134-
}}
135-
>
136-
<AwesomeIcon icon={faClipboard} />
137-
</a>
124+
message="Copied to clipboard"
125+
text={() =>
126+
getComponentClipboardRepresentation(
127+
this.state.entity,
128+
componentName.toLowerCase()
129+
)
130+
}
131+
/>
138132
<a
139133
title="Remove component"
140134
className="button"

src/style/index.styl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,14 @@ body.aframe-inspector-opened
274274
border 0 /* To remove the inner border (specific to Firefox). */
275275
padding 0
276276

277+
.copy-tooltip
278+
background-color var(--color-base-200)
279+
border 1px solid var(--color-base-content)
280+
color var(--color-base-content)
281+
padding 10px
282+
white-space nowrap
283+
z-index 999999
284+
277285
.hidden
278286
visibility hidden
279287

0 commit comments

Comments
 (0)