diff options
Diffstat (limited to 'web')
-rw-r--r-- | web/react/components/login.jsx | 6 | ||||
-rw-r--r-- | web/react/components/settings_sidebar.jsx | 4 | ||||
-rw-r--r-- | web/react/utils/text_formatting.jsx | 104 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_content.scss | 5 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_headers.scss | 1 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_post.scss | 18 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_signup.scss | 15 |
7 files changed, 115 insertions, 38 deletions
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx index f7f5bd23d..423ba9067 100644 --- a/web/react/components/login.jsx +++ b/web/react/components/login.jsx @@ -125,7 +125,7 @@ export default class Login extends React.Component { let emailSignup; if (global.window.mm_config.EnableSignUpWithEmail === 'true') { emailSignup = ( - <div> + <div className='signup__email-container'> <div className={'form-group' + errorClass}> <input autoFocus={focusEmail} @@ -206,7 +206,7 @@ export default class Login extends React.Component { href='/' className='signup-team-login' > - {'Sign up now'} + {'Create one now'} </a> </span> </div> @@ -215,11 +215,11 @@ export default class Login extends React.Component { return ( <div className='signup-team__container'> - {verifiedBox} <h5 className='margin--less'>{'Sign in to:'}</h5> <h2 className='signup-team__name'>{teamDisplayName}</h2> <h2 className='signup-team__subdomain'>{'on '}{global.window.mm_config.SiteName}</h2> <form onSubmit={this.handleSubmit}> + {verifiedBox} <div className={'form-group' + errorClass}> {serverError} </div> diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx index 68d9cea48..4af46c35a 100644 --- a/web/react/components/settings_sidebar.jsx +++ b/web/react/components/settings_sidebar.jsx @@ -1,14 +1,10 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -var utils = require('../utils/utils.jsx'); export default class SettingsSidebar extends React.Component { componentDidUpdate() { $('.settings-modal').find('.modal-body').scrollTop(0); $('.settings-modal').find('.modal-body').perfectScrollbar('update'); - if (utils.isSafari()) { - $('.settings-modal .settings-links .nav').addClass('absolute'); - } } constructor(props) { super(props); diff --git a/web/react/utils/text_formatting.jsx b/web/react/utils/text_formatting.jsx index 705d85cf6..7f1d7175d 100644 --- a/web/react/utils/text_formatting.jsx +++ b/web/react/utils/text_formatting.jsx @@ -259,30 +259,73 @@ function autolinkHashtags(text, tokens) { return output.replace(/(^|\W)(#[a-zA-Z][a-zA-Z0-9.\-_]*)\b/g, replaceHashtagWithToken); } -function highlightSearchTerm(text, tokens, searchTerm) { - let output = text; +const puncStart = /^[.,()&$!\[\]{}':;\\]+/; +const puncEnd = /[.,()&$#!\[\]{}':;\\]+$/; - var newTokens = new Map(); - for (const [alias, token] of tokens) { - if (token.originalText.indexOf(searchTerm.replace(/\*$/, '')) > -1) { - const index = tokens.size + newTokens.size; - const newAlias = `MM_SEARCHTERM${index}`; +function parseSearchTerms(searchTerm) { + let terms = []; - newTokens.set(newAlias, { - value: `<span class='search-highlight'>${alias}</span>`, - originalText: token.originalText - }); + let termString = searchTerm; - output = output.replace(alias, newAlias); + while (termString) { + let captured; + + // check for a quoted string + captured = (/^"(.*?)"/).exec(termString); + if (captured) { + termString = termString.substring(captured[0].length); + terms.push(captured[1]); + continue; + } + + // check for a search flag (and don't add it to terms) + captured = (/^(?:in|from|channel): ?\S+/).exec(termString); + if (captured) { + termString = termString.substring(captured[0].length); + continue; + } + + // capture any plain text up until the next quote or search flag + captured = (/^.+?(?=\bin|\bfrom|\bchannel|"|$)/).exec(termString); + if (captured) { + termString = termString.substring(captured[0].length); + + // break the text up into words based on how the server splits them in SqlPostStore.SearchPosts and then discard empty terms + terms.push(...captured[0].split(/[ <>+\-\(\)\~\@]/).filter((term) => !!term)); + continue; } + + // we should never reach this point since at least one of the regexes should match something in the remaining text + throw new Error('Infinite loop in search term parsing: ' + termString); } - // the new tokens are stashed in a separate map since we can't add objects to a map during iteration - for (const newToken of newTokens) { - tokens.set(newToken[0], newToken[1]); + // remove punctuation from each term + terms = terms.map((term) => term.replace(puncStart, '').replace(puncEnd, '')); + + return terms; +} + +function convertSearchTermToRegex(term) { + let pattern; + if (term.endsWith('*')) { + pattern = '\\b' + escapeRegex(term.substring(0, term.length - 1)); + } else { + pattern = '\\b' + escapeRegex(term) + '\\b'; } - function replaceSearchTermWithToken(fullMatch, prefix, word) { + return new RegExp(pattern, 'gi'); +} + +function highlightSearchTerm(text, tokens, searchTerm) { + const terms = parseSearchTerms(searchTerm); + + if (terms.length === 0) { + return text; + } + + let output = text; + + function replaceSearchTermWithToken(word) { const index = tokens.size; const alias = `MM_SEARCHTERM${index}`; @@ -291,10 +334,35 @@ function highlightSearchTerm(text, tokens, searchTerm) { originalText: word }); - return prefix + alias; + return alias; } - return output.replace(new RegExp(`()(${escapeRegex(searchTerm)})`, 'gi'), replaceSearchTermWithToken); + for (const term of terms) { + // highlight existing tokens matching search terms + var newTokens = new Map(); + for (const [alias, token] of tokens) { + if (token.originalText === term.replace(/\*$/, '')) { + const index = tokens.size + newTokens.size; + const newAlias = `MM_SEARCHTERM${index}`; + + newTokens.set(newAlias, { + value: `<span class='search-highlight'>${alias}</span>`, + originalText: token.originalText + }); + + output = output.replace(alias, newAlias); + } + } + + // the new tokens are stashed in a separate map since we can't add objects to a map during iteration + for (const newToken of newTokens) { + tokens.set(newToken[0], newToken[1]); + } + + output = output.replace(convertSearchTermToRegex(term), replaceSearchTermWithToken); + } + + return output; } function replaceTokens(text, tokens) { diff --git a/web/sass-files/sass/partials/_content.scss b/web/sass-files/sass/partials/_content.scss index d86e225f3..6228cc45e 100644 --- a/web/sass-files/sass/partials/_content.scss +++ b/web/sass-files/sass/partials/_content.scss @@ -18,14 +18,15 @@ margin-left: 220px; position: relative; background: #fff; - display: flex; + @include display-flex; + @include flex-direction(column); flex-direction: column; .channel__wrap & { padding-top: 0; } } #post-create { - flex: 0 0 auto; + @include flex(0 0 auto); background: #fff; width: 100%; z-index: 3; diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss index 74a7cecff..67c938b8c 100644 --- a/web/sass-files/sass/partials/_headers.scss +++ b/web/sass-files/sass/partials/_headers.scss @@ -2,6 +2,7 @@ padding: 3px 0; height: 58px; flex: 0 0 58px; + @include flex(0 0 50px); } .row { &.header { diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index 33748052d..ef30cb213 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -46,7 +46,6 @@ body.ios { .textarea-wrapper { position:relative; - min-height: 36px; .textbox-preview-area { position: absolute; z-index: 2; @@ -62,6 +61,7 @@ body.ios { font-size: 13px; cursor: pointer; } + min-height:36px; } .date-separator, .new-separator { @@ -197,7 +197,7 @@ body.ios { } #post-list { - flex: 1 1 auto; + @include flex(1 1 auto); position: relative; overflow-y: hidden; .post-list-holder-by-time { @@ -544,9 +544,13 @@ body.ios { } &.post-info { .post-profile-time { - width: 150px; - display: inline-block; - margin-left: 50px; + color: #a8adb7; + vertical-align: top; + max-width: 220px; + overflow: hidden; + display: block; + white-space: nowrap; + text-overflow: ellipsis; } } .post-header-col { @@ -554,6 +558,8 @@ body.ios { display: inline-block; margin-right: 10px; &.post-header__reply { + position: relative; + top: -1px; min-width: 70px; .dropdown-menu { right: 0; @@ -706,4 +712,4 @@ body.ios { margin: 0; } } -} +}
\ No newline at end of file diff --git a/web/sass-files/sass/partials/_signup.scss b/web/sass-files/sass/partials/_signup.scss index b9486e254..6216dd9ae 100644 --- a/web/sass-files/sass/partials/_signup.scss +++ b/web/sass-files/sass/partials/_signup.scss @@ -80,7 +80,7 @@ } .inner__content { - padding: 0 15px; + padding: 0 1rem; margin: 30px 0 20px; } @@ -133,17 +133,18 @@ .or__container { height: 1px; background: #dddddd; - text-align: center; + text-align: left; margin: 2em 0; span { - width: 33px; + width: 40px; top: -10px; position: relative; - font-size: em(16px); + font-size: 1.14286em; line-height: 20px; font-weight: 600; background: #fff; display: inline-block; + text-align: center; } } @@ -152,6 +153,10 @@ padding-left: 18px; } + .signup__email-container { + margin-left: 1rem; + } + .btn { font-size: 1em; padding: em(7px) em(15px); @@ -173,7 +178,7 @@ min-width: 200px; width: 200px; padding: 0 1em; - margin: 1em auto; + margin: 1em 1rem; height: 40px; line-height: 34px; color: #fff; |