periodicjs.ext.reactadmin
Version:
An authentication extension for periodicjs that uses passport to authenticate user sessions.
310 lines (295 loc) • 9.86 kB
JavaScript
import React, { Component, PropTypes, } from 'react';
import { Link, } from 'react-router';
// import flatten from 'flat';
import qs from 'querystring';
import * as rb from 're-bulma';
// import debounce from 'debounce';
import utilities from '../../util';
import pluralize from 'pluralize';
const propTypes = {
disabled: PropTypes.bool,
selector: PropTypes.string,
displayfield: PropTypes.string,
displayProps: PropTypes.object,
dbname: PropTypes.string,
multi: PropTypes.bool,
createable: PropTypes.bool,
flattenDataList: PropTypes.bool,
flattenDataListOptions: PropTypes.any,
resourceUrl: PropTypes.string,
createResourceUrl: PropTypes.string,
data: PropTypes.array,
selectedData: PropTypes.any,
value: PropTypes.any,
onChange: PropTypes.func,
onFocus: PropTypes.func,
limit: PropTypes.number,
datalistdata: PropTypes.array,
};
const defaultProps = {
disabled: false,
data: false,
selectedData: [],
createable: false,
value: undefined,
displayProps: {},
flattenDataList:true,
flattenDataListOptions: {},
selector:'_id',
displayField:'title',
dbname:'periodic',
limit: 10,
datalistdata: [],
onChange:(data)=>{
console.debug('ResponsiveDatalist onChange', { data, })
;
},
onFocus: (data)=>{
console.debug('ResponsiveDatalist onFocus', { data, })
;
},
};
class ResponsiveDatalist extends Component {
constructor(props) {
super(props);
this.state = {
disabled: props.disabled,
data: props.data,
value: props.value,
selectedData: props.selectedData,
isSearching:false,
};
this.inputProps = Object.assign({}, this.props.passableProps);
// this.searchFunction = debounce(this.updateDataList, 200);
this.searchFunction = this.updateDataList.bind(this);
this.filterStaticData = this.filterStaticData.bind(this);
}
componentWillUpdate(nextProps) {
}
filterStaticData(options) {
return (options.search)
? this.props.datalistdata.filter(item => (item[ this.props.field ].indexOf(options.search) > -1))
: this.props.datalistdata;
}
updateDataList(options) {
if(this.props.resourceUrl){
this.setState({ isSearching: true, });
let stateProps = this.props.getState();
let fetchURL = `${this.props.resourceUrl}&${qs.stringify({
limit: this.props.limit,
// sort: (newSortOptions.sortProp)
// ? `${newSortOptions.sortOrder}${newSortOptions.sortProp}`
// : undefined,
search: options.search,
allowSpecialCharacters: true,
// pagenum: options.pagenum || 1,
})}`;
let headers = Object.assign({
'x-access-token': stateProps.user.jwt_token,
}, stateProps.settings.userprofile.options.headers);
utilities.fetchComponent(fetchURL, { headers, })()
.then(response => {
let updatedState = {};
updatedState.selectedData = response[pluralize(this.props.entity)];
updatedState.isSearching = false;
// console.debug({updatedState,response});
this.setState(updatedState);
}, e => {
this.props.errorNotification(e);
});
} else if (this.props.staticSearch) {
this.setState({ isSearching: true, });
//options.search is the actual content
let updatedState = {};
updatedState.selectedData = this.filterStaticData(options);
updatedState.isSearching = false;
// console.debug({updatedState,response});
this.setState(updatedState);
//value is the array of selected values
//selectedData is the filtered list that changes everytime user types
} else{
console.debug({ options, });
}
}
onChangeHandler(event) {
this.searchFunction({ search: event.target.value||'', });
}
onFocusHandler(event) {
let updatedState = {};
updatedState.selectedData = this.props.datalistdata.map(data => data.value);
updatedState.isSearching = false;
this.setState(updatedState);
}
getDatalistDisplay(options){
let { displayField, selector, datum} = options;
let displayText = (datum[ displayField ] || datum.title || datum.name || datum.username || datum.email || datum[ selector ] || ((datum && typeof datum === 'string') ? datum : ''));
return (<span style={{
wordBreak: 'break-all',
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'pre-wrap',
wordWrap: 'break-word',
}}>
{
(datum && datum.fileurl && datum.transform && datum.transform.preview)
?<rb.Image src={datum.transform.preview} size="is24X24" style={{ float:'left', marginRight:'5px', }}/>
:null
}
{
(this.props.resourcePreview)
? <Link title={datum.title||displayText} to={`${this.props.resourcePreview}/${datum[selector]||datum}`}>{displayText}</Link>
: displayText
}
{
(this.props.resourceDescription)
? <rb.Content><p>{datum.description}</p></rb.Content>
: null
}
</span>);
}
removeDatalistItem(index) {
// console.debug('clicked datalist',{index});
// console.debug('clicked onclick',this.props);
if(this.props.multi){
let newValue = [].concat(this.state.value);
newValue.splice(index, 1);
// let oldValue = this.state.value;
// console.debug({ oldValue, newValue });
this.setState({
value:newValue,
selectedData: false,
});
this.props.onChange(newValue);
} else {
let datum = undefined;
this.setState({
value:datum,
selectedData: false,
});
// console.debug({ datum });
this.props.onChange(datum);
}
}
onBlurHandler() {
setTimeout(() => {
this.setState({ selectedData: [] });
}, 400)
}
render() {
try {
let notificationStyle={
marginBottom: '5px',
padding:'5px',
border:'1px solid lightgrey',
};
let notificationCloseStyle={
margin: '0px 0px 0px 20px',
borderRadius: '19px',
};
let selectData = (this.props.multi)
? (this.state.value && this.state.value.length )
? (this.state.value.map((selected, k) => {
return (<rb.Notification
key={k}
enableCloseButton
closeButtonProps={{
onClick: this.removeDatalistItem.bind(this, k),
style: notificationCloseStyle,
}}
style={notificationStyle}
>
{this.getDatalistDisplay({
datum:selected,
displayField: this.props.displayField,
selector: this.props.selector,
})}
</rb.Notification>);
}))
: null
: (this.state.value)
?(<rb.Notification
enableCloseButton
closeButtonProps={{
onClick: this.removeDatalistItem.bind(this),
style: notificationCloseStyle,
}}
style={notificationStyle}
>
{this.getDatalistDisplay({
datum:this.state.value,
displayField: this.props.displayField,
selector: this.props.selector,
})}
</rb.Notification>)
: null;
let displayOptions = (Array.isArray(this.state.selectedData) &&this.state.selectedData && this.state.selectedData.length)
? this.state.selectedData.map((datum, k)=>{
return (
<rb.Notification
key={k}
color="isWhite"
style={notificationStyle}
>
<rb.Button
icon="fa fa-plus"
size="isSmall"
style={{
alignSelf:'flex-end',
borderRadius:'20px',
float: 'right',
paddingRight: '0px',
}}
onClick={()=>{
if(this.props.multi){
let newValue = (this.state.value && Array.isArray(this.state.value) && this.state.value.length)
? this.state.value.concat([datum, ])
: [datum, ];
this.setState({
value:newValue,
selectedData: false,
});
this.props.onChange(newValue);
} else {
this.setState({
value:datum,
selectedData: false,
});
this.props.onChange(datum);
}
}}/>
{
this.getDatalistDisplay({
datum,
displayField: this.props.displayField,
selector: this.props.selector,
})
}
</rb.Notification>
);
}
)
: null;
return (<div {...this.props.wrapperProps}>
<div style={{ width:'100%', }}>
<rb.Input {...this.inputProps}
state={this.state.isSearching || undefined}
onChange={this.onChangeHandler.bind(this)}
onFocus={this.onChangeHandler.bind(this)}
onBlur={this.onBlurHandler.bind(this)}
ref={(input)=>{
this.textInput = input;
}}
/>
</div>
<div {...this.props.displayProps}> { displayOptions }</div>
<div>{ selectData }</div>
</div>);
} catch (e) {
console.error(e);
return <span>some error</span>
}
}
}
ResponsiveDatalist.propType = propTypes;
ResponsiveDatalist.defaultProps = defaultProps;
export default ResponsiveDatalist;