-
Notifications
You must be signed in to change notification settings - Fork 10
Dropdown and Checkbox component #49
Changes from 32 commits
bfe3b4e
66f823e
68a244c
d0e4ec8
a2e2a6f
5a3e147
695375b
f1d72eb
913bd6c
e75300a
ddd0f7f
604bf2b
ed0a273
441b6c5
7bed718
fcee961
7110208
5136372
6f343c8
84efbc2
685b524
ef5a7f2
b8485ed
57a0882
cbeeefd
9292d7f
f300722
a3eeb71
251e176
1f7d351
c006d59
b38a9f6
55a458b
d68bebf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import React, { PropTypes } from 'react'; | ||
import { getClassNamesWithMods } from '../_helpers'; | ||
|
||
/** | ||
* Checkbox component. | ||
*/ | ||
function Checkbox({ | ||
checked, | ||
children, | ||
disabled, | ||
mods = [], | ||
name, | ||
onChange, | ||
}) { | ||
disabled && mods.push('is-disabled'); | ||
const className = getClassNamesWithMods('ui-checkbox', mods); | ||
|
||
return ( | ||
<label className={className} htmlFor={name}> | ||
<input | ||
aria-checked={checked} | ||
checked={checked} | ||
disabled={disabled} | ||
id={name} | ||
onChange={onChange} | ||
role="radio" | ||
type="checkbox" | ||
/> | ||
<span className="ui-checkbox__text" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you have a closed |
||
{children} | ||
</label> | ||
); | ||
} | ||
|
||
Checkbox.defaultProps = { | ||
checked: false, | ||
disabled: false, | ||
name: '', | ||
}; | ||
|
||
Checkbox.propTypes = { | ||
/** | ||
* Represents the state of the checkbox. | ||
*/ | ||
checked: PropTypes.bool, | ||
|
||
/** | ||
* Checkbox label. | ||
*/ | ||
children: PropTypes.oneOfType([ | ||
PropTypes.string, | ||
PropTypes.element, | ||
]), | ||
|
||
/** | ||
* Current activity state of the checkbox. | ||
*/ | ||
disabled: PropTypes.bool, | ||
|
||
/** | ||
* You can provide set of custom modifications. | ||
*/ | ||
mods: PropTypes.arrayOf(PropTypes.string), | ||
|
||
/** | ||
* Represents the element's name. | ||
*/ | ||
name: PropTypes.string.isRequired, | ||
|
||
/** | ||
* Represents the callback for onChange event. | ||
*/ | ||
onChange: PropTypes.func.isRequired, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is it required? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @EduardTrutsyk good question. I am assuming it's because this component is a controlled component and therefore it requires an However, I do agree that either we decide to not have a controlled component (therefore the style and value will come from the normal |
||
}; | ||
|
||
export default Checkbox; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
Basic checkbox: | ||
|
||
<div> | ||
<div> | ||
<Checkbox name="checkboxTest1" onChange={() => {alert('without text')}}/><br/><br/> | ||
<Checkbox name="checkboxTest2" checked onChange={() => {alert('checked')}}>checked</Checkbox><br/> | ||
<Checkbox name="checkboxTest3" disabled onChange={() => {alert('disabled')}}>disabled</Checkbox><br/> | ||
<Checkbox | ||
name="checkboxTest4" | ||
checked={state.value} | ||
onChange={() => {setState({value: !state.value})}} | ||
inputAttr={{role: "radio"}}> | ||
can toggle | ||
</Checkbox> | ||
</div> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
.ui-checkbox { | ||
cursor: pointer; | ||
display: flex; | ||
font-family: $tx-generic-font-secondary-font-family, $tx-generic-font-secondary-generic-family; | ||
font-weight: $tx-generic-font-primary-weight-regular; | ||
padding: 0; | ||
user-select: none; | ||
|
||
&.ui-checkbox_is-disabled { | ||
cursor: default; | ||
color: rgba($tx-checkbox-text-disabled-color, .3); | ||
} | ||
|
||
.ui-checkbox__text { | ||
font-size: 13px; | ||
} | ||
|
||
input[type="checkbox"] { | ||
display: none; | ||
|
||
& ~ .ui-checkbox__text { | ||
color: $tx-checkbox-text-color; | ||
padding-left: 25px; | ||
position: relative; | ||
-webkit-tap-highlight-color: transparent; | ||
|
||
&:before { | ||
background-color: $tx-checkbox-text-background-color; | ||
border: 2px solid $tx-checkbox-text-border-color; | ||
content: ''; | ||
height: 12px; | ||
left: 0; | ||
position: absolute; | ||
top: 0; | ||
width: 12px; | ||
} | ||
} | ||
|
||
& ~ .ui-checkbox__text:before { | ||
border-radius: 3px; | ||
} | ||
|
||
& ~ .ui-checkbox__text:hover { | ||
color: $tx-checkbox-text-hover-color; | ||
} | ||
|
||
&:checked { | ||
& ~ .ui-checkbox__text { | ||
&:before { | ||
background: $tx-checkbox-text-checked-background-color; | ||
border-color: $tx-checkbox-text-checked-border-color; | ||
border-radius: 3px; | ||
} | ||
|
||
&:after { | ||
background: transparent; | ||
border: 3px solid $tx-checkbox-text-icon-checked-border-color; | ||
border-radius: 3px; | ||
border-right: none; | ||
border-top: none; | ||
content: ''; | ||
height: 4px; | ||
left: 3px; | ||
position: absolute; | ||
top: 3px; | ||
transform: rotate(-45deg); | ||
width: 7px; | ||
} | ||
} | ||
} | ||
|
||
&:disabled { | ||
& ~ .ui-checkbox__text { | ||
color: rgba($tx-checkbox-text-disabled-color, .3); | ||
|
||
&:before { | ||
border-color: rgba($tx-checkbox-text-disabled-border-color, .3); | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import React, { Component, PropTypes } from 'react'; | ||
import Select from 'react-select'; | ||
|
||
import { getClassNamesWithMods } from '../_helpers'; | ||
import DropdownFilterOptionComponent from './dropdownFilterOptionComponent'; | ||
|
||
/** | ||
* DropDown component | ||
*/ | ||
class DropDown extends Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
this.onChange = this.onChange.bind(this); | ||
this.menuRenderer = this.menuRenderer.bind(this); | ||
} | ||
|
||
optionRef = (onOptionRef, isSelected) => ref => onOptionRef(ref, isSelected); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you change it to a simple method instead of an arrow function? You're returning another function it gets complicated to read: static optionRef(onOptionRef, isSelected) {
return ref => onOptionRef(ref, isSelected);
}, And then change its usage to: |
||
|
||
/** | ||
* Overriding the internal method of react-select for fix autoscrolling | ||
*/ | ||
menuRenderer({ | ||
focusedOption, | ||
instancePrefix, | ||
onFocus, | ||
onSelect, | ||
optionClassName, | ||
optionComponent, | ||
optionRenderer, | ||
options, | ||
valueArray, | ||
valueKey, | ||
onOptionRef, | ||
}) { | ||
let Option = optionComponent; | ||
|
||
return options.map((option, i) => { | ||
let isSelected = valueArray && valueArray.indexOf(option) > -1; | ||
let isFocused = option === focusedOption; | ||
const classes = ['Select-option']; | ||
|
||
optionClassName && classes.push(optionClassName); | ||
isSelected && classes.push('is-selected'); | ||
isFocused && classes.push('is-focused'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now, you can use object instead of array, to apply classes based on props: const mods = {
'is-focused': isFocused,
'is-selecte': isSelected
};
<Option classNames={getClassNamesWithMods('Select-option', mods)} /> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need class names without concatenation |
||
option.disabled && classes.push('is-disabled'); | ||
const optionClass = classes.join(' '); | ||
|
||
return ( | ||
<Option | ||
className={optionClass} | ||
instancePrefix={instancePrefix} | ||
isDisabled={option.disabled} | ||
isFocused={this.props.filterMode ? false : isFocused} | ||
isSelected={isSelected} | ||
key={`option-${i}-${option[valueKey]}`} | ||
onFocus={onFocus} | ||
onSelect={onSelect} | ||
option={option} | ||
optionIndex={i} | ||
ref={this.optionRef(onOptionRef, isSelected)} | ||
> | ||
{optionRenderer(option, i)} | ||
</Option> | ||
); | ||
}); | ||
} | ||
|
||
/** | ||
* @param {Array} changedOptions | ||
*/ | ||
onChange(changedOptions) { | ||
if (!this.props.filterMode) { | ||
this.props.onChange(changedOptions); | ||
return; | ||
} | ||
|
||
const option = changedOptions[0]; | ||
const optionIndex = this.props.options.findIndex(o => o.value === option.value); | ||
this.props.onChange(option, optionIndex, this.props.filterKey); | ||
} | ||
|
||
render() { | ||
const isFiltermode = this.props.filterMode; | ||
const mods = []; | ||
isFiltermode && mods.push('filter'); | ||
isFiltermode && this.props.options.some(option => option.checked) && mods.push('state-active'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
|
||
const className = getClassNamesWithMods('ui-dropdown', mods); | ||
|
||
return ( | ||
<Select | ||
className={className} | ||
clearable={this.props.clearable} | ||
menuRenderer={this.menuRenderer} | ||
multi={isFiltermode ? true : this.props.multi} | ||
name={this.props.name} | ||
onChange={this.onChange} | ||
optionComponent={isFiltermode ? DropdownFilterOptionComponent : undefined} | ||
options={this.props.options} | ||
placeholder={this.props.placeholder} | ||
scrollMenuIntoView={this.props.scrollMenuIntoView} | ||
searchable={this.props.searchable} | ||
value={this.props.value} | ||
/> | ||
); | ||
} | ||
} | ||
|
||
DropDown.defaultProps = { | ||
clearable: false, | ||
filterMode: false, | ||
multi: false, | ||
placeholder: '', | ||
scrollMenuIntoView: false, | ||
searchable: false, | ||
}; | ||
|
||
DropDown.propTypes = { | ||
/** | ||
* Should it be possible to reset value | ||
*/ | ||
clearable: PropTypes.bool, | ||
/** | ||
* Filter key | ||
*/ | ||
filterKey: PropTypes.string, | ||
/** | ||
* Filter mode | ||
*/ | ||
filterMode: PropTypes.bool, | ||
/** | ||
* Multi-value input | ||
*/ | ||
multi: PropTypes.bool, | ||
/** | ||
* Generates a hidden <input /> tag with this field name for html forms | ||
*/ | ||
name: PropTypes.string, | ||
/** | ||
* onChange handler: function (newValue) {} | ||
*/ | ||
onChange: PropTypes.func.isRequired, | ||
/** | ||
* Array of options | ||
*/ | ||
options: PropTypes.array.isRequired, | ||
/** | ||
* Field placeholder, displayed when there's no value | ||
*/ | ||
placeholder: PropTypes.string, | ||
/** | ||
* Boolean to enable the viewport to shift so that the full menu fully visible when engaged | ||
*/ | ||
scrollMenuIntoView: PropTypes.bool, | ||
/** | ||
* Whether to enable searching feature or not | ||
*/ | ||
searchable: PropTypes.bool, | ||
/** | ||
* Initial field value | ||
*/ | ||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array, PropTypes.object]), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
}; | ||
|
||
export default DropDown; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lovely ❤️