goldfish-search
Version:
A SharePoint 2013 inline People Search component
349 lines (280 loc) • 10.6 kB
JSX
import React from 'react';
import styles from './PeopleSearch.css';
import cssModules from 'react-css-modules';
import Utils from '../../utils/utilities';
import Waypoint from 'react-waypoint';
import Menu from '../../components/menu/Menu.jsx';
import Search from '../../components/search/Search.jsx';
import Settings from '../../components/settings/Settings.jsx';
import Layout from '../../components/layout/Layout.jsx';
import Favourites from '../../components/favourites/Favourites.jsx';
import Results from '../../components/results/Results.jsx';
import Paging from '../../components/paging/Paging.jsx';
import Title from '../../components/title/Title.jsx';
import FavouriteStore from '../../stores/FavouriteStore';
import LayoutStore from '../../stores/LayoutStore';
import PeopleSearchActions from '../../actions/PeopleSearchActions';
import SettingsManager from '../../utils/settings';
function getFavouritesState() {
PeopleSearchActions.getFavourites();
return FavouriteStore.getCurrentFavourites();
}
function getLayoutState() {
PeopleSearchActions.fetchLayout();
return LayoutStore.getLayout();
}
function getMenuClass(menu) {
const safe = ['alternate-tabs'];
const menuClass = typeof menu === 'string' ? menu : 'NO_CLASS';
return safe.indexOf(menuClass) > -1 ? menuClass : '';
}
class PeopleSearch extends React.Component {
constructor(props) {
super(props);
// the very first thing we do in the app is apply any options present
this.setInitialState();
this.applyOptions = this.applyOptions.bind(this);
// if the app position is moved reset to default state
document.addEventListener('Goldfish.Snappin', this.resetWayPointAfterPositioning, false);
}
componentDidUpdate() {
// ie workaround
if (this.state === null) {
this.setInitialState();
this.applyOptions();
}
}
componentDidMount() {
if (typeof Sys !== 'undefined' && Sys && Sys.Application) {
Sys.Application.notifyScriptLoaded();
}
if (typeof SP !== 'undefined') {
if (typeof SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs === 'function') {
// Inform the create functionthat Goldfish can now load safely
SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs('goldfish.ready.min.js');
}
}
}
resetWayPointAfterPositioning = () => {
if (this.state.items.length > 0) {
// before we whipe the state, we need a record of it
const previous = this.state;
// reset the clocks so our waypoint works nicely
this.setInitialState();
// reset the state to what it was before
this.state = previous;
}
}
onSearch(items) {
this.setState(items);
this.setState({
searching: false,
refresh: false,
});
}
onSearching() {
this.setState({
searching: true,
items: [],
count: 0,
pageNum: 0,
refresh: false,
});
}
onExport() {
if (this.state.items.length > 0) {
const csv = Exporter.convertArrayObjectsToCsv(this.state.items);
if (csv.length > 0) {
const csvFileName = this.state.term.replace(/ /g, '-');
Exporter.exportCsvStringToFile(csv, csvFileName);
}
}
}
onPage(pages) {
this.setState(pages);
}
onSettingChange(collection) {
// flush any current searches
this.setInitialState();
this.state.settings = collection;
this.applyOptions();
}
onRefreshFinish() {
this.setState({
refresh: false,
});
}
onItemUpdate(index, favourite, type) {
const items = this.state.items;
if (type === 'person') {
items[index].Cells.Favourite = favourite;
this.setState({ items: items });
} else {
// refresh the view now the favourites have changed
this.setState({ refresh: true });
}
}
onLayoutChange(view) {
this.setState(view);
}
onFavouritesChange(favourites) {
this.setState({favourites: favourites});
}
applyOptions() {
if (Object.keys(this.props.options).length > 0) {
// suggest taxonomy applied from options
if (typeof this.props.options.termsets !== 'undefined') {
this.setState({ termsets: this.props.options.termsets });
}
if (typeof this.props.options.userInformationFields !== 'undefined') {
this.setState({ userInformationFields: this.props.options.userInformationFields });
}
// css overrides applied from options
if (typeof this.props.options.css !== 'undefined') {
if (typeof this.props.options.css.overrides !== 'undefined') {
SettingsManager.settingRouting('cssOveride', this.props.options.css.overrides);
}
}
} else {
this.setState({ suggestions: [] });
}
}
isInfiniteScrollActive() {
// check to see if the super search is enabled
return this.state.settings.some(function(el) {
return Object.keys(el)[0] === 'inifiniteScroll' && el[Object.keys(el)[0]];
});
}
renderPaging() {
if (!this.isInfiniteScrollActive()) {
return (<Paging
count={this.state.count}
onSearching={this.onSearching.bind(this)}
properties={this.props.options.properties}
pageNum={this.state.pageNum}
term={this.state.term}
onPaging={this.onPage.bind(this)} />);
}
}
infiniteScroll() {
// check the settings to see if we have asked for results to be fetch on scroll
if (this.isInfiniteScrollActive()) {
const self = this;
// only add a scroll waypoint if we do not have all the results already
if (this.state.count > this.state.items.length) {
// we wrap the waypoint to ensure that it never floats along side a result (and sits at the bottom)
return (
<div styleName="wp-holder">
<Waypoint
onEnter={({ previousPosition, currentPosition, event }) => {
// only fetch new results if we are not currently doing so...
if (!self.state.searching) {
// if we have some results enable constant result fetching (infinite scroll)
if (self.state.items.length > 0) {
// get the page number for the search
const next = (typeof self.state.pageNum === 'undefined' || self.state.pageNum === 0) ? 2 : (self.state.pageNum + 1);
// ensure we haven't already fetched these results
if (Math.ceil(self.state.items.length / 10) < next) {
self.setState({
searching: true,
});
const url = Utils.getFullSearchQueryUrl(self.state.term, self.props.options.properties);
// load x more search results
PeopleSearchActions.fetchData(url, self.state.term, next, true);
}
}
}
}} />
</div>
);
}
}
}
setInitialState() {
this.state = {
items: [],
searching: false,
refresh: false,
settings: [],
count: 0,
pageNum: 0,
term: '',
text: '',
favourites: getFavouritesState(),
layout: getLayoutState(),
};
}
render() {
if (this.state === null) {
this.componentDidUpdate();
return null;
}
const alternateMenu = getMenuClass(this.props.options.menu);
const inlineStyles = alternateMenu !== '' ? { paddingTop: '45px' } : { paddingTop: '0' };
return (
<div id="outer-space" key="outer-space" className="goldfishSnapRight animated bounceInRight">
<Menu
onExport={this.onExport}
alternate={alternateMenu} />
<div id="component" styleName="component" style={inlineStyles}>
<div styleName="container">
<Title
text={this.props.options.title} />
</div>
<div className="content">
<div className="ui center aligned" styleName="container">
<Search
onSearchChanged={this.onSearch.bind(this)}
onSearching={this.onSearching.bind(this)}
properties={this.props.options.properties}
settings={this.state.settings}
termsets={this.state.termsets}
userInformationFields={this.state.userInformationFields} />
</div>
</div>
<div className="content" id="component-vision" styleName="everything-worth-while">
{this.renderPaging()}
<Results
items={this.state.items}
term={this.state.term}
refresh={this.state.refresh}
onRefreshFinish={this.onRefreshFinish.bind(this)}
searching={this.state.searching}
favourites={this.state.favourites}
layout={this.state.layout}
onLayoutChange={this.onLayoutChange.bind(this)}
onFavouritesChange={this.onFavouritesChange.bind(this)}
onItemUpdate={this.onItemUpdate.bind(this)} />
{this.infiniteScroll()}
{this.renderPaging()}
</div>
</div>
<Favourites
layout={this.state.layout}
title={this.props.options.title}
paddingTop={inlineStyles.paddingTop}
favourites={this.state.favourites}
onFavouritesChange={this.onFavouritesChange.bind(this)}
onItemUpdate={this.onItemUpdate.bind(this)} />
<Layout
title={this.props.options.title}
paddingTop={inlineStyles.paddingTop}
onLayoutChange={this.onLayoutChange.bind(this)} />
<Settings
title={this.props.options.title}
paddingTop={inlineStyles.paddingTop}
onSettingChange={this.onSettingChange.bind(this)} />
</div>
);
}
}
PeopleSearch.propTypes = {
options: React.PropTypes.object,
};
PeopleSearch.defaultProps = {
options: {
title: 'Goldfish',
properties: '',
},
};
export default cssModules(PeopleSearch, styles);