instantjob-recruiter-client
Version:
a set of tools for creating an instantjob recruiter react client
254 lines (234 loc) • 7.75 kB
JSX
import React, {Component} from 'react'
import classNames from 'classnames/bind'
import {MdCheck} from 'react-icons/lib/md'
import {color} from '../common/constants'
import {array_contains, hash_with_key} from '../common/utilities'
import auto_bind from '../common/auto_bind'
import Button from 'components/utils/button'
import SearchBar from './search_bar'
import Hover from 'components/utils/hover'
import Field from './field'
import {new_values_fuse, action_create_value} from '../common/fields'
import store from '../common/store'
import {alert_failure} from '../actions/display'
let cx = classNames.bind(require('../styles/field.scss'))
class NonExclusiveField extends Component {
constructor(props) {
super(props)
this.state = {
comment: props.comment,
active: false,
query: "",
results: [],
}
auto_bind(this)
}
componentDidMount() {
this.mounted = true
if (this.should_set_state) {
this.setState(this.should_set_state)
this.should_set_state = false
}
}
componentWillUnmount() {
this.mounted = false
}
on_query_change(query, results) {
if (this.mounted) {
this.setState({query, results})
} else {
this.should_set_state = {query, results}
}
}
create_value() {
this.search_bar.set_query("")
this.props.create_value(this.state.query)
.then((id) => {
this.props.on_select_value(id)
}, () => {})
}
get_default_results() {
return this.props.values.sort((a, b) => a.name.localeCompare(b.name)).map((value) => value.id)
}
deactivate() {
this.setState({active: false})
}
activate() {
this.setState({active: true})
this.focus_search_bar()
}
focus_search_bar() {
setTimeout(() => {
if (this.mounted && this.search_bar) {
this.search_bar.focus()
}
})
}
toggle_selected(selected, value_id) {
return () => {
this.should_blur = false
this.focus_search_bar()
if (selected) {
this.props.on_deselect_value(value_id)
} else {
this.props.on_select_value(value_id)
}
}
}
render_hoverable(hover) {
let editing = this.state.active && this.props.editable
let use_ellipsis = !hover && !editing
let {selected_values} = this.props
if (this.props.ellipsis && use_ellipsis) {
selected_values = selected_values.slice(0, this.props.ellipsis)
}
let values_hash = hash_with_key(this.props.values)
let displayed_value_ids = this.props.searchable ? this.state.results : this.get_default_results()
return (
<div>
<div
tabIndex={0}
onFocus={this.activate}
className={cx('field__display', {'field__display_empty': this.props.selected_values.length == 0, 'field__display_magnify': hover || editing})}
>
{this.props.selected_values.length == 0 ? (
<div className={cx('field__label', {'field__label_ellipsis': this.props.field_ellipsis})} style={this.props.field_ellipsis ? {maxWidth: this.props.field_ellipsis} : {}}>
{this.props.placeholder || <span>…</span>}
</div>
) : (
selected_values.sort((a, b) => a.name.localeCompare(b.name)).map((value) => (
<SelectedValue
key={value.id}
name={value.name}
ellipsis={use_ellipsis && this.props.value_ellipsis}
/>
))
)}
{this.props.ellipsis && selected_values.length < this.props.selected_values.length ? (
<div style={{...styles.pill, color: color('primary', 'light')}}>…</div>
) : null}
</div>
{this.props.editable ? (
<div className={cx('field__selection-values')} onMouseLeave={this.deactivate}>
{this.props.searchable ? (
<SearchBar
ref={(search_bar) => this.search_bar = search_bar}
key={'search_bar'}
className={cx('field__search-bar')}
make_fuse={new_values_fuse}
items={this.props.values}
default_results={this.get_default_results()}
on_query_change={this.on_query_change}
placeholder=""
/>
) : null}
<div className={cx('field__selection-values-container')}>
{displayed_value_ids.length == 0 ? (
this.props.allow_creating_values && this.state.query.length > 0 ? (
<div style={{padding: 5, display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
<Button onClick={this.create_value}>
Ajouter
</Button>
</div>
) : (
<div style={{padding: 10, color: color('black', 'bright'), textAlign: 'center', fontStyle: 'italic', backgroundColor: color('black', 'pale')}}>
Aucune valeur
</div>
)
) : (
displayed_value_ids.map((id) => values_hash[id])
.filter(Boolean)
.map((value) => {
let selected = array_contains(this.props.selected_values, value)
return (
<SelectionValue
key={value.id}
name={value.name}
selected={selected}
on_click={this.toggle_selected(selected, value.id)}
/>
)
})
)}
</div>
</div>
) : null}
</div>
)
}
render() {
return (
<Field
{...this.props}
category="non-exclusive"
className={cx({
'field_selected-values_empty': this.props.selected_values.length == 0,
'field_active': this.state.active && this.props.editable
})}
>
<Hover>
{this.render_hoverable}
</Hover>
</Field>
)
}
}
class SelectedValue extends Component {
render() {
return (
<div className={cx('field__selected-value', {'field__selected-value_ellipsis': this.props.ellipsis})} style={this.props.ellipsis ? {maxWidth: this.props.ellipsis} : {}}>
{this.props.name == "" ? (
<span style={{opacity: 0.54, fontStyle: 'italic'}}>Valeur sans nom</span>
) : (
<span>{this.props.name}</span>
)}
</div>
)
}
}
class SelectionValue extends Component {
render() {
return (
<div onClick={this.props.on_click} className={cx('field__selection-value', {'field__selection-value_selected': this.props.selected})}>
{this.props.name == "" ? (
<span style={{opacity: 0.54, fontStyle: 'italic'}}>Valeur sans nom</span>
) : (
<span>{this.props.name}</span>
)}
<span style={{width: 30, textAlign: 'center'}}>
{this.props.selected ? (
<MdCheck />
) : null}
</span>
</div>
)
}
}
const styles = {
pill: {
borderRadius: 5, paddingLeft: 14, paddingTop: 1, paddingRight: 14, paddingBottom: 1,
},
truncate: {
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
},
}
NonExclusiveField.defaultProps = {
name: "",
show_label: true,
selected_values: [],
values: [],
on_select_value: (id) => {},
on_deselect_value: (id) => {},
ellipsis: false,
placeholder: "",
value_ellipsis: false,
field_ellipsis: false,
edit_mode: 'hover',
editable: true,
searchable: true,
allow_creating_values: true,
create_value: (value) => new Promise((resolve, reject) => reject()),
}
export default NonExclusiveField