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