Compare commits
5 Commits
2.0.0-beta
...
dev
Author | SHA1 | Date |
---|---|---|
Vitaliy Filippov | e745e3332e | |
Vitaliy Filippov | 8690e3a709 | |
Vitaliy Filippov | ba3993c811 | |
Vitaliy Filippov | 0e31a465c7 | |
Vitaliy Filippov | fce0362b5b |
|
@ -383,17 +383,17 @@ const factory = (Chip, Input) => {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
placeholder, allowClear, className, clearTooltip, disabled,
|
placeholder, allowClear, className, clearTooltip, disabled,
|
||||||
error, label, value, selectedPosition, theme, multiple
|
error, label, value, selectedPosition, style, theme, multiple
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const inputProps = this.props.inputProps || {};
|
const inputProps = this.props.inputProps || {};
|
||||||
const outerClassName = classnames(theme.autocomplete, {
|
const outerClassName = classnames(theme.autocomplete, {
|
||||||
[theme.focus]: this.state.focus,
|
[theme.focus]: this.state.focus,
|
||||||
}, className);
|
}, className);
|
||||||
const withClear = allowClear && (multiple
|
const withClear = allowClear && !disabled && (multiple
|
||||||
? value && Object.keys(value).length > 0
|
? value && Object.keys(value).length > 0
|
||||||
: value != null);
|
: value != null);
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox="autocomplete" className={outerClassName}>
|
<div data-react-toolbox="autocomplete" className={outerClassName} style={style}>
|
||||||
{selectedPosition === 'above' ? this.renderSelected() : null}
|
{selectedPosition === 'above' ? this.renderSelected() : null}
|
||||||
{withClear ? <span
|
{withClear ? <span
|
||||||
className={'material-icons '+theme.clear}
|
className={'material-icons '+theme.clear}
|
||||||
|
|
|
@ -21,11 +21,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.withclear input {
|
.withclear input {
|
||||||
text-indent: 28px !important;
|
text-indent: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.withclear label {
|
.withclear label {
|
||||||
transition-property: top, left, font-size, color !important;
|
transition-property: top, left, font-size, color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputFilled {
|
.inputFilled {
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.withclear input:not(:focus):not(.inputFilled) ~ label {
|
.withclear input:not(:focus):not(.inputFilled) ~ label {
|
||||||
left: 28px !important;
|
left: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.values {
|
.values {
|
||||||
|
|
|
@ -69,6 +69,7 @@ const factory = (Check) => {
|
||||||
className={className}
|
className={className}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
|
style={style}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
{...others}
|
{...others}
|
||||||
|
@ -85,7 +86,6 @@ const factory = (Check) => {
|
||||||
checked={checked}
|
checked={checked}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
rippleClassName={theme.ripple}
|
rippleClassName={theme.ripple}
|
||||||
style={style}
|
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
{label ? <span data-react-toolbox="label" className={theme.text}>{label}</span> : null}
|
{label ? <span data-react-toolbox="label" className={theme.text}>{label}</span> : null}
|
||||||
|
|
|
@ -20,6 +20,8 @@ const factory = (Overlay, Button) => {
|
||||||
[props.theme.active]: props.active,
|
[props.theme.active]: props.active,
|
||||||
}, props.className);
|
}, props.className);
|
||||||
|
|
||||||
|
const style = props.style; // eslint-disable-line
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Portal className={props.theme.wrapper}>
|
<Portal className={props.theme.wrapper}>
|
||||||
<Overlay
|
<Overlay
|
||||||
|
@ -33,7 +35,7 @@ const factory = (Overlay, Button) => {
|
||||||
theme={props.theme}
|
theme={props.theme}
|
||||||
themeNamespace="overlay"
|
themeNamespace="overlay"
|
||||||
/>
|
/>
|
||||||
<div data-react-toolbox="dialog" className={className}>
|
<div data-react-toolbox="dialog" className={className} style={style}>
|
||||||
<section role="body" className={props.theme.body}>
|
<section role="body" className={props.theme.body}>
|
||||||
{props.title ? <h6 className={props.theme.title}>{props.title}</h6> : null}
|
{props.title ? <h6 className={props.theme.title}>{props.title}</h6> : null}
|
||||||
{props.children}
|
{props.children}
|
||||||
|
|
|
@ -1,132 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import ReactToolbox from "../index";
|
|
||||||
|
|
||||||
export interface DropdownTheme {
|
|
||||||
/**
|
|
||||||
* Added to the root element when the dropdown is active.
|
|
||||||
*/
|
|
||||||
active?: string;
|
|
||||||
/**
|
|
||||||
* Added to the root element when it's disabled.
|
|
||||||
*/
|
|
||||||
disabled?: string;
|
|
||||||
/**
|
|
||||||
* Root element class.
|
|
||||||
*/
|
|
||||||
dropdown?: string;
|
|
||||||
/**
|
|
||||||
* Used for the error element.
|
|
||||||
*/
|
|
||||||
error?: string;
|
|
||||||
/**
|
|
||||||
* Added to the inner wrapper if it's errored.
|
|
||||||
*/
|
|
||||||
errored?: string;
|
|
||||||
/**
|
|
||||||
* Used for the inner wrapper of the component.
|
|
||||||
*/
|
|
||||||
field?: string;
|
|
||||||
/**
|
|
||||||
* Used for the the label element.
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
/**
|
|
||||||
* Used when dropdown has required attribute.
|
|
||||||
*/
|
|
||||||
required?: string;
|
|
||||||
/**
|
|
||||||
* Used to highlight the selected value.
|
|
||||||
*/
|
|
||||||
selected?: string;
|
|
||||||
/**
|
|
||||||
* Used as a wrapper for the given template value.
|
|
||||||
*/
|
|
||||||
templateValue?: string;
|
|
||||||
/**
|
|
||||||
* Added to the root element when it's opening up.
|
|
||||||
*/
|
|
||||||
up?: string;
|
|
||||||
/**
|
|
||||||
* Used for each value in the dropdown component.
|
|
||||||
*/
|
|
||||||
value?: string;
|
|
||||||
/**
|
|
||||||
* Used for the list of values.
|
|
||||||
*/
|
|
||||||
values?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DropdownProps extends ReactToolbox.Props {
|
|
||||||
/**
|
|
||||||
* If true the dropdown will preselect the first item if the supplied value matches none of the options' values.
|
|
||||||
* @default true
|
|
||||||
*/
|
|
||||||
allowBlank?: boolean;
|
|
||||||
/**
|
|
||||||
* If true, the dropdown will open up or down depending on the position in the screen.
|
|
||||||
* @default true
|
|
||||||
*/
|
|
||||||
auto?: boolean;
|
|
||||||
/**
|
|
||||||
* Set the component as disabled.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
/**
|
|
||||||
* Give an error string to display under the field.
|
|
||||||
*/
|
|
||||||
error?: string;
|
|
||||||
/**
|
|
||||||
* The text string to use for the floating label element.
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
/**
|
|
||||||
* Used for setting the label from source
|
|
||||||
*/
|
|
||||||
labelKey?: string;
|
|
||||||
/**
|
|
||||||
* Name for the input field.
|
|
||||||
*/
|
|
||||||
name?: string;
|
|
||||||
/**
|
|
||||||
* Callback function that is fired when the component is blurred.
|
|
||||||
*/
|
|
||||||
onBlur?: Function;
|
|
||||||
/**
|
|
||||||
* Callback function that is fired when the component's value changes.
|
|
||||||
*/
|
|
||||||
onChange?: Function;
|
|
||||||
/**
|
|
||||||
* Callback function that is fired when the component is focused.
|
|
||||||
*/
|
|
||||||
onFocus?: Function;
|
|
||||||
/**
|
|
||||||
* If true, the dropdown has a required attribute.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
required?: boolean;
|
|
||||||
/**
|
|
||||||
* Array of data objects with the data to represent in the dropdown.
|
|
||||||
*/
|
|
||||||
source: any[];
|
|
||||||
/**
|
|
||||||
* Callback function that returns a JSX template to represent the element.
|
|
||||||
*/
|
|
||||||
template?: Function;
|
|
||||||
/**
|
|
||||||
* Classnames object defining the component style.
|
|
||||||
*/
|
|
||||||
theme?: DropdownTheme;
|
|
||||||
/**
|
|
||||||
* Default value using JSON data.
|
|
||||||
*/
|
|
||||||
value?: string | number;
|
|
||||||
/**
|
|
||||||
* Used for setting the value from source
|
|
||||||
*/
|
|
||||||
valueKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Dropdown extends React.Component<DropdownProps, {}> { }
|
|
||||||
|
|
||||||
export default Dropdown;
|
|
|
@ -1,247 +0,0 @@
|
||||||
/* eslint-disable */
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import { themr } from 'react-css-themr';
|
|
||||||
import { DROPDOWN } from '../identifiers';
|
|
||||||
import InjectInput from '../input/Input';
|
|
||||||
import events from '../utils/events';
|
|
||||||
|
|
||||||
const factory = (Input) => {
|
|
||||||
class Dropdown extends Component {
|
|
||||||
static propTypes = {
|
|
||||||
allowBlank: PropTypes.bool,
|
|
||||||
allowClear: PropTypes.bool,
|
|
||||||
clearTooltip: PropTypes.string,
|
|
||||||
auto: PropTypes.bool,
|
|
||||||
className: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
error: PropTypes.string,
|
|
||||||
label: PropTypes.string,
|
|
||||||
labelKey: PropTypes.string,
|
|
||||||
name: PropTypes.string,
|
|
||||||
onBlur: PropTypes.func,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
onFocus: PropTypes.func,
|
|
||||||
required: PropTypes.bool,
|
|
||||||
source: PropTypes.arrayOf(PropTypes.oneOfType([
|
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.object,
|
|
||||||
])).isRequired,
|
|
||||||
template: PropTypes.func,
|
|
||||||
theme: PropTypes.shape({
|
|
||||||
active: PropTypes.string,
|
|
||||||
disabled: PropTypes.string,
|
|
||||||
dropdown: PropTypes.string,
|
|
||||||
error: PropTypes.string,
|
|
||||||
errored: PropTypes.string,
|
|
||||||
field: PropTypes.string,
|
|
||||||
label: PropTypes.string,
|
|
||||||
required: PropTypes.string,
|
|
||||||
selected: PropTypes.string,
|
|
||||||
templateValue: PropTypes.string,
|
|
||||||
up: PropTypes.string,
|
|
||||||
value: PropTypes.string,
|
|
||||||
values: PropTypes.string,
|
|
||||||
}),
|
|
||||||
value: PropTypes.oneOfType([
|
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.number,
|
|
||||||
]),
|
|
||||||
valueKey: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
auto: true,
|
|
||||||
className: '',
|
|
||||||
allowBlank: true,
|
|
||||||
allowClear: false,
|
|
||||||
clearTooltip: 'Clear',
|
|
||||||
disabled: false,
|
|
||||||
labelKey: 'label',
|
|
||||||
required: false,
|
|
||||||
valueKey: 'value',
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
active: false,
|
|
||||||
up: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentWillUpdate(nextProps, nextState) {
|
|
||||||
if (!this.state.active && nextState.active) {
|
|
||||||
events.addEventsToDocument(this.getDocumentEvents());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
if (prevState.active && !this.state.active) {
|
|
||||||
events.removeEventsFromDocument(this.getDocumentEvents());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.state.active) {
|
|
||||||
events.removeEventsFromDocument(this.getDocumentEvents());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getDocumentEvents = () => ({
|
|
||||||
click: this.handleDocumentClick,
|
|
||||||
touchend: this.handleDocumentClick,
|
|
||||||
});
|
|
||||||
|
|
||||||
getSelectedItem = () => {
|
|
||||||
for (const item of this.props.source) {
|
|
||||||
if (item[this.props.valueKey] === this.props.value) return item;
|
|
||||||
}
|
|
||||||
return !this.props.allowBlank
|
|
||||||
? this.props.source[0]
|
|
||||||
: undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSelect = (item, event) => {
|
|
||||||
if (!this.props.disabled && this.props.onChange) {
|
|
||||||
if (this.props.name) event.target.name = this.props.name;
|
|
||||||
this.props.onChange(item, event);
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleClick = (event) => {
|
|
||||||
this.open(event);
|
|
||||||
events.pauseEvent(event);
|
|
||||||
if (this.props.onClick) this.props.onClick(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleDocumentClick = (event) => {
|
|
||||||
if (this.state.active && !events.targetIsDescendant(event, ReactDOM.findDOMNode(this))) {
|
|
||||||
this.setState({ active: false });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
close = () => {
|
|
||||||
if (this.state.active) {
|
|
||||||
this.setState({ active: false });
|
|
||||||
if (this.props.onBlur) this.props.onBlur(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open = (event) => {
|
|
||||||
if (this.state.active) return;
|
|
||||||
const client = event.target.getBoundingClientRect();
|
|
||||||
const screenHeight = window.innerHeight || document.documentElement.offsetHeight;
|
|
||||||
const up = this.props.auto ? client.top > ((screenHeight / 2) + client.height) : false;
|
|
||||||
this.setState({ active: true, up });
|
|
||||||
if (this.props.onFocus) this.props.onFocus(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleFocus = (event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
if (!this.props.disabled) this.open(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleBlur = (event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
if (this.state.active) this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTemplateValue(selected) {
|
|
||||||
const { theme } = this.props;
|
|
||||||
const className = classnames(theme.field, {
|
|
||||||
[theme.errored]: this.props.error,
|
|
||||||
[theme.disabled]: this.props.disabled,
|
|
||||||
[theme.required]: this.props.required,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className} onClick={this.handleClick}>
|
|
||||||
<div className={`${theme.templateValue} ${theme.value}`}>
|
|
||||||
{this.props.template(selected)}
|
|
||||||
</div>
|
|
||||||
{this.props.label
|
|
||||||
? (
|
|
||||||
<label className={theme.label}>
|
|
||||||
{this.props.label}
|
|
||||||
{this.props.required ? <span className={theme.required}> * </span> : null}
|
|
||||||
</label>
|
|
||||||
) : null}
|
|
||||||
{this.props.error ? <span className={theme.error}>{this.props.error}</span> : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderValue = (item, idx) => {
|
|
||||||
const { labelKey, theme, valueKey } = this.props;
|
|
||||||
const className = classnames({
|
|
||||||
[theme.selected]: item[valueKey] === this.props.value,
|
|
||||||
[theme.disabled]: item.disabled,
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
key={idx}
|
|
||||||
className={className}
|
|
||||||
onMouseDown={!item.disabled && this.handleSelect.bind(this, item[valueKey])}
|
|
||||||
>
|
|
||||||
{this.props.template ? this.props.template(item) : item[labelKey]}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
allowBlank, allowClear, clearTooltip, auto, labelKey, required, onChange, onFocus, onBlur, // eslint-disable-line no-unused-vars
|
|
||||||
source, template, theme, valueKey, ...others
|
|
||||||
} = this.props;
|
|
||||||
const selected = this.getSelectedItem();
|
|
||||||
const className = classnames(theme.dropdown, {
|
|
||||||
[theme.up]: this.state.up,
|
|
||||||
[theme.active]: this.state.active,
|
|
||||||
[theme.disabled]: this.props.disabled,
|
|
||||||
[theme.required]: this.props.required,
|
|
||||||
[theme.withclear]: allowClear && selected,
|
|
||||||
}, this.props.className);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={className}
|
|
||||||
style={{outline: 'none'}}
|
|
||||||
data-react-toolbox="dropdown"
|
|
||||||
tabIndex="-1"
|
|
||||||
>
|
|
||||||
{allowClear && selected ? <span
|
|
||||||
className={'material-icons '+theme.clear}
|
|
||||||
title={clearTooltip}
|
|
||||||
onClick={(e) => this.props.onChange(null, e)}>clear</span> : null}
|
|
||||||
<Input
|
|
||||||
{...others}
|
|
||||||
tabIndex="0"
|
|
||||||
className={theme.value}
|
|
||||||
onClick={this.handleClick}
|
|
||||||
onBlur={this.handleBlur}
|
|
||||||
onFocus={this.handleFocus}
|
|
||||||
required={this.props.required}
|
|
||||||
readOnly
|
|
||||||
type={template && selected ? 'hidden' : null}
|
|
||||||
theme={theme}
|
|
||||||
themeNamespace="input"
|
|
||||||
value={selected && selected[labelKey] ? selected[labelKey] : ''}
|
|
||||||
/>
|
|
||||||
{template && selected ? this.renderTemplateValue(selected) : null}
|
|
||||||
<ul className={theme.values} style={this.state.up ? {bottom: '100%'} : {top: '100%'}}>
|
|
||||||
{source.map(this.renderValue)}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Dropdown;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Dropdown = factory(InjectInput);
|
|
||||||
export default themr(DROPDOWN)(Dropdown);
|
|
||||||
export { factory as dropdownFactory };
|
|
||||||
export { Dropdown };
|
|
|
@ -1,10 +0,0 @@
|
||||||
:root {
|
|
||||||
--dropdown-value-border-size: calc(var(--input-field-height) / 7);
|
|
||||||
--dropdown-color-white: var(--color-white);
|
|
||||||
--dropdown-color-primary: var(--color-primary);
|
|
||||||
--dropdown-color-primary-contrast: var(--color-primary-contrast);
|
|
||||||
--dropdown-color-disabled: color(var(--color-black) a(26%));
|
|
||||||
--dropdown-value-hover-background: var(--palette-grey-200);
|
|
||||||
--dropdown-overflow-max-height: 45vh;
|
|
||||||
--dropdown-value-border-radius: calc(0.2 * var(--unit));
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { Dropdown } from './Dropdown';
|
|
||||||
|
|
||||||
export { DropdownProps, DropdownTheme } from './Dropdown';
|
|
||||||
export { Dropdown }
|
|
||||||
export default Dropdown;
|
|
|
@ -1,11 +1,26 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { DROPDOWN } from '../identifiers';
|
import { autocompleteFactory } from '../autocomplete';
|
||||||
import { dropdownFactory } from './Dropdown';
|
import { Chip } from '../chip';
|
||||||
import { Input } from '../input';
|
import { Input } from '../input';
|
||||||
import theme from './theme.css';
|
|
||||||
|
|
||||||
const Dropdown = dropdownFactory(Input);
|
import theme from '../autocomplete/theme.css';
|
||||||
const ThemedDropdown = themr(DROPDOWN, theme)(Dropdown);
|
import overrides from './overrides.css';
|
||||||
|
|
||||||
export default ThemedDropdown;
|
const overriddenTheme = { ...theme, inputInputElement: theme.inputInputElement+' '+overrides.inputInputElement };
|
||||||
export { ThemedDropdown as Dropdown };
|
|
||||||
|
const Autocomplete = autocompleteFactory(Chip, Input);
|
||||||
|
const ThemedAutocomplete = themr(AUTOCOMPLETE, overriddenTheme, { withRef: true })(Autocomplete);
|
||||||
|
|
||||||
|
export const Dropdown = (props) => <Autocomplete
|
||||||
|
readOnly
|
||||||
|
multiple={false}
|
||||||
|
keepFocusOnChange={false}
|
||||||
|
allowCreate={false}
|
||||||
|
showSelectedWhenNotInSource={false}
|
||||||
|
suggestionMatch="disabled"
|
||||||
|
{...props}
|
||||||
|
/>;
|
||||||
|
|
||||||
|
export default Dropdown;
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
.inputInputElement {
|
||||||
|
caret-color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
|
@ -1,73 +0,0 @@
|
||||||
# Dropdown
|
|
||||||
|
|
||||||
The Dropdown selects an option between multiple selections. The element displays the current state and a down arrow. When it is clicked, it displays the list of available options.
|
|
||||||
|
|
||||||
<!-- example -->
|
|
||||||
```jsx
|
|
||||||
import Dropdown from 'react-toolbox/lib/dropdown';
|
|
||||||
|
|
||||||
const countries = [
|
|
||||||
{ value: 'EN-gb', label: 'England' },
|
|
||||||
{ value: 'ES-es', label: 'Spain'},
|
|
||||||
{ value: 'TH-th', label: 'Thailand' },
|
|
||||||
{ value: 'EN-en', label: 'USA'}
|
|
||||||
];
|
|
||||||
|
|
||||||
class DropdownTest extends React.Component {
|
|
||||||
state = { value: 'ES-es' };
|
|
||||||
|
|
||||||
handleChange = (value) => {
|
|
||||||
this.setState({value: value});
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
auto
|
|
||||||
onChange={this.handleChange}
|
|
||||||
source={countries}
|
|
||||||
value={this.state.value}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to provide a theme via context, the component key is `RTDropdown`.
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
|
||||||
|:-------------|:-----------|:--------|:------------|
|
|
||||||
| `allowBlank` | `Boolean` | `true` | If false the dropdown will preselect the first item if the supplied value matches none of the options' values.|
|
|
||||||
| `auto` | `Boolean` | `true` | If true, the dropdown will open up or down depending on the position in the screen.|
|
|
||||||
| `className` | `String` | `''` | Set the class to give custom styles to the dropdown.|
|
|
||||||
| `disabled` | `Boolean` | `false` | Set the component as disabled.|
|
|
||||||
| `error` | `String` | | Give an error string to display under the field.|
|
|
||||||
| `label` | `String` | | The text string to use for the floating label element.|
|
|
||||||
| `onBlur` | `Function` | | Callback function that is fired when the component is blurred.|
|
|
||||||
| `onChange` | `Function` | | Callback function that is fired when the component's value changes.|
|
|
||||||
| `onFocus` | `Function` | | Callback function that is fired when the component is focused.|
|
|
||||||
| `source` | `Array` | | Array of data objects with the data to represent in the dropdown.|
|
|
||||||
| `template` | `Function` | | Callback function that returns a JSX template to represent the element.|
|
|
||||||
| `value` | `String` | | Default value using JSON data.|
|
|
||||||
| `required` | `Boolean` | `false` | If true, the dropdown has a required attribute.|
|
|
||||||
|
|
||||||
## Theming
|
|
||||||
|
|
||||||
This component uses an `Input` under the covers. The theme object is passed down namespaced under `input` keyword. This means you can use the same theme classNames from `Input` component but namespaced with `input`. For example, to style the label you have to use `inputLabel` className.
|
|
||||||
|
|
||||||
| Name | Description|
|
|
||||||
|:----------------|:-----------|
|
|
||||||
| `active` | Added to the root element when the dropdown is active.|
|
|
||||||
| `disabled` | Added to the root element when it's disabled.|
|
|
||||||
| `dropdown` | Root element class.|
|
|
||||||
| `error` | Used for the error element.|
|
|
||||||
| `errored` | Added to the inner wrapper if it's errored.|
|
|
||||||
| `field` | Used for the inner wrapper of the component.|
|
|
||||||
| `label` | Used for the the label element.|
|
|
||||||
| `selected` | Used to highlight the selected value.|
|
|
||||||
| `templateValue` | Used as a wrapper for the given template value.|
|
|
||||||
| `up` | Added to the root element when it's opening up.|
|
|
||||||
| `value` | Used for each value in the dropdown component.|
|
|
||||||
| `values` | Used for the list of values.|
|
|
|
@ -1,180 +0,0 @@
|
||||||
@import '../colors.css';
|
|
||||||
@import '../variables.css';
|
|
||||||
@import '../input/config.css';
|
|
||||||
@import './config.css';
|
|
||||||
|
|
||||||
.dropdown {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
@apply --reset;
|
|
||||||
|
|
||||||
&:not(.active) {
|
|
||||||
& > .values {
|
|
||||||
max-height: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
& > .label,
|
|
||||||
& > .value {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .values {
|
|
||||||
box-shadow: var(--zdepth-shadow-1);
|
|
||||||
max-height: var(--dropdown-overflow-max-height);
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.up) > .values {
|
|
||||||
bottom: auto;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.up > .values {
|
|
||||||
bottom: 0;
|
|
||||||
top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
cursor: normal;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.withclear input {
|
|
||||||
text-indent: 28px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
& > input {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
border-left: var(--dropdown-value-border-size) solid transparent;
|
|
||||||
border-right: var(--dropdown-value-border-size) solid transparent;
|
|
||||||
border-top: var(--dropdown-value-border-size) solid var(--input-text-bottom-border-color);
|
|
||||||
content: '';
|
|
||||||
height: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
right: var(--input-chevron-offset);
|
|
||||||
top: 50%;
|
|
||||||
transition: transform var(--animation-duration) var(--animation-curve-default);
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.field {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: var(--input-padding) 0;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&.errored {
|
|
||||||
padding-bottom: 0;
|
|
||||||
|
|
||||||
& > .label {
|
|
||||||
color: var(--input-text-error-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .templateValue {
|
|
||||||
border-bottom: 1px solid var(--input-text-error-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .label > .required {
|
|
||||||
color: var(--input-text-error-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
cursor: normal;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
& > .templateValue {
|
|
||||||
border-bottom-style: dotted;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.templateValue {
|
|
||||||
background-color: var(--input-text-background-color);
|
|
||||||
border-bottom: 1px solid var(--input-text-bottom-border-color);
|
|
||||||
color: var(--color-text);
|
|
||||||
min-height: var(--input-field-height);
|
|
||||||
padding: var(--input-field-padding) 0;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
color: var(--input-text-label-color);
|
|
||||||
font-size: var(--input-label-font-size);
|
|
||||||
left: 0;
|
|
||||||
line-height: var(--input-field-font-size);
|
|
||||||
position: absolute;
|
|
||||||
top: var(--input-focus-label-top);
|
|
||||||
|
|
||||||
& .required {
|
|
||||||
color: var(--input-text-error-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
color: var(--input-text-error-color);
|
|
||||||
font-size: var(--input-label-font-size);
|
|
||||||
line-height: var(--input-underline-height);
|
|
||||||
margin-bottom: calc(-1 * var(--input-underline-height));
|
|
||||||
}
|
|
||||||
|
|
||||||
.values {
|
|
||||||
background-color: var(--dropdown-color-white);
|
|
||||||
border-radius: var(--dropdown-value-border-radius);
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 0;
|
|
||||||
position: absolute;
|
|
||||||
transition-duration: var(--animation-duration);
|
|
||||||
transition-property: max-height, box-shadow;
|
|
||||||
transition-timing-function: var(--animation-curve-default);
|
|
||||||
width: 100%;
|
|
||||||
z-index: var(--z-index-high);
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
cursor: pointer;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: var(--unit);
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:hover:not(.disabled) {
|
|
||||||
background-color: var(--dropdown-value-hover-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
color: var(--dropdown-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
color: var(--dropdown-color-disabled);
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.clear {
|
|
||||||
cursor: pointer;
|
|
||||||
display: block;
|
|
||||||
left: -4px;
|
|
||||||
padding: 4px;
|
|
||||||
position: absolute;
|
|
||||||
top: 12px;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
|
@ -163,7 +163,7 @@ const factory = (FontIcon) => {
|
||||||
render() {
|
render() {
|
||||||
const { children, defaultValue, disabled, error, floating, hint, icon,
|
const { children, defaultValue, disabled, error, floating, hint, icon,
|
||||||
name, label: labelText, maxLength, multiline, required, role,
|
name, label: labelText, maxLength, multiline, required, role,
|
||||||
theme, type, value, onKeyPress, rows = 1, ...others } = this.props;
|
theme, type, value, onKeyPress, rows = 1, style, ...others } = this.props; // eslint-disable-line
|
||||||
const length = maxLength && value ? value.length : 0;
|
const length = maxLength && value ? value.length : 0;
|
||||||
const labelClassName = classnames(theme.label, { [theme.fixed]: !floating });
|
const labelClassName = classnames(theme.label, { [theme.fixed]: !floating });
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ const factory = (FontIcon) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox="input" className={className}>
|
<div data-react-toolbox="input" className={className} style={style}>
|
||||||
{React.createElement(multiline ? 'textarea' : 'input', inputElementProps)}
|
{React.createElement(multiline ? 'textarea' : 'input', inputElementProps)}
|
||||||
{icon ? <FontIcon className={theme.icon} value={icon} /> : null}
|
{icon ? <FontIcon className={theme.icon} value={icon} /> : null}
|
||||||
<span className={theme.bar} />
|
<span className={theme.bar} />
|
||||||
|
|
|
@ -58,12 +58,13 @@ const factory = (Thumb) => {
|
||||||
disabled,
|
disabled,
|
||||||
onChange, // eslint-disable-line no-unused-vars
|
onChange, // eslint-disable-line no-unused-vars
|
||||||
ripple,
|
ripple,
|
||||||
|
style, // eslint-disable-line
|
||||||
theme,
|
theme,
|
||||||
...others
|
...others
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const _className = classnames(theme[disabled ? 'disabled' : 'field'], className);
|
const _className = classnames(theme[disabled ? 'disabled' : 'field'], className);
|
||||||
return (
|
return (
|
||||||
<label data-react-toolbox="switch" className={_className}>
|
<label data-react-toolbox="switch" className={_className} style={style}>
|
||||||
<input
|
<input
|
||||||
{...others}
|
{...others}
|
||||||
checked={this.props.checked}
|
checked={this.props.checked}
|
||||||
|
|
|
@ -47,39 +47,44 @@
|
||||||
&.selected { background-color: var(--table-selection-color); }
|
&.selected { background-color: var(--table-selection-color); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* common styles for all kinds of cells */
|
||||||
.table > thead > tr > th,
|
.table > thead > tr > th,
|
||||||
.table > tbody > tr > th,
|
.table > tbody > tr > th,
|
||||||
.table > thead > tr > td,
|
.table > thead > tr > td,
|
||||||
.table > tbody > tr > td,
|
.table > tbody > tr > td,
|
||||||
.rowCell,
|
.rowCell,
|
||||||
.headCell {
|
.headCell {
|
||||||
|
border-bottom: var(--table-dividers);
|
||||||
|
height: var(--table-row-height);
|
||||||
padding: 0 var(--table-column-padding) 12px var(--table-column-padding);
|
padding: 0 var(--table-column-padding) 12px var(--table-column-padding);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
&:first-of-type { padding-left: 24px; }
|
&:first-of-type { padding-left: 24px; }
|
||||||
&:last-of-type { padding-right: 24px; }
|
&:last-of-type { padding-right: 24px; }
|
||||||
&.numeric { text-align: right; }
|
&.numeric { text-align: right; }
|
||||||
}
|
|
||||||
|
|
||||||
.table > thead > tr > td,
|
|
||||||
.table > tbody > tr > td,
|
|
||||||
.rowCell {
|
|
||||||
border-bottom: var(--table-dividers);
|
|
||||||
border-top: var(--table-dividers);
|
|
||||||
height: var(--table-row-height);
|
|
||||||
padding-top: var(--table-cell-top);
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
&.checkboxCell {
|
&.checkboxCell {
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
width: calc(1.8 * var(--unit));
|
width: calc(1.8 * var(--unit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* styles for both header and normal body cells */
|
||||||
|
.table > tbody > tr > th,
|
||||||
|
.table > tbody > tr > td,
|
||||||
|
.rowCell {
|
||||||
|
border-top: var(--table-dividers);
|
||||||
|
padding-top: var(--table-cell-top);
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
&.checkboxCell {
|
||||||
& > * {
|
& > * {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* styles for header cells both in body and header */
|
||||||
.table > thead > tr > th,
|
.table > thead > tr > th,
|
||||||
.table > tbody > tr > th,
|
.table > tbody > tr > th,
|
||||||
.headCell {
|
.headCell {
|
||||||
|
@ -88,14 +93,17 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
height: var(--table-row-height);
|
height: var(--table-row-height);
|
||||||
line-height: calc(2.4 * var(--unit));
|
line-height: calc(2.4 * var(--unit));
|
||||||
padding-bottom: 8px;
|
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* styles for all cells in header */
|
||||||
|
.table > thead > tr > th,
|
||||||
|
.table > thead > tr > td,
|
||||||
|
.headCell {
|
||||||
|
padding-bottom: 8px;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
|
|
||||||
&.checkboxCell {
|
&.checkboxCell {
|
||||||
padding-right: 5px;
|
|
||||||
width: calc(1.8 * var(--unit));
|
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
margin: 0 0 3px;
|
margin: 0 0 3px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,6 +217,9 @@ const tooltipFactory = (options = {}) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleMouseLeaveForChildren = () => {
|
handleMouseLeaveForChildren = () => {
|
||||||
|
if (this.timeout) {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
}
|
||||||
this.timeout = setTimeout(() => {
|
this.timeout = setTimeout(() => {
|
||||||
this.deactivate();
|
this.deactivate();
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "react-toolbox",
|
"name": "react-toolbox",
|
||||||
"description": "A set of React components implementing Google's Material Design specification with the power of CSS Modules.",
|
"description": "A set of React components implementing Google's Material Design specification with the power of CSS Modules.",
|
||||||
"homepage": "http://www.react-toolbox.io",
|
"homepage": "http://www.react-toolbox.io",
|
||||||
"version": "2.0.0-beta.20",
|
"version": "2.0.0-beta.24",
|
||||||
"main": "./lib",
|
"main": "./lib",
|
||||||
"module": "./components",
|
"module": "./components",
|
||||||
"author": {
|
"author": {
|
||||||
|
|
Loading…
Reference in New Issue