diff options
Diffstat (limited to 'web/react')
-rw-r--r-- | web/react/components/admin_console/admin_sidebar.jsx | 25 | ||||
-rw-r--r-- | web/react/components/admin_console/select_team_modal.jsx | 2 | ||||
-rw-r--r-- | web/react/components/post.jsx | 4 | ||||
-rw-r--r-- | web/react/components/suggestion/emoticon_provider.jsx | 74 | ||||
-rw-r--r-- | web/react/components/suggestion/search_suggestion_list.jsx | 2 | ||||
-rw-r--r-- | web/react/components/suggestion/suggestion_box.jsx | 32 | ||||
-rw-r--r-- | web/react/components/suggestion/suggestion_list.jsx | 6 | ||||
-rw-r--r-- | web/react/components/textbox.jsx | 3 | ||||
-rw-r--r-- | web/react/components/user_settings/user_settings_display.jsx | 9 | ||||
-rw-r--r-- | web/react/dispatcher/event_helpers.jsx | 7 | ||||
-rw-r--r-- | web/react/stores/suggestion_store.jsx | 5 | ||||
-rw-r--r-- | web/react/utils/constants.jsx | 1 | ||||
-rw-r--r-- | web/react/utils/emoticons.jsx | 8 | ||||
-rw-r--r-- | web/react/utils/utils.jsx | 22 |
14 files changed, 143 insertions, 57 deletions
diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx index 076a07618..cc98c495e 100644 --- a/web/react/components/admin_console/admin_sidebar.jsx +++ b/web/react/components/admin_console/admin_sidebar.jsx @@ -5,6 +5,9 @@ import AdminSidebarHeader from './admin_sidebar_header.jsx'; import SelectTeamModal from './select_team_modal.jsx'; import * as Utils from '../../utils/utils.jsx'; +const Tooltip = ReactBootstrap.Tooltip; +const OverlayTrigger = ReactBootstrap.OverlayTrigger; + export default class AdminSidebar extends React.Component { constructor(props) { super(props); @@ -80,6 +83,12 @@ export default class AdminSidebar extends React.Component { render() { var count = '*'; var teams = 'Loading'; + const removeTooltip = ( + <Tooltip id='remove-team-tooltip'>{'Remove team from sidebar menu'}</Tooltip> + ); + const addTeamTooltip = ( + <Tooltip id='add-team-tooltip'>{'Add team from sidebar menu'}</Tooltip> + ); if (this.props.teams != null) { count = '' + Object.keys(this.props.teams).length; @@ -102,14 +111,19 @@ export default class AdminSidebar extends React.Component { className={'nav__sub-menu-item ' + this.isSelected('team_users', team.id)} > {team.name} + <OverlayTrigger + delayShow={1000} + placement='top' + overlay={removeTooltip} + > <span className='menu-icon--right menu__close' onClick={this.removeTeam.bind(this, team.id)} style={{cursor: 'pointer'}} - title='Remove team from sidebar menu' > - {'x'} + {'×'} </span> + </OverlayTrigger> </a> </li> <li> @@ -245,15 +259,20 @@ export default class AdminSidebar extends React.Component { <span className='icon fa fa-gear'></span> <span>{'TEAMS (' + count + ')'}</span> <span className='menu-icon--right'> + <OverlayTrigger + delayShow={1000} + placement='top' + overlay={addTeamTooltip} + > <a href='#' onClick={this.showTeamSelect} > <i className='fa fa-plus' - title='Add team to sidebar menu' ></i> </a> + </OverlayTrigger> </span> </h4> </li> diff --git a/web/react/components/admin_console/select_team_modal.jsx b/web/react/components/admin_console/select_team_modal.jsx index 22189821b..858b6bbfe 100644 --- a/web/react/components/admin_console/select_team_modal.jsx +++ b/web/react/components/admin_console/select_team_modal.jsx @@ -57,7 +57,7 @@ export default class SelectTeamModal extends React.Component { <select ref='team' size='10' - style={{width: '100%'}} + className='form-control' > {options} </select> diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index 66d8c507a..b32656bfc 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -95,6 +95,10 @@ export default class Post extends React.Component { return true; } + if (nextProps.shouldHighlight !== this.props.shouldHighlight) { + return true; + } + return false; } getCommentCount(props) { diff --git a/web/react/components/suggestion/emoticon_provider.jsx b/web/react/components/suggestion/emoticon_provider.jsx new file mode 100644 index 000000000..7dcb86442 --- /dev/null +++ b/web/react/components/suggestion/emoticon_provider.jsx @@ -0,0 +1,74 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import SuggestionStore from '../../stores/suggestion_store.jsx'; +import * as Emoticons from '../../utils/emoticons.jsx'; + +const MAX_EMOTICON_SUGGESTIONS = 40; + +class EmoticonSuggestion extends React.Component { + render() { + const text = this.props.term; + const name = this.props.item; + + let className = 'emoticon-suggestion'; + if (this.props.isSelection) { + className += ' suggestion--selected'; + } + + return ( + <div + className={className} + onClick={this.props.onClick} + > + <div className='pull-left'> + <img + alt={text} + className='emoticon-suggestion__image' + src={Emoticons.getImagePathForEmoticon(name)} + title={text} + /> + </div> + <div className='pull-left'> + {text} + </div> + </div> + ); + } +} + +EmoticonSuggestion.propTypes = { + item: React.PropTypes.string.isRequired, + term: React.PropTypes.string.isRequired, + isSelection: React.PropTypes.bool, + onClick: React.PropTypes.func +}; + +export default class EmoticonProvider { + handlePretextChanged(suggestionId, pretext) { + const captured = (/(?:^|\s)(:([a-zA-Z0-9_+\-]*))$/g).exec(pretext); + if (captured) { + const text = captured[1]; + const partialName = captured[2]; + + const terms = []; + const names = []; + + for (const emoticon of Emoticons.emoticonMap.keys()) { + if (emoticon.indexOf(partialName) !== -1) { + terms.push(':' + emoticon + ':'); + names.push(emoticon); + + if (terms.length >= MAX_EMOTICON_SUGGESTIONS) { + break; + } + } + } + + if (terms.length > 0) { + SuggestionStore.setMatchedPretext(suggestionId, text); + SuggestionStore.addSuggestions(suggestionId, terms, names, EmoticonSuggestion); + } + } + } +} diff --git a/web/react/components/suggestion/search_suggestion_list.jsx b/web/react/components/suggestion/search_suggestion_list.jsx index 542d28ddd..3378a33a0 100644 --- a/web/react/components/suggestion/search_suggestion_list.jsx +++ b/web/react/components/suggestion/search_suggestion_list.jsx @@ -35,7 +35,7 @@ export default class SearchSuggestionList extends SuggestionList { } render() { - if (this.state.items.length === 0 || !this.props.show) { + if (this.state.items.length === 0) { return null; } diff --git a/web/react/components/suggestion/suggestion_box.jsx b/web/react/components/suggestion/suggestion_box.jsx index 4ca461e82..4cfb38f8e 100644 --- a/web/react/components/suggestion/suggestion_box.jsx +++ b/web/react/components/suggestion/suggestion_box.jsx @@ -13,7 +13,6 @@ export default class SuggestionBox extends React.Component { super(props); this.handleDocumentClick = this.handleDocumentClick.bind(this); - this.handleFocus = this.handleFocus.bind(this); this.handleChange = this.handleChange.bind(this); this.handleCompleteWord = this.handleCompleteWord.bind(this); @@ -21,10 +20,6 @@ export default class SuggestionBox extends React.Component { this.handlePretextChanged = this.handlePretextChanged.bind(this); this.suggestionId = Utils.generateId(); - - this.state = { - focused: false - }; } componentDidMount() { @@ -49,27 +44,11 @@ export default class SuggestionBox extends React.Component { } handleDocumentClick(e) { - if (!this.state.focused) { - return; - } - const container = $(ReactDOM.findDOMNode(this)); if (!(container.is(e.target) || container.has(e.target).length > 0)) { // we can't just use blur for this because it fires and hides the children before // their click handlers can be called - this.setState({ - focused: false - }); - } - } - - handleFocus() { - this.setState({ - focused: true - }); - - if (this.props.onFocus) { - this.props.onFocus(); + EventHelpers.emitClearSuggestions(this.suggestionId); } } @@ -134,7 +113,6 @@ export default class SuggestionBox extends React.Component { render() { const newProps = Object.assign({}, this.props, { - onFocus: this.handleFocus, onChange: this.handleChange, onKeyDown: this.handleKeyDown }); @@ -162,10 +140,7 @@ export default class SuggestionBox extends React.Component { return ( <div> {textbox} - <SuggestionListComponent - suggestionId={this.suggestionId} - show={this.state.focused} - /> + <SuggestionListComponent suggestionId={this.suggestionId} /> </div> ); } @@ -184,6 +159,5 @@ SuggestionBox.propTypes = { // explicitly name any input event handlers we override and need to manually call onChange: React.PropTypes.func, - onKeyDown: React.PropTypes.func, - onFocus: React.PropTypes.func + onKeyDown: React.PropTypes.func }; diff --git a/web/react/components/suggestion/suggestion_list.jsx b/web/react/components/suggestion/suggestion_list.jsx index 45843f4c8..e3ccd0f08 100644 --- a/web/react/components/suggestion/suggestion_list.jsx +++ b/web/react/components/suggestion/suggestion_list.jsx @@ -82,7 +82,7 @@ export default class SuggestionList extends React.Component { } render() { - if (this.state.items.length === 0 || !this.props.show) { + if (this.state.items.length === 0) { return null; } @@ -100,6 +100,7 @@ export default class SuggestionList extends React.Component { key={term} ref={term} item={item} + term={term} isSelection={isSelection} onClick={this.handleItemClick.bind(this, term)} /> @@ -120,6 +121,5 @@ export default class SuggestionList extends React.Component { } SuggestionList.propTypes = { - suggestionId: React.PropTypes.string.isRequired, - show: React.PropTypes.bool.isRequired + suggestionId: React.PropTypes.string.isRequired }; diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx index 107e65f57..b29f304ab 100644 --- a/web/react/components/textbox.jsx +++ b/web/react/components/textbox.jsx @@ -3,6 +3,7 @@ import AtMentionProvider from './suggestion/at_mention_provider.jsx'; import CommandProvider from './suggestion/command_provider.jsx'; +import EmoticonProvider from './suggestion/emoticon_provider.jsx'; import SuggestionList from './suggestion/suggestion_list.jsx'; import SuggestionBox from './suggestion/suggestion_box.jsx'; import ErrorStore from '../stores/error_store.jsx'; @@ -29,7 +30,7 @@ export default class Textbox extends React.Component { connection: '' }; - this.suggestionProviders = [new AtMentionProvider()]; + this.suggestionProviders = [new AtMentionProvider(), new EmoticonProvider()]; if (props.supportsCommands) { this.suggestionProviders.push(new CommandProvider()); } diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx index dc3865c68..c464258de 100644 --- a/web/react/components/user_settings/user_settings_display.jsx +++ b/web/react/components/user_settings/user_settings_display.jsx @@ -241,7 +241,7 @@ export default class UserSettingsDisplay extends React.Component { const inputs = [ <div key='userDisplayNameOptions'> <div - className='input-group theme-group dropdown' + className='dropdown' > <select className='form-control' @@ -251,9 +251,6 @@ export default class UserSettingsDisplay extends React.Component { > {options} </select> - <span className={'input-group-addon ' + Constants.FONTS[this.state.selectedFont]}> - {this.state.selectedFont} - </span> </div> <div><br/>{'Select the font displayed in the Mattermost user interface.'}</div> </div> @@ -312,12 +309,12 @@ export default class UserSettingsDisplay extends React.Component { <div className='user-settings'> <h3 className='tab-header'>{'Display Settings'}</h3> <div className='divider-dark first'/> + {fontSection} + <div className='divider-dark'/> {clockSection} <div className='divider-dark'/> {nameFormatSection} <div className='divider-dark'/> - {fontSection} - <div className='divider-dark'/> </div> </div> ); diff --git a/web/react/dispatcher/event_helpers.jsx b/web/react/dispatcher/event_helpers.jsx index f792c610f..3deddd754 100644 --- a/web/react/dispatcher/event_helpers.jsx +++ b/web/react/dispatcher/event_helpers.jsx @@ -141,3 +141,10 @@ export function emitCompleteWordSuggestion(suggestionId, term = '') { term }); } + +export function emitClearSuggestions(suggestionId) { + AppDispatcher.handleViewAction({ + type: Constants.ActionTypes.SUGGESTION_CLEAR_SUGGESTIONS, + id: suggestionId + }); +} diff --git a/web/react/stores/suggestion_store.jsx b/web/react/stores/suggestion_store.jsx index 182f5810f..2250ec234 100644 --- a/web/react/stores/suggestion_store.jsx +++ b/web/react/stores/suggestion_store.jsx @@ -244,6 +244,11 @@ class SuggestionStore extends EventEmitter { this.emitSuggestionsChanged(id); } break; + case ActionTypes.SUGGESTION_CLEAR_SUGGESTIONS: + this.clearSuggestions(id); + this.clearSelection(id); + this.emitSuggestionsChanged(id); + break; case ActionTypes.SUGGESTION_SELECT_NEXT: this.selectNext(id); this.emitSuggestionsChanged(id); diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 8164095b9..b641e966b 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -55,6 +55,7 @@ export default { SUGGESTION_PRETEXT_CHANGED: null, SUGGESTION_RECEIVED_SUGGESTIONS: null, + SUGGESTION_CLEAR_SUGGESTIONS: null, SUGGESTION_COMPLETE_WORD: null, SUGGESTION_SELECT_NEXT: null, SUGGESTION_SELECT_PREVIOUS: null diff --git a/web/react/utils/emoticons.jsx b/web/react/utils/emoticons.jsx index 8943e9544..ab04936c0 100644 --- a/web/react/utils/emoticons.jsx +++ b/web/react/utils/emoticons.jsx @@ -116,19 +116,19 @@ function initializeEmoticonMap() { const out = new Map(); for (let i = 0; i < emoticonNames.length; i++) { - out[emoticonNames[i]] = true; + out.set(emoticonNames[i], true); } return out; } -const emoticonMap = initializeEmoticonMap(); +export const emoticonMap = initializeEmoticonMap(); export function handleEmoticons(text, tokens) { let output = text; function replaceEmoticonWithToken(fullMatch, prefix, matchText, name) { - if (emoticonMap[name]) { + if (emoticonMap.has(name)) { const index = tokens.size; const alias = `MM_EMOTICON${index}`; @@ -159,4 +159,4 @@ export function getImagePathForEmoticon(name) { return `/static/images/emoji/${name}.png`; } return `/static/images/emoji`; -}
\ No newline at end of file +} diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 788d8a45c..0a52f5b37 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -695,15 +695,19 @@ export function applyTheme(theme) { } export function applyFont(fontName) { - const body = document.querySelector('body'); - const keys = Object.getOwnPropertyNames(body.classList); - keys.forEach((k) => { - const className = body.classList[k]; - if (className && className.lastIndexOf('font') === 0) { - body.classList.remove(className); + const body = $('body'); + + for (const key of Reflect.ownKeys(Constants.FONTS)) { + const className = Constants.FONTS[key]; + + if (fontName === key) { + if (!body.hasClass(className)) { + body.addClass(className); + } + } else { + body.removeClass(className); } - }); - body.classList.add(Constants.FONTS[fontName]); + } } export function changeCss(className, classValue, classRepeat) { @@ -1238,4 +1242,4 @@ export function getPostTerm(post) { export function isFeatureEnabled(feature) { return PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, Constants.FeatureTogglePrefix + feature.label, {value: 'false'}).value === 'true'; -}
\ No newline at end of file +} |