UNPKG

d2-ui

Version:
174 lines (148 loc) 5.39 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Subject, Observable } from 'rxjs'; import AutoComplete from 'material-ui/AutoComplete'; import { accessObjectToString } from './utils'; import PermissionPicker from './PermissionPicker.component'; const styles = { container: { fontWeight: '400', padding: 16, backgroundColor: '#F5F5F5', display: 'flex', flexDirection: 'column', justifyContent: 'center', }, innerContainer: { display: 'flex', flexDirection: 'row', flex: 1, }, title: { color: '#818181', paddingBottom: 8, }, searchBox: { backgroundColor: 'white', boxShadow: '2px 2px 2px #cccccc', padding: '0px 16px', marginRight: '16px', }, }; const searchDelay = 300; class UserSearch extends Component { state = { defaultAccess: { meta: { canView: true, canEdit: true }, data: { canView: false, canEdit: false }, }, searchResult: [], searchText: '', }; constructor(props, context) { super(props); context.d2.i18n.addStrings(['add_users_and_user_groups', 'enter_names']); } componentWillMount() { this.inputStream .debounce(() => Observable.timer(searchDelay)) .subscribe((searchText) => { this.fetchSearchResult(searchText); }); } onResultClick = (_, index) => { // Material UI triggers an 'onUpdateInput' when a search result is clicked. Therefore, we // immediately pushes a new item to the search stream to prevent the stream from searching // for the item again. this.inputStream.next(''); const selection = this.state.searchResult[index]; const type = selection.type; delete selection.type; if (type === 'userAccess') { this.props.addUserAccess({ ...selection, access: accessObjectToString(this.state.defaultAccess) }); } else { this.props.addUserGroupAccess({ ...selection, access: accessObjectToString(this.state.defaultAccess) }); } this.clearSearchText(); } inputStream = new Subject(); hasNoCurrentAccess = userOrGroup => this.props.currentAccessIds.indexOf(userOrGroup.id) === -1; fetchSearchResult = (searchText) => { if (searchText === '') { this.handleSearchResult([]); } else { this.props.onSearch(searchText).then(({ users, userGroups }) => { const addType = type => result => ({ ...result, type }); const searchResult = users .map(addType('userAccess')) .filter(this.hasNoCurrentAccess) .concat(userGroups .map(addType('userGroupAccess')) .filter(this.hasNoCurrentAccess), ); this.handleSearchResult(searchResult); }); } } handleSearchResult = (searchResult) => { this.setState({ searchResult }); } handleUpdateInput = (searchText) => { this.inputStream.next(searchText); this.setState({ searchText }); } accessOptionsChanged = (accessOptions) => { this.setState({ defaultAccess: accessOptions, }); } clearSearchText = () => { this.setState({ searchText: '', }); } noFilter = () => true; render() { return ( <div style={styles.container}> <div style={styles.title}> {this.context.d2.i18n.getTranslation('add_users_and_user_groups')} </div> <div style={styles.innerContainer}> <AutoComplete fullWidth openOnFocus filter={this.noFilter} dataSource={this.state.searchResult} dataSourceConfig={{ text: 'displayName', value: 'id' }} hintText={this.context.d2.i18n.getTranslation('enter_names')} onNewRequest={this.onResultClick} onUpdateInput={this.handleUpdateInput} style={styles.searchBox} searchText={this.state.searchText} underlineShow={false} /> <PermissionPicker access={this.state.defaultAccess} accessOptions={{ meta: { canView: true, canEdit: true, noAccess: false }, data: this.props.dataShareable && { canView: true, canEdit: true, noAccess: true }, }} onChange={this.accessOptionsChanged} /> </div> </div> ); } } UserSearch.propTypes = { onSearch: PropTypes.func.isRequired, addUserAccess: PropTypes.func.isRequired, dataShareable: PropTypes.bool.isRequired, addUserGroupAccess: PropTypes.func.isRequired, currentAccessIds: PropTypes.array.isRequired, }; UserSearch.contextTypes = { d2: PropTypes.object.isRequired, }; export default UserSearch;