@attivio/suit
Version:
Attivio SUIT, the Search UI Toolkit, is a library for creating search clients for searching the Attivio platform.
377 lines (312 loc) • 12.5 kB
JavaScript
'use strict';
exports.__esModule = true;
var _class, _temp;
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _MenuItem = require('react-bootstrap/lib/MenuItem');
var _MenuItem2 = _interopRequireDefault(_MenuItem);
var _Configurable = require('./Configurable');
var _Configurable2 = _interopRequireDefault(_Configurable);
var _QueryResponse = require('../api/QueryResponse');
var _QueryResponse2 = _interopRequireDefault(_QueryResponse);
var _SimpleQueryRequest = require('../api/SimpleQueryRequest');
var _SimpleQueryRequest2 = _interopRequireDefault(_SimpleQueryRequest);
var _ObjectUtils = require('../util/ObjectUtils');
var _ObjectUtils2 = _interopRequireDefault(_ObjectUtils);
var _SearchFacetBucket = require('../api/SearchFacetBucket');
var _SearchFacetBucket2 = _interopRequireDefault(_SearchFacetBucket);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* Component that wraps a Facet that allows searching for specific values for that facet,
* as well as exporting that facet's values to a CSV
*/
var FacetSearchBar = (_temp = _class = function (_React$Component) {
_inherits(FacetSearchBar, _React$Component);
/**
* Generate a string with the the CSV representation of the facet value data.
*/
FacetSearchBar.convertArrayOfObjectsToCSV = function convertArrayOfObjectsToCSV(data) {
var columnDelimiter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ',';
var lineDelimiter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '\n';
if (data === null || !data.length) {
return null;
}
// Create the header row of the CSV
var keys = Array.from(data[0].keys());
var header = keys.join(columnDelimiter);
// Populate the rows of the CSV
var rows = data.map(function (item) {
// Get the values for each data row from the map
var rowValues = keys.map(function (key) {
return item.get(key);
});
// Concatenate the values, separated by the column delimiter
return rowValues.join(columnDelimiter);
});
// Add the header to the beginning of the rows
rows.unshift(header);
// Concatenate the rows, separated by the line delimiter
return '' + header + lineDelimiter + rows.join(lineDelimiter);
};
function FacetSearchBar(props) {
_classCallCheck(this, FacetSearchBar);
var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
_this.state = {
query: '',
recognizing: false,
suggestions: [],
facetValue: '',
error: null
};
_this.doKeyPress = _this.doKeyPress.bind(_this);
_this.doSearch = _this.doSearch.bind(_this);
_this.queryChanged = _this.queryChanged.bind(_this);
_this.addFilter = _this.addFilter.bind(_this);
_this.handleSearchResults = _this.handleSearchResults.bind(_this);
return _this;
}
/**
* Generates a list of menu items to show for the Facet values that match the query,
* based on values currently set on this.state.suggestions.
*/
FacetSearchBar.prototype.getSuggestionList = function getSuggestionList() {
var _this2 = this;
if (!this.state.suggestions || this.state.suggestions.length === 0) {
return null;
}
var suggestionsAdded = 0;
var contents = this.state.suggestions.map(function (suggestion, index) {
var include = suggestion.displayLabel().length >= _this2.state.query.length;
include = include && suggestion.displayLabel().toLowerCase().indexOf(_this2.state.query.toLowerCase()) !== -1;
var returnVal = '';
if (include && suggestionsAdded < _this2.props.maxValues) {
suggestionsAdded += 1;
returnVal = _react2.default.createElement(
'button',
{
className: 'facet-suggestion',
key: suggestionsAdded,
onClick: function onClick() {
return _this2.addFilter(index);
},
style: { width: '100%', textAlign: 'left', borderWidth: '0px', backgroundColor: '#FFFFFF' }
},
_react2.default.createElement(
_MenuItem2.default,
{ eventKey: index, key: suggestionsAdded, onSelect: _this2.addFilter, tabIndex: index },
suggestion.displayLabel() + ' (' + suggestion.count + ')'
)
);
}
return returnVal;
});
if (contents.length > 0) {
return _react2.default.createElement(
'div',
{
className: 'facet-suggestion',
style: { width: '100%', border: '1px solid #D2D2D2', borderTop: 'none', passingTop: '11px', position: 'absolute' }
},
_react2.default.createElement(
'ul',
{ role: 'menu', style: { marginBottom: 0 } },
contents
)
);
}
return null;
};
/**
* Calls the function to fire off the query, then maps the results
* into a format ready to be written to CSV
*/
FacetSearchBar.prototype.getAllFacetValues = function getAllFacetValues(callback) {
function localCallback(qr, error) {
if (qr) {
var facets = qr.facets[0].buckets;
var response = facets.map(function (facet) {
return _ObjectUtils2.default.toMap({ 'Facet Value': facet.displayLabel(), 'Document Count': facet.count });
});
callback(response);
} else if (error) {
// Failed!
}
return [];
}
this.doConfiguredSearch('*', -1, localCallback, this.context.searcher);
};
/**
* Handles when a user clicks on a facet value from the suggestion list.
*/
FacetSearchBar.prototype.addFilter = function addFilter(eventKey) {
this.props.addFacetFilter(this.state.suggestions[eventKey]);
this.setState({ suggestions: [], query: '' });
};
/**
* Handles the results and sets the facets to state.
*/
FacetSearchBar.prototype.handleSearchResults = function handleSearchResults(response, error) {
if (response) {
var facets = response.facets[0].buckets;
this.setState({ suggestions: facets });
} else if (error) {
// Failed!
this.setState({
suggestions: [],
error: error
});
}
};
/**
* Fires off the search for the matching facet values, while respecting the query
* and filters the user has already entered.
*/
FacetSearchBar.prototype.doConfiguredSearch = function doConfiguredSearch(queryTerm, maxBuckets, callback, searcher) {
if (searcher) {
var searchTerm = searcher.state.query;
var simpleQR = new _SimpleQueryRequest2.default();
simpleQR.query = searchTerm;
simpleQR.facets = [this.props.name + '(maxBuckets=' + maxBuckets + ')'];
simpleQR.facetFilters = searcher.state.facetFilters;
simpleQR.filters = [];
if (searcher.getQueryRequest().filters && searcher.getQueryRequest().filters.length > 0) {
Array.prototype.push.apply(simpleQR.filters, searcher.getQueryRequest().filters);
}
simpleQR.filters.push(this.props.name + ':' + queryTerm);
simpleQR.rows = 0;
simpleQR.queryLanguage = 'simple';
simpleQR.workflow = searcher.getQueryRequest().workflow;
searcher.doCustomSearch(simpleQR, callback);
}
};
/**
* Called when the user wants to search (hits enter or clicks search).
*/
FacetSearchBar.prototype.doSearch = function doSearch() {
var callback = this.handleSearchResults;
this.doConfiguredSearch(this.state.facetValue + '*', this.props.maxValues * 2, callback, this.context.searcher);
};
/**
* Handles when the user updates the query for this facet.
*/
FacetSearchBar.prototype.queryChanged = function queryChanged(e) {
if (e.target instanceof HTMLInputElement) {
var newQuery = e.target.value;
this.setState({ facetValue: newQuery, query: newQuery });
}
};
/**
* Exports all values and counts for the facet to CSV.
*/
FacetSearchBar.prototype.downloadCSV = function downloadCSV() {
var _this3 = this;
var callback = function callback(data) {
var csv = FacetSearchBar.convertArrayOfObjectsToCSV(data);
if (csv !== null) {
var filename = _this3.props.name + '_facet_values.csv';
// Make the CSV data into a data URI
var encodedData = encodeURI('data:text/csv;charset=utf-8,' + csv);
var link = document.createElement('a');
link.setAttribute('href', encodedData);
link.setAttribute('download', filename);
link.click();
}
};
this.getAllFacetValues(callback);
};
/**
* Called when a user presses a key.
*/
FacetSearchBar.prototype.doKeyPress = function doKeyPress(e) {
// If the user presses enter, do the search
if (e.target instanceof HTMLInputElement && e.keyCode) {
if (e.keyCode === 13) {
this.doSearch();
}
}
};
FacetSearchBar.prototype.render = function render() {
var _this4 = this;
var inputClass = 'form-control facet-search-bar';
var query = this.state.query;
var placeholder = this.props.placeholder;
var suggestionList = this.getSuggestionList();
// Only show the search button once the user has typed something in the field
var searchButton = this.state.query.length > 0 ? _react2.default.createElement(
'button',
{
type: 'submit',
className: 'btn attivio-globalmast-search-submit',
style: {
paddingLeft: '4px',
paddingRight: '4px',
height: 'calc(100% - 7px)',
lineHeight: 'calc(100% - 7px)'
},
onClick: this.doSearch
},
this.props.buttonLabel
) : '';
var inputComponent = this.props.showSearchBar ? _react2.default.createElement(
'div',
{ className: 'attivio-globalmast-search', role: 'search', style: { display: 'inline-block' } },
_react2.default.createElement(
'div',
{ className: 'form-group', style: { position: 'relative' } },
_react2.default.createElement('input', {
type: 'search',
className: inputClass,
placeholder: placeholder,
onChange: this.queryChanged,
onKeyDown: this.doKeyPress,
value: query,
style: { width: '100%', height: '1.75em' }
}),
searchButton
),
suggestionList
) : null;
var exportButton = this.props.showExportButton ? _react2.default.createElement(
'div',
null,
_react2.default.createElement(
'a',
{
className: 'attivio-facet-more attivio-more',
onClick: function onClick() {
return _this4.downloadCSV();
},
role: 'button',
tabIndex: 0,
style: { fontSize: '12px' }
},
this.props.exportButtonLabel
)
) : null;
return _react2.default.createElement(
'div',
null,
inputComponent,
this.props.children,
exportButton
);
};
return FacetSearchBar;
}(_react2.default.Component), _class.contextTypes = {
searcher: _propTypes2.default.any
}, _class.defaultProps = {
placeholder: 'Search facet values\u2026',
buttonLabel: 'Search',
name: '*',
maxValues: 5,
showSearchBar: false,
showExportButton: false,
exportButtonLabel: 'Export facet as CSV\u2026'
}, _class.displayName = 'FacetSearchBar', _temp);
exports.default = (0, _Configurable2.default)(FacetSearchBar);
module.exports = exports['default'];