From ef6eb96aaf9fa003c397d904d8986b0c145142dd Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Mon, 3 Aug 2015 16:11:38 -0700 Subject: [PATCH] Show cutesy animation when copy to clipboard succeeds --- package.json | 1 + src/App.css | 20 ++++++++++++++++++++ src/Field.react.js | 21 +++++++++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b960db7..e2bd79d 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "webpack-dev-server": "^1.8.2" }, "dependencies": { + "classnames": "^2.1.3", "react": "^0.13.0" } } diff --git a/src/App.css b/src/App.css index ca6cea4..1656dad 100644 --- a/src/App.css +++ b/src/App.css @@ -21,6 +21,7 @@ body { flex-grow: 5; font-family: monospace; font-size: 12px; + position: relative; } .hextrapolate-field input { @@ -52,6 +53,7 @@ body { background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIj48cGF0aCBmaWxsPSIjNDQ0IiBkPSJNMjAgOFYwSDZMMCA2djE4aDEydjhoMjBWOEgyMHpNNiAyLjgyOFY2SDIuODI4TDYgMi44Mjh6TTIgMjJWOGg2VjJoMTB2NmwtNiA2djhIMnptMTYtMTEuMTcyVjE0aC0zLjE3MkwxOCAxMC44Mjh6TTMwIDMwSDE0VjE2aDZ2LTZoMTB2MjB6Ii8+PC9zdmc+); background-repeat: no-repeat; background-size: 16px 16px; + cursor: pointer; display: inline-block; height: 16px; margin-left: 8px; @@ -59,5 +61,23 @@ body { position: relative; text-indent: -10000px; top: 4px; + user-select: none; width: 16px; } + +.hextrapolate-copy-status { + color: #0f0; + opacity: 0; + position: absolute; + top: 4px; + right: 4px; + transform: scale(1); + transition: transform .25s, opacity .25s; + user-select: none; +} + +.hextrapolate-copy-success { + transform: scale(5); + opacity: .5; + transition: transform .5s, opacity .5s; +} diff --git a/src/Field.react.js b/src/Field.react.js index 912a2ae..a629d30 100644 --- a/src/Field.react.js +++ b/src/Field.react.js @@ -9,6 +9,7 @@ import React from 'react'; import convert from './convert'; +import cx from 'classnames'; export type Value = { base: number; @@ -50,6 +51,7 @@ export default class Field extends React.Component { `base prop must be between 2..${DIGITS.length}` ); } + this.state = {copySucceeded: false}; } _isValid(value: string): boolean { @@ -69,14 +71,20 @@ export default class Field extends React.Component { } } - _onCopy = () => { + _onCopy = event => { + event.preventDefault(); + event.stopPropagation(); React.findDOMNode(this._input).select(); // May throw a SecurityError. try { - document.execCommand('copy'); + this.setState({ + copySucceeded: document.execCommand('copy'), + }); + setTimeout(() => this.setState({copySucceeded: false}), 750); } catch(error) { // eslint-disable-line no-empty // Swallow. + this.setState({copySucceeded: false}); } } @@ -95,6 +103,14 @@ export default class Field extends React.Component { ); } + _copyStatus() { + const classNames = cx({ + 'hextrapolate-copy-status': true, + 'hextrapolate-copy-success': this.state.copySucceeded, + }); + return ; + } + focus() { React.findDOMNode(this._input).focus(); } @@ -108,6 +124,7 @@ export default class Field extends React.Component { type="text" value={fromValue(this.props.value, this.props.base)} /> + {this._copyStatus()} {this._copyLink()} ); -- 2.37.1