From c1ae28cc107725de1178951ca442a1c2156afe60 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 5 Nov 2015 11:48:22 -0500 Subject: Changed search autocomplete to scroll when making a selection using the keyboard --- web/react/components/search_autocomplete.jsx | 63 ++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 13 deletions(-) (limited to 'web') diff --git a/web/react/components/search_autocomplete.jsx b/web/react/components/search_autocomplete.jsx index 03e14ec49..0be93b05a 100644 --- a/web/react/components/search_autocomplete.jsx +++ b/web/react/components/search_autocomplete.jsx @@ -3,6 +3,7 @@ const ChannelStore = require('../stores/channel_store.jsx'); const KeyCodes = require('../utils/constants.jsx').KeyCodes; +const Popover = ReactBootstrap.Popover; const UserStore = require('../stores/user_store.jsx'); const Utils = require('../utils/utils.jsx'); @@ -10,7 +11,6 @@ const patterns = new Map([ ['channels', /\b(?:in|channel):\s*(\S*)$/i], ['users', /\bfrom:\s*(\S*)$/i] ]); -const Popover = ReactBootstrap.Popover; export default class SearchAutocomplete extends React.Component { constructor(props) { @@ -22,6 +22,8 @@ export default class SearchAutocomplete extends React.Component { this.handleKeyDown = this.handleKeyDown.bind(this); this.completeWord = this.completeWord.bind(this); + this.getSelection = this.getSelection.bind(this); + this.scrollToItem = this.scrollToItem.bind(this); this.updateSuggestions = this.updateSuggestions.bind(this); this.state = { @@ -37,9 +39,18 @@ export default class SearchAutocomplete extends React.Component { $(document).on('click', this.handleDocumentClick); } - componentDidUpdate() { - $(ReactDOM.findDOMNode(this.refs.searchPopover)).find('.popover-content').perfectScrollbar(); - $(ReactDOM.findDOMNode(this.refs.searchPopover)).find('.popover-content').css('max-height', $(window).height() - 200); + componentDidUpdate(prevProps, prevState) { + const content = $(ReactDOM.findDOMNode(this.refs.searchPopover)).find('.popover-content'); + + if (this.state.show) { + if (!prevState.show) { + content.perfectScrollbar(); + content.css('max-height', $(window).height() - 200); + } + + // keep the keyboard selection visible when scrolling + this.scrollToItem(this.getSelection()); + } } componentWillUnmount() { @@ -111,15 +122,7 @@ export default class SearchAutocomplete extends React.Component { } else if (e.which === KeyCodes.ENTER || e.which === KeyCodes.SPACE) { e.preventDefault(); - this.completeSelectedWord(); - } - } - - completeSelectedWord() { - if (this.state.mode === 'channels') { - this.completeWord(this.state.suggestions[this.state.selection].name); - } else if (this.state.mode === 'users') { - this.completeWord(this.state.suggestions[this.state.selection].username); + this.completeWord(this.getSelection()); } } @@ -135,6 +138,40 @@ export default class SearchAutocomplete extends React.Component { }); } + getSelection() { + if (this.state.mode === 'channels') { + return this.state.suggestions[this.state.selection].name; + } else if (this.state.mode === 'users') { + return this.state.suggestions[this.state.selection].username; + } + + return ''; + } + + scrollToItem(itemName) { + const content = $(ReactDOM.findDOMNode(this.refs.searchPopover)).find('.popover-content'); + const visibleContentHeight = content[0].clientHeight; + const actualContentHeight = content[0].scrollHeight; + + if (this.state.suggestions.length > 0 && visibleContentHeight < actualContentHeight) { + const contentTop = content.scrollTop(); + const contentTopPadding = parseInt(content.css('padding-top'), 10); + const contentBottomPadding = parseInt(content.css('padding-top'), 10); + + const item = $(this.refs[itemName]); + const itemTop = item[0].offsetTop - parseInt(item.css('margin-top'), 10); + const itemBottom = item[0].offsetTop + item.height() + parseInt(item.css('margin-bottom'), 10); + + if (itemTop - contentTopPadding < contentTop) { + // the item is off the top of the visible space + content.scrollTop(itemTop - contentTopPadding); + } else if (itemBottom + contentTopPadding + contentBottomPadding > contentTop + visibleContentHeight) { + // the item has gone off the bottom of the visible space + content.scrollTop(itemBottom - visibleContentHeight + contentTopPadding + contentBottomPadding); + } + } + } + updateSuggestions(mode, filter) { let suggestions = []; -- cgit v1.2.3-1-g7c22 From 52e75012c37f5af6a695995d3c133e63e2e4b725 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 5 Nov 2015 12:24:54 -0500 Subject: Fixed search autocomplete click handling --- web/react/components/search_autocomplete.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'web') diff --git a/web/react/components/search_autocomplete.jsx b/web/react/components/search_autocomplete.jsx index 0be93b05a..419b6dbf4 100644 --- a/web/react/components/search_autocomplete.jsx +++ b/web/react/components/search_autocomplete.jsx @@ -62,7 +62,7 @@ export default class SearchAutocomplete extends React.Component { } handleDocumentClick(e) { - const container = $(ReactDOM.findDOMNode(this.refs.container)); + const container = $(ReactDOM.findDOMNode(this.refs.searchPopover)); if (!(container.is(e.target) || container.has(e.target).length > 0)) { this.setState({ -- cgit v1.2.3-1-g7c22 From e29342d4267c81a709cdc19fe992762ae468e0d9 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 5 Nov 2015 13:32:06 -0500 Subject: Moved public and private channels into separate sections in the search autocomplete --- .../components/edit_channel_purpose_modal.jsx | 9 +-- web/react/components/search_autocomplete.jsx | 92 +++++++++++++++------- web/react/utils/utils.jsx | 9 +++ 3 files changed, 74 insertions(+), 36 deletions(-) (limited to 'web') diff --git a/web/react/components/edit_channel_purpose_modal.jsx b/web/react/components/edit_channel_purpose_modal.jsx index 4cb96a3ff..65e8183de 100644 --- a/web/react/components/edit_channel_purpose_modal.jsx +++ b/web/react/components/edit_channel_purpose_modal.jsx @@ -3,6 +3,8 @@ const AsyncClient = require('../utils/async_client.jsx'); const Client = require('../utils/client.jsx'); +const Utils = require('../utils/utils.jsx'); + const Modal = ReactBootstrap.Modal; export default class EditChannelPurposeModal extends React.Component { @@ -75,11 +77,6 @@ export default class EditChannelPurposeModal extends React.Component { title = {'Edit Purpose for '}{this.props.channel.display_name}; } - let channelTerm = 'Channel'; - if (this.props.channel.channelType === 'P') { - channelTerm = 'Group'; - } - return ( -

{`Describe how this ${channelTerm} should be used.`}

+

{`Describe how this ${Utils.getChannelTerm(this.props.channel.channelType)} should be used.`}