UNPKG

@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
'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'];