Tom Kirkpatrick
6 years ago
10 changed files with 0 additions and 892 deletions
@ -1,139 +0,0 @@ |
|||||
import React from 'react' |
|
||||
import PropTypes from 'prop-types' |
|
||||
import ReactModal from 'react-modal' |
|
||||
import FaCircle from 'react-icons/lib/fa/circle' |
|
||||
import MdClose from 'react-icons/lib/md/close' |
|
||||
|
|
||||
import { btc } from 'lib/utils' |
|
||||
|
|
||||
import styles from './ContactModal.scss' |
|
||||
|
|
||||
const ContactModal = ({ |
|
||||
isOpen, |
|
||||
channel, |
|
||||
closeContactModal, |
|
||||
channelNodes, |
|
||||
closeChannel, |
|
||||
closingChannelIds |
|
||||
}) => { |
|
||||
if (!channel) { |
|
||||
return <span /> |
|
||||
} |
|
||||
|
|
||||
const customStyles = { |
|
||||
overlay: { |
|
||||
cursor: 'pointer', |
|
||||
overflowY: 'auto' |
|
||||
}, |
|
||||
content: { |
|
||||
top: 'auto', |
|
||||
left: '0', |
|
||||
right: '0', |
|
||||
bottom: 'auto', |
|
||||
width: '40%', |
|
||||
margin: '50px auto', |
|
||||
borderRadius: 'none', |
|
||||
padding: '0' |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const removeClicked = () => { |
|
||||
closeChannel({ |
|
||||
channel_point: channel.channel_point, |
|
||||
chan_id: channel.chan_id, |
|
||||
force: !channel.active |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// the remote node for the channel
|
|
||||
const node = channelNodes.find(node => node.pub_key === channel.remote_pubkey) |
|
||||
|
|
||||
return ( |
|
||||
<ReactModal |
|
||||
isOpen={isOpen} |
|
||||
contentLabel="No Overlay Click Modal" |
|
||||
ariaHideApp |
|
||||
shouldCloseOnOverlayClick |
|
||||
onRequestClose={closeContactModal} |
|
||||
parentSelector={() => document.body} |
|
||||
style={customStyles} |
|
||||
> |
|
||||
{channel && ( |
|
||||
<div className={styles.container}> |
|
||||
<header className={styles.header}> |
|
||||
<div className={`${styles.status} ${channel.active ? styles.online : undefined}`}> |
|
||||
<FaCircle style={{ verticalAlign: 'top' }} /> |
|
||||
<span>{channel.active ? 'Online' : 'Offline'}</span> |
|
||||
</div> |
|
||||
<div className={styles.closeContainer}> |
|
||||
<span onClick={closeContactModal}> |
|
||||
<MdClose /> |
|
||||
</span> |
|
||||
</div> |
|
||||
</header> |
|
||||
|
|
||||
<section className={styles.title}> |
|
||||
{node && <h1>{node.alias}</h1>} |
|
||||
<h2>{channel.remote_pubkey}</h2> |
|
||||
</section> |
|
||||
|
|
||||
<section className={styles.stats}> |
|
||||
<div className={styles.pay}> |
|
||||
<h4>Can Pay</h4> |
|
||||
<div className={styles.meter}> |
|
||||
<div |
|
||||
className={styles.amount} |
|
||||
style={{ width: `${(channel.local_balance / channel.capacity) * 100}%` }} |
|
||||
/> |
|
||||
</div> |
|
||||
<span>{btc.satoshisToBtc(channel.local_balance)} BTC</span> |
|
||||
</div> |
|
||||
|
|
||||
<div className={styles.pay}> |
|
||||
<h4>Can Receive</h4> |
|
||||
<div className={styles.meter}> |
|
||||
<div |
|
||||
className={styles.amount} |
|
||||
style={{ width: `${(channel.remote_balance / channel.capacity) * 100}%` }} |
|
||||
/> |
|
||||
</div> |
|
||||
<span>{btc.satoshisToBtc(channel.remote_balance)} BTC</span> |
|
||||
</div> |
|
||||
|
|
||||
<div className={styles.sent}> |
|
||||
<h4>Total Bitcoin Sent</h4> |
|
||||
<p>{btc.satoshisToBtc(channel.total_satoshis_sent)} BTC</p> |
|
||||
</div> |
|
||||
<div className={styles.received}> |
|
||||
<h4>Total Bitcoin Received</h4> |
|
||||
<p>{btc.satoshisToBtc(channel.total_satoshis_received)} BTC</p> |
|
||||
</div> |
|
||||
</section> |
|
||||
|
|
||||
<footer> |
|
||||
{closingChannelIds.includes(channel.chan_id) ? ( |
|
||||
<span className={styles.inactive}> |
|
||||
<div className={styles.loading}> |
|
||||
<div className={styles.spinner} /> |
|
||||
</div> |
|
||||
</span> |
|
||||
) : ( |
|
||||
<div onClick={removeClicked}>Remove</div> |
|
||||
)} |
|
||||
</footer> |
|
||||
</div> |
|
||||
)} |
|
||||
</ReactModal> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
ContactModal.propTypes = { |
|
||||
channel: PropTypes.object, |
|
||||
isOpen: PropTypes.bool.isRequired, |
|
||||
closeContactModal: PropTypes.func.isRequired, |
|
||||
channelNodes: PropTypes.array.isRequired, |
|
||||
closeChannel: PropTypes.func.isRequired, |
|
||||
closingChannelIds: PropTypes.array.isRequired |
|
||||
} |
|
||||
|
|
||||
export default ContactModal |
|
@ -1,151 +0,0 @@ |
|||||
@import 'styles/variables.scss'; |
|
||||
|
|
||||
.header { |
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
justify-content: space-between; |
|
||||
align-items: flex-end; |
|
||||
background: $lightgrey; |
|
||||
padding: 20px; |
|
||||
|
|
||||
.status { |
|
||||
font-size: 12px; |
|
||||
color: $darkestgrey; |
|
||||
|
|
||||
&.online { |
|
||||
color: $green; |
|
||||
} |
|
||||
|
|
||||
span { |
|
||||
margin-left: 5px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.closeContainer { |
|
||||
background: $lightgrey; |
|
||||
line-height: 12px; |
|
||||
|
|
||||
span { |
|
||||
color: $darkestgrey; |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.container section { |
|
||||
margin-bottom: 30px; |
|
||||
padding: 0 20px; |
|
||||
|
|
||||
.pay, |
|
||||
.receive, |
|
||||
.sent, |
|
||||
.received { |
|
||||
margin: 40px 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.container .title { |
|
||||
margin: 0; |
|
||||
padding: 30px 20px; |
|
||||
background: $lightgrey; |
|
||||
|
|
||||
h1 { |
|
||||
color: $secondary; |
|
||||
font-weight: bold; |
|
||||
font-size: 16px; |
|
||||
letter-spacing: 1.1px; |
|
||||
margin-bottom: 10px; |
|
||||
} |
|
||||
|
|
||||
h2 { |
|
||||
font-size: 12px; |
|
||||
color: $darkestgrey; |
|
||||
font-weight: 100; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.stats { |
|
||||
h4 { |
|
||||
color: $secondary; |
|
||||
font-weight: bold; |
|
||||
font-size: 12px; |
|
||||
} |
|
||||
|
|
||||
span { |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
|
|
||||
p { |
|
||||
margin-top: 10px; |
|
||||
color: $darkestgrey; |
|
||||
} |
|
||||
|
|
||||
.meter, |
|
||||
.amount { |
|
||||
height: 10px; |
|
||||
border-radius: 10px; |
|
||||
} |
|
||||
|
|
||||
.meter { |
|
||||
background: $darkgrey; |
|
||||
width: 100%; |
|
||||
margin: 10px 0; |
|
||||
} |
|
||||
|
|
||||
.amount { |
|
||||
background: $darkestgrey; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.container footer { |
|
||||
padding: 20px; |
|
||||
text-align: center; |
|
||||
|
|
||||
div { |
|
||||
color: $red; |
|
||||
font-size: 18px; |
|
||||
|
|
||||
&:hover { |
|
||||
color: lighten($red, 10%); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@-webkit-keyframes animation-rotate { |
|
||||
100% { |
|
||||
-webkit-transform: rotate(360deg); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@-moz-keyframes animation-rotate { |
|
||||
100% { |
|
||||
-moz-transform: rotate(360deg); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@-o-keyframes animation-rotate { |
|
||||
100% { |
|
||||
-o-transform: rotate(360deg); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@keyframes animation-rotate { |
|
||||
100% { |
|
||||
transform: rotate(360deg); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.spinner { |
|
||||
border: 1px solid rgba(0, 0, 0, 0.1); |
|
||||
border-left-color: rgba(0, 0, 0, 0.4); |
|
||||
-webkit-border-radius: 999px; |
|
||||
-moz-border-radius: 999px; |
|
||||
border-radius: 999px; |
|
||||
margin: 0 auto; |
|
||||
height: 20px; |
|
||||
width: 20px; |
|
||||
-webkit-animation: animation-rotate 1000ms linear infinite; |
|
||||
-moz-animation: animation-rotate 1000ms linear infinite; |
|
||||
-o-animation: animation-rotate 1000ms linear infinite; |
|
||||
animation: animation-rotate 1000ms linear infinite; |
|
||||
} |
|
@ -1,3 +0,0 @@ |
|||||
import ContactModal from './ContactModal' |
|
||||
|
|
||||
export default ContactModal |
|
@ -1,275 +0,0 @@ |
|||||
import React from 'react' |
|
||||
import PropTypes from 'prop-types' |
|
||||
import ReactModal from 'react-modal' |
|
||||
import MdClose from 'react-icons/lib/md/close' |
|
||||
import FaCircle from 'react-icons/lib/fa/circle' |
|
||||
import FaQuestionCircle from 'react-icons/lib/fa/question-circle' |
|
||||
import styles from './ContactsForm.scss' |
|
||||
|
|
||||
class ContactsForm extends React.Component { |
|
||||
constructor(props) { |
|
||||
super(props) |
|
||||
|
|
||||
this.state = { |
|
||||
editing: false |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
render() { |
|
||||
const { |
|
||||
contactsform, |
|
||||
contactsform: { showErrors }, |
|
||||
closeContactsForm, |
|
||||
updateContactFormSearchQuery, |
|
||||
updateManualFormSearchQuery, |
|
||||
updateContactCapacity, |
|
||||
openChannel, |
|
||||
updateManualFormErrors, |
|
||||
activeChannelPubkeys, |
|
||||
nonActiveChannelPubkeys, |
|
||||
pendingOpenChannelPubkeys, |
|
||||
filteredNetworkNodes, |
|
||||
loadingChannelPubkeys, |
|
||||
showManualForm, |
|
||||
manualFormIsValid |
|
||||
} = this.props |
|
||||
|
|
||||
const { editing } = this.state |
|
||||
|
|
||||
const renderRightSide = node => { |
|
||||
if (loadingChannelPubkeys.includes(node.pub_key)) { |
|
||||
return ( |
|
||||
<span className={styles.inactive}> |
|
||||
<div className={styles.loading}> |
|
||||
<div className={styles.spinner} /> |
|
||||
</div> |
|
||||
</span> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
if (activeChannelPubkeys.includes(node.pub_key)) { |
|
||||
return ( |
|
||||
<span className={`${styles.online} ${styles.inactive}`}> |
|
||||
<FaCircle style={{ verticalAlign: 'top' }} /> <span>Online</span> |
|
||||
</span> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
if (nonActiveChannelPubkeys.includes(node.pub_key)) { |
|
||||
return ( |
|
||||
<span className={`${styles.offline} ${styles.inactive}`}> |
|
||||
<FaCircle style={{ verticalAlign: 'top' }} /> <span>Offline</span> |
|
||||
</span> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
if (pendingOpenChannelPubkeys.includes(node.pub_key)) { |
|
||||
return ( |
|
||||
<span className={`${styles.pending} ${styles.inactive}`}> |
|
||||
<FaCircle style={{ verticalAlign: 'top' }} /> <span>Pending</span> |
|
||||
</span> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
if (!node.addresses.length) { |
|
||||
return <span className={`${styles.private} ${styles.inactive}`}>Private</span> |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<span |
|
||||
className={`${styles.connect} hint--left`} |
|
||||
data-hint={`Connect with ${contactsform.contactCapacity} BTC`} |
|
||||
onClick={() => |
|
||||
openChannel({ |
|
||||
pubkey: node.pub_key, |
|
||||
host: node.addresses[0].addr, |
|
||||
local_amt: contactsform.contactCapacity |
|
||||
}) |
|
||||
} |
|
||||
> |
|
||||
Connect |
|
||||
</span> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
const inputClicked = () => { |
|
||||
if (editing) { |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
this.setState({ editing: true }) |
|
||||
} |
|
||||
|
|
||||
const manualFormSubmit = () => { |
|
||||
if (!manualFormIsValid.isValid) { |
|
||||
updateManualFormErrors(manualFormIsValid.errors) |
|
||||
updateManualFormSearchQuery('') |
|
||||
return |
|
||||
} |
|
||||
// clear any existing errors
|
|
||||
|
|
||||
updateManualFormErrors({ manualInput: null }) |
|
||||
const [pubkey, host] = |
|
||||
contactsform.manualSearchQuery && contactsform.manualSearchQuery.split('@') |
|
||||
|
|
||||
openChannel({ pubkey, host, local_amt: contactsform.contactCapacity }) |
|
||||
|
|
||||
updateManualFormSearchQuery('') |
|
||||
} |
|
||||
|
|
||||
const searchUpdated = search => { |
|
||||
updateContactFormSearchQuery(search) |
|
||||
|
|
||||
if (search.includes('@') && search.split('@')[0].length === 66) { |
|
||||
updateManualFormSearchQuery(search) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div> |
|
||||
<ReactModal |
|
||||
isOpen={contactsform.isOpen} |
|
||||
contentLabel="No Overlay Click Modal" |
|
||||
ariaHideApp |
|
||||
shouldCloseOnOverlayClick |
|
||||
onRequestClose={() => closeContactsForm} |
|
||||
parentSelector={() => document.body} |
|
||||
className={styles.modal} |
|
||||
> |
|
||||
<header> |
|
||||
<div> |
|
||||
<h1>Add Contact</h1> |
|
||||
</div> |
|
||||
<div onClick={closeContactsForm} className={styles.modalClose}> |
|
||||
<MdClose /> |
|
||||
</div> |
|
||||
</header> |
|
||||
|
|
||||
<div className={styles.form}> |
|
||||
<div className={styles.search}> |
|
||||
<input |
|
||||
type="text" |
|
||||
placeholder="Find contact by alias or pubkey" |
|
||||
className={styles.searchInput} |
|
||||
value={contactsform.searchQuery} |
|
||||
onChange={event => searchUpdated(event.target.value)} |
|
||||
/> |
|
||||
</div> |
|
||||
|
|
||||
<ul className={styles.networkResults}> |
|
||||
{filteredNetworkNodes.map(node => ( |
|
||||
<li key={node.pub_key}> |
|
||||
<section> |
|
||||
{node.alias.length > 0 ? ( |
|
||||
<h2> |
|
||||
<span>{node.alias.trim()}</span> |
|
||||
<span> |
|
||||
({node.pub_key.substr(0, 10)} |
|
||||
... |
|
||||
{node.pub_key.substr(node.pub_key.length - 10)}) |
|
||||
</span> |
|
||||
</h2> |
|
||||
) : ( |
|
||||
<h2> |
|
||||
<span>{node.pub_key}</span> |
|
||||
</h2> |
|
||||
)} |
|
||||
</section> |
|
||||
<section>{renderRightSide(node)}</section> |
|
||||
</li> |
|
||||
))} |
|
||||
</ul> |
|
||||
</div> |
|
||||
|
|
||||
{showManualForm && ( |
|
||||
<div className={styles.manualForm}> |
|
||||
<h2> |
|
||||
Hm, looks like we can’t see that contact from here. Want to try and manually |
|
||||
connect? |
|
||||
</h2> |
|
||||
<section> |
|
||||
<input |
|
||||
type="text" |
|
||||
placeholder="pubkey@host" |
|
||||
value={contactsform.manualSearchQuery} |
|
||||
onChange={event => updateManualFormSearchQuery(event.target.value)} |
|
||||
/> |
|
||||
<div className={styles.submit} onClick={manualFormSubmit}> |
|
||||
Submit |
|
||||
</div> |
|
||||
|
|
||||
{loadingChannelPubkeys.length > 0 && ( |
|
||||
<div className={styles.manualFormSpinner}> |
|
||||
<div className={styles.loading}> |
|
||||
<div className={styles.spinner} /> |
|
||||
</div> |
|
||||
</div> |
|
||||
)} |
|
||||
</section> |
|
||||
|
|
||||
<section |
|
||||
className={`${styles.errorMessage} ${ |
|
||||
showErrors.manualInput ? styles.active : undefined |
|
||||
}`}
|
|
||||
> |
|
||||
{showErrors.manualInput && ( |
|
||||
<span>{manualFormIsValid && manualFormIsValid.errors.manualInput}</span> |
|
||||
)} |
|
||||
</section> |
|
||||
</div> |
|
||||
)} |
|
||||
|
|
||||
<footer className={styles.footer}> |
|
||||
<div> |
|
||||
<span>Use</span> |
|
||||
<span className={styles.amount}> |
|
||||
<input |
|
||||
type="text" |
|
||||
value={contactsform.contactCapacity} |
|
||||
onChange={event => updateContactCapacity(event.target.value)} |
|
||||
onClick={inputClicked} |
|
||||
onKeyPress={event => event.charCode === 13 && this.setState({ editing: false })} |
|
||||
readOnly={!editing} |
|
||||
style={{ |
|
||||
width: `${editing ? 20 : contactsform.contactCapacity.toString().length + 1}%` |
|
||||
}} |
|
||||
/> |
|
||||
</span> |
|
||||
<span className={styles.caption}> |
|
||||
BTC per contact |
|
||||
<i |
|
||||
data-hint="You aren't spending anything, just moving money onto the Lightning Network" |
|
||||
className="hint--top" |
|
||||
> |
|
||||
<FaQuestionCircle style={{ verticalAlign: 'top' }} /> |
|
||||
</i> |
|
||||
</span> |
|
||||
</div> |
|
||||
</footer> |
|
||||
</ReactModal> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
ContactsForm.propTypes = { |
|
||||
contactsform: PropTypes.object.isRequired, |
|
||||
closeContactsForm: PropTypes.func.isRequired, |
|
||||
updateContactFormSearchQuery: PropTypes.func.isRequired, |
|
||||
updateManualFormSearchQuery: PropTypes.func.isRequired, |
|
||||
manualFormIsValid: PropTypes.shape({ |
|
||||
errors: PropTypes.object, |
|
||||
isValid: PropTypes.bool |
|
||||
}).isRequired, |
|
||||
updateContactCapacity: PropTypes.func.isRequired, |
|
||||
updateManualFormErrors: PropTypes.func.isRequired, |
|
||||
openChannel: PropTypes.func.isRequired, |
|
||||
activeChannelPubkeys: PropTypes.array.isRequired, |
|
||||
nonActiveChannelPubkeys: PropTypes.array.isRequired, |
|
||||
pendingOpenChannelPubkeys: PropTypes.array.isRequired, |
|
||||
filteredNetworkNodes: PropTypes.array.isRequired, |
|
||||
loadingChannelPubkeys: PropTypes.array.isRequired, |
|
||||
showManualForm: PropTypes.bool.isRequired |
|
||||
} |
|
||||
|
|
||||
export default ContactsForm |
|
@ -1,253 +0,0 @@ |
|||||
@import 'styles/variables.scss'; |
|
||||
|
|
||||
.modal { |
|
||||
position: absolute; |
|
||||
width: 50%; |
|
||||
margin: 50px auto; |
|
||||
top: auto; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
bottom: auto; |
|
||||
background: $white; |
|
||||
outline: none; |
|
||||
z-index: -2; |
|
||||
border: 1px solid $darkgrey; |
|
||||
|
|
||||
header { |
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
justify-content: space-between; |
|
||||
padding: 15px; |
|
||||
border-bottom: 1px solid $darkgrey; |
|
||||
|
|
||||
h1, |
|
||||
svg { |
|
||||
font-size: 22px; |
|
||||
} |
|
||||
|
|
||||
svg { |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.form { |
|
||||
padding: 30px 15px; |
|
||||
|
|
||||
.search { |
|
||||
.searchInput { |
|
||||
width: calc(100% - 30px); |
|
||||
padding: 10px 15px; |
|
||||
outline: 0; |
|
||||
border: 0; |
|
||||
background: $lightgrey; |
|
||||
color: $darkestgrey; |
|
||||
border-radius: 5px; |
|
||||
font-size: 16px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.networkResults { |
|
||||
overflow-y: auto; |
|
||||
height: 250px; |
|
||||
margin-top: 30px; |
|
||||
padding: 20px 0; |
|
||||
|
|
||||
li { |
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
justify-content: space-between; |
|
||||
padding: 10px 0; |
|
||||
|
|
||||
h2 { |
|
||||
font-size: 16px; |
|
||||
font-weight: bold; |
|
||||
letter-spacing: 1.3px; |
|
||||
|
|
||||
span { |
|
||||
display: inline-block; |
|
||||
vertical-align: middle; |
|
||||
|
|
||||
&:nth-child(1) { |
|
||||
font-size: 14px; |
|
||||
font-weight: bold; |
|
||||
letter-spacing: 1.3px; |
|
||||
} |
|
||||
|
|
||||
&:nth-child(2) { |
|
||||
color: $darkestgrey; |
|
||||
font-size: 12px; |
|
||||
line-height: 14px; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.connect { |
|
||||
cursor: pointer; |
|
||||
color: $darkestgrey; |
|
||||
transition: all 0.25s; |
|
||||
font-size: 12px; |
|
||||
|
|
||||
&:hover { |
|
||||
color: $main; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.inactive { |
|
||||
font-size: 12px; |
|
||||
display: inline-block; |
|
||||
vertical-align: top; |
|
||||
|
|
||||
&.online { |
|
||||
color: $green; |
|
||||
} |
|
||||
|
|
||||
&.offline { |
|
||||
color: $darkestgrey; |
|
||||
} |
|
||||
|
|
||||
&.pending { |
|
||||
color: $orange; |
|
||||
} |
|
||||
|
|
||||
&.private { |
|
||||
color: darken($darkestgrey, 50%); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.manualForm { |
|
||||
position: relative; |
|
||||
background: $lightgrey; |
|
||||
color: $darkestgrey; |
|
||||
padding: 30px 15px; |
|
||||
|
|
||||
h2 { |
|
||||
font-size: 16px; |
|
||||
margin-bottom: 10px; |
|
||||
} |
|
||||
|
|
||||
input { |
|
||||
border: 0; |
|
||||
outline: 0; |
|
||||
background: transparent; |
|
||||
color: $darkestgrey; |
|
||||
border-bottom: 1px solid $darkestgrey; |
|
||||
padding: 10px 5px; |
|
||||
width: 80%; |
|
||||
} |
|
||||
|
|
||||
.submit { |
|
||||
display: inline-block; |
|
||||
vertical-align: middle; |
|
||||
width: 15%; |
|
||||
margin-left: 2.5%; |
|
||||
font-size: 12px; |
|
||||
|
|
||||
&:hover { |
|
||||
cursor: pointer; |
|
||||
color: $main; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.manualFormSpinner { |
|
||||
position: absolute; |
|
||||
right: 0; |
|
||||
top: 40%; |
|
||||
padding: 0 10px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.errorMessage { |
|
||||
margin: 10px 0; |
|
||||
min-height: 20px; |
|
||||
color: $red; |
|
||||
opacity: 0; |
|
||||
transition: all 0.25s ease; |
|
||||
|
|
||||
&.active { |
|
||||
opacity: 1; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.footer { |
|
||||
padding: 10px 15px; |
|
||||
border-top: 1px solid $darkgrey; |
|
||||
font-size: 14px; |
|
||||
|
|
||||
span { |
|
||||
&.amount { |
|
||||
&:hover { |
|
||||
input { |
|
||||
border: 1px solid $darkgrey; |
|
||||
cursor: text; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
input { |
|
||||
border: 1px solid transparent; |
|
||||
padding: 0; |
|
||||
outline: 0; |
|
||||
font-weight: bold; |
|
||||
font-size: 14px; |
|
||||
line-height: 14px; |
|
||||
transition: all 0.25s; |
|
||||
|
|
||||
&.isEditing { |
|
||||
width: 100%; |
|
||||
border-bottom: 1px solid $darkgrey; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
&:nth-child(2) { |
|
||||
margin-left: 2px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.caption svg { |
|
||||
font-size: 10px; |
|
||||
color: $darkestgrey; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@-webkit-keyframes animation-rotate { |
|
||||
100% { |
|
||||
-webkit-transform: rotate(360deg); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@-moz-keyframes animation-rotate { |
|
||||
100% { |
|
||||
-moz-transform: rotate(360deg); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@-o-keyframes animation-rotate { |
|
||||
100% { |
|
||||
-o-transform: rotate(360deg); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@keyframes animation-rotate { |
|
||||
100% { |
|
||||
transform: rotate(360deg); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.spinner { |
|
||||
border: 1px solid rgba(0, 0, 0, 0.1); |
|
||||
border-left-color: rgba(0, 0, 0, 0.4); |
|
||||
-webkit-border-radius: 999px; |
|
||||
-moz-border-radius: 999px; |
|
||||
border-radius: 999px; |
|
||||
margin: 0 auto; |
|
||||
height: 20px; |
|
||||
width: 20px; |
|
||||
-webkit-animation: animation-rotate 1000ms linear infinite; |
|
||||
-moz-animation: animation-rotate 1000ms linear infinite; |
|
||||
-o-animation: animation-rotate 1000ms linear infinite; |
|
||||
animation: animation-rotate 1000ms linear infinite; |
|
||||
} |
|
@ -1,3 +0,0 @@ |
|||||
import ContactsForm from './ContactsForm' |
|
||||
|
|
||||
export default ContactsForm |
|
Loading…
Reference in new issue