Convert Contract dropdowns to react-select (#890)
* use Select in InteractForm instead of handrolled select * convert InteractExplorer to react-select and tighten types * remove log * cleanup json abi placeholder * Add react-select style overrides (#897) * Add react-select style overrides * Add comment * Add variables & mixins * Fix border off by 1px * use simpler .map instead of forEach
This commit is contained in:
parent
05b9066f9e
commit
7c0cf7cb9e
|
@ -11,19 +11,23 @@ import { connect } from 'react-redux';
|
|||
import { Fields } from './components';
|
||||
import { setDataField, TSetDataField } from 'actions/transaction';
|
||||
import { Data } from 'libs/units';
|
||||
import Select from 'react-select';
|
||||
|
||||
interface StateProps {
|
||||
nodeLib: INode;
|
||||
to: AppState['transaction']['fields']['to'];
|
||||
dataExists: boolean;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
showNotification: TShowNotification;
|
||||
setDataField: TSetDataField;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
contractFunctions: any;
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps & OwnProps;
|
||||
|
||||
interface State {
|
||||
|
@ -31,8 +35,21 @@ interface State {
|
|||
[key: string]: { rawData: string; parsedData: string[] | string };
|
||||
};
|
||||
outputs;
|
||||
selectedFunction: null | any;
|
||||
selectedFunctionName: string;
|
||||
selectedFunction: null | ContractOption;
|
||||
}
|
||||
|
||||
interface ContractFunction {
|
||||
constant: boolean;
|
||||
decodeInput: any;
|
||||
decodeOutput: any;
|
||||
encodeInput: any;
|
||||
inputs: any[];
|
||||
outputs: any;
|
||||
}
|
||||
|
||||
interface ContractOption {
|
||||
contract: ContractFunction;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class InteractExplorerClass extends Component<Props, State> {
|
||||
|
@ -42,15 +59,16 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
|
||||
public state: State = {
|
||||
selectedFunction: null,
|
||||
selectedFunctionName: '',
|
||||
inputs: {},
|
||||
outputs: {}
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { inputs, outputs, selectedFunction, selectedFunctionName } = this.state;
|
||||
const { inputs, outputs, selectedFunction } = this.state;
|
||||
const contractFunctionsOptions = this.contractOptions();
|
||||
|
||||
const { to } = this.props;
|
||||
|
||||
const generateOrWriteButton = this.props.dataExists ? (
|
||||
<GenerateTransaction />
|
||||
) : (
|
||||
|
@ -61,6 +79,7 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
{translate('CONTRACT_Write')}
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="InteractExplorer">
|
||||
<h3 className="InteractExplorer-title">
|
||||
|
@ -68,19 +87,22 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
<span className="InteractExplorer-title-address">{to.raw}</span>
|
||||
</h3>
|
||||
|
||||
<select
|
||||
value={selectedFunction ? selectedFunction.name : ''}
|
||||
className="InteractExplorer-fnselect form-control"
|
||||
<Select
|
||||
name="exploreContract"
|
||||
value={selectedFunction as any}
|
||||
placeholder="Please select a function..."
|
||||
onChange={this.handleFunctionSelect}
|
||||
>
|
||||
<option>{translate('CONTRACT_Interact_CTA', true)}</option>
|
||||
{this.contractOptions()}
|
||||
</select>
|
||||
options={contractFunctionsOptions}
|
||||
clearable={false}
|
||||
searchable={false}
|
||||
labelKey="name"
|
||||
valueKey="contract"
|
||||
/>
|
||||
|
||||
{selectedFunction && (
|
||||
<div key={selectedFunctionName} className="InteractExplorer-func">
|
||||
<div key={selectedFunction.name} className="InteractExplorer-func">
|
||||
{/* TODO: Use reusable components with validation */}
|
||||
{selectedFunction.inputs.map(input => {
|
||||
{selectedFunction.contract.inputs.map(input => {
|
||||
const { type, name } = input;
|
||||
|
||||
return (
|
||||
|
@ -98,7 +120,7 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
</label>
|
||||
);
|
||||
})}
|
||||
{selectedFunction.outputs.map((output, index) => {
|
||||
{selectedFunction.contract.outputs.map((output, index) => {
|
||||
const { type, name } = output;
|
||||
const parsedName = name === '' ? index : name;
|
||||
|
||||
|
@ -117,7 +139,7 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
);
|
||||
})}
|
||||
|
||||
{selectedFunction.constant ? (
|
||||
{selectedFunction.contract.constant ? (
|
||||
<button
|
||||
className="InteractExplorer-func-submit btn btn-primary"
|
||||
onClick={this.handleFunctionCall}
|
||||
|
@ -137,14 +159,16 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
|
||||
private contractOptions = () => {
|
||||
const { contractFunctions } = this.props;
|
||||
|
||||
return Object.keys(contractFunctions).map(name => {
|
||||
return (
|
||||
<option key={name} value={name}>
|
||||
{name}
|
||||
</option>
|
||||
);
|
||||
});
|
||||
const transformedContractFunction: ContractOption[] = Object.keys(contractFunctions).map(
|
||||
contractFunction => {
|
||||
const contract = contractFunctions[contractFunction];
|
||||
return {
|
||||
name: contractFunction,
|
||||
contract
|
||||
};
|
||||
}
|
||||
);
|
||||
return transformedContractFunction;
|
||||
};
|
||||
|
||||
private handleFunctionCall = async (_: React.FormEvent<HTMLButtonElement>) => {
|
||||
|
@ -160,7 +184,7 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
const callData = { to: to.raw, data };
|
||||
const results = await nodeLib.sendCallRequest(callData);
|
||||
|
||||
const parsedResult = selectedFunction.decodeOutput(results);
|
||||
const parsedResult = selectedFunction!.contract.decodeOutput(results);
|
||||
this.setState({ outputs: parsedResult });
|
||||
} catch (e) {
|
||||
this.props.showNotification(
|
||||
|
@ -184,14 +208,9 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
}
|
||||
};
|
||||
|
||||
private handleFunctionSelect = (ev: React.FormEvent<HTMLSelectElement>) => {
|
||||
const { contractFunctions } = this.props;
|
||||
const selectedFunctionName = ev.currentTarget.value;
|
||||
const selectedFunction = contractFunctions[selectedFunctionName];
|
||||
|
||||
private handleFunctionSelect = (selectedFunction: ContractOption) => {
|
||||
this.setState({
|
||||
selectedFunction,
|
||||
selectedFunctionName,
|
||||
outputs: {},
|
||||
inputs: {}
|
||||
});
|
||||
|
@ -203,8 +222,7 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
(accu, key) => ({ ...accu, [key]: inputs[key].parsedData }),
|
||||
{}
|
||||
);
|
||||
const data = selectedFunction.encodeInput(parsedInputs);
|
||||
return data;
|
||||
return selectedFunction!.contract.encodeInput(parsedInputs);
|
||||
}
|
||||
|
||||
private tryParseJSON(input: string) {
|
||||
|
|
|
@ -6,60 +6,76 @@ import { connect } from 'react-redux';
|
|||
import { AppState } from 'reducers';
|
||||
import { isValidETHAddress, isValidAbiJson } from 'libs/validators';
|
||||
import classnames from 'classnames';
|
||||
import Select from 'react-select';
|
||||
|
||||
interface Props {
|
||||
interface ContractOption {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
contracts: NetworkContract[];
|
||||
accessContract(abiJson: string, address: string): (ev) => void;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
accessContract(contractAbi: string, address: string): (ev) => void;
|
||||
resetState(): void;
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
interface State {
|
||||
address: string;
|
||||
abiJson: string;
|
||||
contract: ContractOption | null;
|
||||
contractPlaceholder: string;
|
||||
}
|
||||
|
||||
class InteractForm extends Component<Props, State> {
|
||||
public state = {
|
||||
address: '',
|
||||
abiJson: ''
|
||||
};
|
||||
const abiJsonPlaceholder = [
|
||||
{
|
||||
type: 'constructor',
|
||||
inputs: [{ name: 'param1', type: 'uint256', indexed: true }],
|
||||
name: 'Event'
|
||||
},
|
||||
{ type: 'function', inputs: [{ name: 'a', type: 'uint256' }], name: 'foo', outputs: [] }
|
||||
];
|
||||
|
||||
private abiJsonPlaceholder = '[{ "type":"contructor", "inputs":\
|
||||
[{ "name":"param1","type":"uint256", "indexed":true }],\
|
||||
"name":"Event" }, { "type":"function", "inputs": [{"nam\
|
||||
e":"a", "type":"uint256"}], "name":"foo", "outputs": [] }]';
|
||||
class InteractForm extends Component<Props, State> {
|
||||
private abiJsonPlaceholder = JSON.stringify(abiJsonPlaceholder, null, 0);
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
address: '',
|
||||
abiJson: '',
|
||||
contract: null,
|
||||
contractPlaceholder: this.isContractsValid()
|
||||
? 'Please select a contract...'
|
||||
: 'No contracts available'
|
||||
};
|
||||
}
|
||||
|
||||
public isContractsValid = () => {
|
||||
const { contracts } = this.props;
|
||||
return contracts && contracts.length;
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { contracts, accessContract } = this.props;
|
||||
const { address, abiJson } = this.state;
|
||||
const { address, abiJson, contract } = this.state;
|
||||
const validEthAddress = isValidETHAddress(address);
|
||||
const validAbiJson = isValidAbiJson(abiJson);
|
||||
const showContractAccessButton = validEthAddress && validAbiJson;
|
||||
let contractOptions;
|
||||
if (contracts && contracts.length) {
|
||||
contractOptions = [
|
||||
{
|
||||
name: 'Select a contract...',
|
||||
value: null
|
||||
}
|
||||
];
|
||||
let contractOptions: ContractOption[] = [];
|
||||
|
||||
contractOptions = contractOptions.concat(
|
||||
contracts.map(contract => {
|
||||
const addr = contract.address ? `(${contract.address.substr(0, 10)}...)` : '';
|
||||
return {
|
||||
name: `${contract.name} ${addr}`,
|
||||
value: this.makeContractValue(contract)
|
||||
};
|
||||
})
|
||||
);
|
||||
} else {
|
||||
contractOptions = [
|
||||
{
|
||||
name: 'No contracts available',
|
||||
value: null
|
||||
}
|
||||
];
|
||||
if (this.isContractsValid()) {
|
||||
contractOptions = contracts.map(con => {
|
||||
const addr = con.address ? `(${con.address.substr(0, 10)}...)` : '';
|
||||
return {
|
||||
name: `${con.name} ${addr}`,
|
||||
value: this.makeContractValue(con)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Use common components for address, abi json
|
||||
|
@ -82,17 +98,17 @@ e":"a", "type":"uint256"}], "name":"foo", "outputs": [] }]';
|
|||
|
||||
<label className="InteractForm-address-contract form-group col-sm-6">
|
||||
<h4>{translate('CONTRACT_Title_2')}</h4>
|
||||
<select
|
||||
className="InteractForm-address-field-input form-control"
|
||||
<Select
|
||||
name="interactContract"
|
||||
className={`${!contract ? 'is-invalid' : ''}`}
|
||||
value={contract as any}
|
||||
placeholder={this.state.contractPlaceholder}
|
||||
onChange={this.handleSelectContract}
|
||||
disabled={!contracts || !contracts.length}
|
||||
>
|
||||
{contractOptions.map(opt => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
{opt.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
options={contractOptions}
|
||||
clearable={false}
|
||||
searchable={false}
|
||||
labelKey="name"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
@ -128,15 +144,16 @@ e":"a", "type":"uint256"}], "name":"foo", "outputs": [] }]';
|
|||
this.setState({ [name]: ev.currentTarget.value });
|
||||
};
|
||||
|
||||
private handleSelectContract = (ev: React.FormEvent<HTMLSelectElement>) => {
|
||||
private handleSelectContract = (contract: ContractOption) => {
|
||||
this.props.resetState();
|
||||
const contract = this.props.contracts.find(currContract => {
|
||||
return this.makeContractValue(currContract) === ev.currentTarget.value;
|
||||
const fullContract = this.props.contracts.find(currContract => {
|
||||
return this.makeContractValue(currContract) === contract.value;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
address: contract && contract.address ? contract.address : '',
|
||||
abiJson: contract && contract.abi ? contract.abi : ''
|
||||
address: fullContract && fullContract.address ? fullContract.address : '',
|
||||
abiJson: fullContract && fullContract.abi ? fullContract.abi : '',
|
||||
contract
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -146,7 +163,7 @@ e":"a", "type":"uint256"}], "name":"foo", "outputs": [] }]';
|
|||
}
|
||||
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
contracts: getNetworkContracts(state)
|
||||
contracts: getNetworkContracts(state) || []
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(InteractForm);
|
||||
export default connect<StateProps, {}>(mapStateToProps)(InteractForm);
|
||||
|
|
|
@ -46,9 +46,14 @@ class InteractClass extends Component<DispatchProps, State> {
|
|||
public render() {
|
||||
const { showExplorer, currentContract } = this.state;
|
||||
|
||||
const interactProps = {
|
||||
accessContract: this.accessContract,
|
||||
resetState: this.resetState
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="Interact Tab-content-pane" role="main">
|
||||
<InteractForm accessContract={this.accessContract} resetState={this.resetState} />
|
||||
<InteractForm {...interactProps} />
|
||||
<hr />
|
||||
{showExplorer &&
|
||||
currentContract && (
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@import "./variables";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/mixins";
|
||||
@import './variables';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/mixins';
|
||||
|
||||
@mixin bg-gradient {
|
||||
background: $ether-navy;
|
||||
|
@ -16,7 +16,7 @@
|
|||
|
||||
@mixin clearfix {
|
||||
&:after {
|
||||
content: "";
|
||||
content: '';
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
|||
@mixin mono {
|
||||
font-family: $font-family-monospace;
|
||||
font-weight: normal;
|
||||
letter-spacing: .02em;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
@mixin ellipsis {
|
||||
|
@ -96,3 +96,11 @@
|
|||
transition-delay: 400ms, 400ms, 300ms;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin input-shadow($color) {
|
||||
box-shadow: inset 0 1px 1px rgba(black, 0.075), 0 0 1px rgba($color, 0.5);
|
||||
}
|
||||
|
||||
@mixin input-focus-shadow($color) {
|
||||
box-shadow: inset 0 1px 2px rgba(black, 0.125), 0 0 1px rgba($color, 0.5);
|
||||
}
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
// Where shared common styles live
|
||||
|
||||
// --- BOOTSTRAP ---
|
||||
@import "./variables";
|
||||
@import "./mixins";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/normalize";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/print";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/scaffolding";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/type";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/code";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/grid";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/tables";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/forms";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/buttons";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/dropdowns";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/button-groups";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/input-groups";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/alerts";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/wells";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/close";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/utilities";
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities";
|
||||
@import './variables';
|
||||
@import './mixins';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/normalize';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/print';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/scaffolding';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/type';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/code';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/grid';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/tables';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/forms';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/buttons';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/dropdowns';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/button-groups';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/input-groups';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/alerts';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/wells';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/close';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/utilities';
|
||||
@import '~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities';
|
||||
|
||||
// --- RC SLIDER ---
|
||||
@import "~rc-slider/assets/index.css";
|
||||
@import '~rc-slider/assets/index.css';
|
||||
|
||||
// --- React Select ---
|
||||
@import '~react-select/dist/react-select.css';
|
||||
|
||||
// --- CUSTOM ---
|
||||
@import "./styles/badbrowser";
|
||||
@import "./styles/noscript";
|
||||
@import "./styles/overrides";
|
||||
@import "./styles/scaffolding";
|
||||
@import "./styles/tab";
|
||||
@import "./fonts";
|
||||
@import './styles/badbrowser';
|
||||
@import './styles/noscript';
|
||||
@import './styles/overrides';
|
||||
@import './styles/scaffolding';
|
||||
@import './styles/tab';
|
||||
@import './fonts';
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
// This contains all of the bootstrap style overrides we do. Files should
|
||||
// correspond to the bootstrap filename, and be placed in the overrides/ folder
|
||||
@import "./overrides/alerts";
|
||||
@import "./overrides/buttons";
|
||||
@import "./overrides/button-groups";
|
||||
@import "./overrides/dropdowns";
|
||||
@import "./overrides/forms";
|
||||
@import "./overrides/grid";
|
||||
@import "./overrides/input-groups";
|
||||
@import "./overrides/type";
|
||||
@import './overrides/alerts';
|
||||
@import './overrides/buttons';
|
||||
@import './overrides/button-groups';
|
||||
@import './overrides/dropdowns';
|
||||
@import './overrides/forms';
|
||||
@import './overrides/grid';
|
||||
@import './overrides/input-groups';
|
||||
@import './overrides/type';
|
||||
|
||||
// Other overrides
|
||||
@import './overrides/react-select';
|
||||
|
||||
// And an override for rc-slider
|
||||
@import "./overrides/rc-slider";
|
||||
@import './overrides/rc-slider';
|
||||
|
|
|
@ -33,12 +33,12 @@ input[readonly] {
|
|||
margin-top: $space-sm;
|
||||
margin-bottom: $space-sm;
|
||||
transition: $transition;
|
||||
padding: $input-padding;
|
||||
|
||||
&:focus {
|
||||
border-color: $input-border-focus;
|
||||
outline: 0;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
|
||||
0 0 1px rgba($brand-primary, 0.5);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 1px rgba($brand-primary, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,13 +63,11 @@ select.form-control {
|
|||
// Custom feedback classes
|
||||
@mixin form-control-state($color) {
|
||||
border-color: lighten($color, 20%);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075)
|
||||
0 0 1px rgba($color, 0.1);
|
||||
@include input-shadow($color);
|
||||
|
||||
&:focus {
|
||||
border-color: lighten($color, 5%);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.125),
|
||||
0 0 1px rgba($color, 0.5);
|
||||
@include input-focus-shadow($color);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
// This syntax is necessary to override css styles in react-select
|
||||
.Select {
|
||||
font-size: 1rem;
|
||||
.Select-control {
|
||||
height: $input-height-base;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
font-weight: 400;
|
||||
border-radius: 0px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
border: 1px solid $input-border;
|
||||
transition: $transition;
|
||||
&:hover {
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
border: 1px solid $input-border;
|
||||
}
|
||||
.Select-value {
|
||||
padding-left: $input-padding-x;
|
||||
padding-right: $input-padding-x;
|
||||
.Select-value-label {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.Select-arrow-zone {
|
||||
float: right;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
.Select-menu-outer {
|
||||
box-sizing: content-box;
|
||||
font-weight: 400;
|
||||
border: 1px solid $input-border;
|
||||
width: calc(100% - 1px);
|
||||
}
|
||||
.Select-placeholder {
|
||||
color: #d3d3d3;
|
||||
padding: 0px $input-padding-x;
|
||||
line-height: $input-height-base;
|
||||
}
|
||||
.Select-input {
|
||||
opacity: 0;
|
||||
}
|
||||
&.is-open {
|
||||
.Select-control {
|
||||
border: 1px solid $input-border;
|
||||
}
|
||||
}
|
||||
&.is-focused {
|
||||
&:not(.is-open):not(.is-invalid) {
|
||||
.Select-control {
|
||||
border-color: $brand-primary;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 1px rgba(14, 151, 192, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin react-select-control-state($color) {
|
||||
.Select-control {
|
||||
border-color: lighten($color, 20%);
|
||||
@include input-shadow($color);
|
||||
}
|
||||
.Select-menu-outer {
|
||||
border-color: lighten($color, 20%);
|
||||
@include input-shadow($color);
|
||||
}
|
||||
|
||||
&.is-focused {
|
||||
.Select-control {
|
||||
border-color: lighten($color, 5%);
|
||||
@include input-focus-shadow($color);
|
||||
}
|
||||
.Select-menu-outer {
|
||||
border-color: lighten($color, 5%);
|
||||
@include input-focus-shadow($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-valid {
|
||||
@include react-select-control-state($brand-success);
|
||||
}
|
||||
|
||||
&.is-invalid {
|
||||
@include react-select-control-state($brand-danger);
|
||||
}
|
||||
|
||||
&.is-warning {
|
||||
@include react-select-control-state($brand-warning);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,9 @@ $input-color: #333333;
|
|||
$input-border: $gray-lighter;
|
||||
$input-border-focus: rgba($brand-primary, 0.6);
|
||||
$input-color-placeholder: darken($gray-lighter, 10%);
|
||||
$input-padding-x: 1rem;
|
||||
$input-padding-y: 0.75rem;
|
||||
$input-padding: $input-padding-y $input-padding-x;
|
||||
|
||||
$input-height-base: 2.55rem;
|
||||
$input-height-large: 4rem;
|
||||
|
@ -20,7 +23,7 @@ $input-group-addon-border-color: $input-border;
|
|||
$cursor-disabled: default;
|
||||
|
||||
$dropdown-bg: #fff;
|
||||
$dropdown-border: rgba(0, 0, 0, .15);
|
||||
$dropdown-border: rgba(0, 0, 0, 0.15);
|
||||
$dropdown-fallback-border: $gray-lighter;
|
||||
$dropdown-divider-bg: #e5e5e5;
|
||||
$dropdown-link-color: $ether-navy;
|
||||
|
|
Loading…
Reference in New Issue