diff options
Diffstat (limited to 'web')
38 files changed, 1154 insertions, 186 deletions
diff --git a/web/react/components/channel_loader.jsx b/web/react/components/channel_loader.jsx index ce6f60f87..1261b957b 100644 --- a/web/react/components/channel_loader.jsx +++ b/web/react/components/channel_loader.jsx @@ -12,6 +12,7 @@ var PostStore = require('../stores/post_store.jsx'); var UserStore = require('../stores/user_store.jsx'); var Utils = require('../utils/utils.jsx'); +var Constants = require('../utils/constants.jsx'); export default class ChannelLoader extends React.Component { constructor(props) { @@ -68,25 +69,10 @@ export default class ChannelLoader extends React.Component { /* Update CSS classes to match user theme */ var user = UserStore.getCurrentUser(); - if (user.props && user.props.theme) { - Utils.changeCss('div.theme', 'background-color:' + user.props.theme + ';'); - Utils.changeCss('.btn.btn-primary', 'background: ' + user.props.theme + ';'); - Utils.changeCss('.modal .modal-header', 'background: ' + user.props.theme + ';'); - Utils.changeCss('.mention', 'background: ' + user.props.theme + ';'); - Utils.changeCss('.mention-link', 'color: ' + user.props.theme + ';'); - Utils.changeCss('@media(max-width: 768px){.search-bar__container', 'background: ' + user.props.theme + ';}'); - Utils.changeCss('.search-item-container:hover', 'background: ' + Utils.changeOpacity(user.props.theme, 0.05) + ';'); - } - - if (user.props.theme !== '#000000' && user.props.theme !== '#585858') { - Utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + Utils.changeColor(user.props.theme, -10) + ';'); - Utils.changeCss('a.theme', 'color:' + user.props.theme + '; fill:' + user.props.theme + '!important;'); - } else if (user.props.theme === '#000000') { - Utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + Utils.changeColor(user.props.theme, +50) + ';'); - $('.team__header').addClass('theme--black'); - } else if (user.props.theme === '#585858') { - Utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + Utils.changeColor(user.props.theme, +10) + ';'); - $('.team__header').addClass('theme--gray'); + if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) { + Utils.applyTheme(user.theme_props); + } else { + Utils.applyTheme(Constants.THEMES.default); } /* Setup global mouse events */ diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx index ec873dd00..a2ca8b00f 100644 --- a/web/react/components/popover_list_members.jsx +++ b/web/react/components/popover_list_members.jsx @@ -65,7 +65,7 @@ export default class PopoverListMembers extends React.Component { > {count} <span - className='glyphicon glyphicon-user' + className='fa fa-user' aria-hidden='true' /> </div> diff --git a/web/react/components/register_app_modal.jsx b/web/react/components/register_app_modal.jsx index 3dd5c094e..473ff3f91 100644 --- a/web/react/components/register_app_modal.jsx +++ b/web/react/components/register_app_modal.jsx @@ -228,7 +228,7 @@ export default class RegisterAppModal extends React.Component { data-dismiss='modal' aria-label='Close' > - <span aria-hidden='true'>{'x'}</span> + <span aria-hidden='true'>{'×'}</span> </button> <h4 className='modal-title' diff --git a/web/react/components/rhs_header_post.jsx b/web/react/components/rhs_header_post.jsx index 5156ec4d7..f55c4095e 100644 --- a/web/react/components/rhs_header_post.jsx +++ b/web/react/components/rhs_header_post.jsx @@ -65,6 +65,7 @@ export default class RhsHeaderPost extends React.Component { aria-label='Close' onClick={this.handleClose} > + <i className='fa fa-sign-out'/> </button> </div> ); diff --git a/web/react/components/search_results_header.jsx b/web/react/components/search_results_header.jsx index 694f0c55d..4e8a3ef10 100644 --- a/web/react/components/search_results_header.jsx +++ b/web/react/components/search_results_header.jsx @@ -50,6 +50,7 @@ export default class SearchResultsHeader extends React.Component { title='Close' onClick={this.handleClose} > + <i className='fa fa-sign-out'/> </button> </div> ); diff --git a/web/react/components/user_settings/custom_theme_chooser.jsx b/web/react/components/user_settings/custom_theme_chooser.jsx new file mode 100644 index 000000000..44630a318 --- /dev/null +++ b/web/react/components/user_settings/custom_theme_chooser.jsx @@ -0,0 +1,108 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var Constants = require('../../utils/constants.jsx'); + +export default class CustomThemeChooser extends React.Component { + constructor(props) { + super(props); + + this.onPickerChange = this.onPickerChange.bind(this); + this.onInputChange = this.onInputChange.bind(this); + this.pasteBoxChange = this.pasteBoxChange.bind(this); + + this.state = {}; + } + componentDidMount() { + $('.color-picker').colorpicker().on('changeColor', this.onPickerChange); + } + onPickerChange(e) { + const theme = this.props.theme; + theme[e.target.id] = e.color.toHex(); + theme.type = 'custom'; + this.props.updateTheme(theme); + } + onInputChange(e) { + const theme = this.props.theme; + theme[e.target.parentNode.id] = e.target.value; + theme.type = 'custom'; + this.props.updateTheme(theme); + } + pasteBoxChange(e) { + const text = e.target.value; + + if (text.length === 0) { + return; + } + + const colors = text.split(','); + + const theme = {type: 'custom'}; + let index = 0; + Constants.THEME_ELEMENTS.forEach((element) => { + if (index < colors.length) { + theme[element.id] = colors[index]; + } + index++; + }); + + this.props.updateTheme(theme); + } + render() { + const theme = this.props.theme; + + const elements = []; + let colors = ''; + Constants.THEME_ELEMENTS.forEach((element) => { + elements.push( + <div className='col-sm-4 form-group'> + <label className='custom-label'>{element.uiName}</label> + <div + className='input-group color-picker' + id={element.id} + > + <input + className='form-control' + type='text' + defaultValue={theme[element.id]} + onChange={this.onInputChange} + /> + <span className='input-group-addon'><i></i></span> + </div> + </div> + ); + + colors += theme[element.id] + ','; + }); + + const pasteBox = ( + <div className='col-sm-12'> + <label className='custom-label'> + {'Copy and paste to share theme colors:'} + </label> + <input + type='text' + className='form-control' + value={colors} + onChange={this.pasteBoxChange} + /> + </div> + ); + + return ( + <div> + <div className='row form-group'> + {elements} + </div> + <div className='row'> + {pasteBox} + </div> + </div> + ); + } +} + +CustomThemeChooser.propTypes = { + theme: React.PropTypes.object.isRequired, + updateTheme: React.PropTypes.func.isRequired +}; diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx new file mode 100644 index 000000000..48be83afe --- /dev/null +++ b/web/react/components/user_settings/import_theme_modal.jsx @@ -0,0 +1,179 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +const UserStore = require('../../stores/user_store.jsx'); +const Utils = require('../../utils/utils.jsx'); +const Client = require('../../utils/client.jsx'); +const Modal = ReactBootstrap.Modal; + +const AppDispatcher = require('../../dispatcher/app_dispatcher.jsx'); +const Constants = require('../../utils/constants.jsx'); +const ActionTypes = Constants.ActionTypes; + +export default class ImportThemeModal extends React.Component { + constructor(props) { + super(props); + + this.updateShow = this.updateShow.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + this.handleChange = this.handleChange.bind(this); + + this.state = { + inputError: '', + show: false + }; + } + componentDidMount() { + UserStore.addImportModalListener(this.updateShow); + } + componentWillUnmount() { + UserStore.removeImportModalListener(this.updateShow); + } + updateShow(show) { + this.setState({show}); + } + handleSubmit(e) { + e.preventDefault(); + + const text = React.findDOMNode(this.refs.input).value; + + if (!this.isInputValid(text)) { + this.setState({inputError: 'Invalid format, please try copying and pasting in again.'}); + return; + } + + const colors = text.split(','); + const theme = {type: 'custom'}; + + theme.sidebarBg = colors[0]; + theme.sidebarText = colors[5]; + theme.sidebarUnreadText = colors[5]; + theme.sidebarTextHoverBg = colors[4]; + theme.sidebarTextHoverColor = colors[5]; + theme.sidebarTextActiveBg = colors[2]; + theme.sidebarTextActiveColor = colors[3]; + theme.sidebarHeaderBg = colors[1]; + theme.sidebarHeaderTextColor = colors[5]; + theme.onlineIndicator = colors[6]; + theme.mentionBj = colors[7]; + theme.mentionColor = '#ffffff'; + theme.centerChannelBg = '#ffffff'; + theme.centerChannelColor = '#333333'; + theme.linkColor = '#2389d7'; + theme.buttonBg = '#26a970'; + theme.buttonColor = '#ffffff'; + + let user = UserStore.getCurrentUser(); + user.theme_props = theme; + + Client.updateUser(user, + (data) => { + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_ME, + me: data + }); + + this.setState({show: false}); + Utils.applyTheme(theme); + $('#user_settings').modal('show'); + }, + (err) => { + var state = this.getStateFromStores(); + state.serverError = err; + this.setState(state); + } + ); + } + isInputValid(text) { + if (text.length === 0) { + return false; + } + + if (text.indexOf(' ') !== -1) { + return false; + } + + if (text.length > 0 && text.indexOf(',') === -1) { + return false; + } + + if (text.length > 0) { + const colors = text.split(','); + + if (colors.length !== 8) { + return false; + } + + for (let i = 0; i < colors.length; i++) { + if (colors[i].length !== 7 && colors[i].length !== 4) { + return false; + } + + if (colors[i].charAt(0) !== '#') { + return false; + } + } + } + + return true; + } + handleChange(e) { + if (this.isInputValid(e.target.value)) { + this.setState({inputError: null}); + } else { + this.setState({inputError: 'Invalid format, please try copying and pasting in again.'}); + } + } + render() { + return ( + <span> + <Modal + show={this.state.show} + onHide={() => this.setState({show: false})} + > + <Modal.Header closeButton={true}> + <Modal.Title>{'Import Slack Theme'}</Modal.Title> + </Modal.Header> + <form + role='form' + className='form-horizontal' + > + <Modal.Body> + <p> + {'To import a theme, go to a Slack team and look for “”Preferences” -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:'} + </p> + <div className='form-group less'> + <div className='col-sm-9'> + <input + ref='input' + type='text' + className='form-control' + onChange={this.handleChange} + /> + {this.state.inputError} + </div> + </div> + </Modal.Body> + <Modal.Footer> + <button + type='button' + className='btn btn-default' + onClick={() => this.setState({show: false})} + > + {'Cancel'} + </button> + <button + onClick={this.handleSubmit} + type='submit' + className='btn btn-primary' + tabIndex='3' + > + {'Submit'} + </button> + </Modal.Footer> + </form> + </Modal> + </span> + ); + } +} diff --git a/web/react/components/user_settings/premade_theme_chooser.jsx b/web/react/components/user_settings/premade_theme_chooser.jsx new file mode 100644 index 000000000..e36503053 --- /dev/null +++ b/web/react/components/user_settings/premade_theme_chooser.jsx @@ -0,0 +1,55 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var Utils = require('../../utils/utils.jsx'); +var Constants = require('../../utils/constants.jsx'); + +export default class PremadeThemeChooser extends React.Component { + constructor(props) { + super(props); + this.state = {}; + } + render() { + const theme = this.props.theme; + + const premadeThemes = []; + for (const k in Constants.THEMES) { + if (Constants.THEMES.hasOwnProperty(k)) { + const premadeTheme = $.extend(true, {}, Constants.THEMES[k]); + + let activeClass = ''; + if (premadeTheme.type === theme.type) { + activeClass = 'active'; + } + + premadeThemes.push( + <div className='col-sm-3 premade-themes'> + <div + className={activeClass} + onClick={() => this.props.updateTheme(premadeTheme)} + > + <label> + <img + className='img-responsive' + src={'/static/images/themes/' + premadeTheme.type + '.png'} + /> + <div className='theme-label'>{Utils.toTitleCase(premadeTheme.type)}</div> + </label> + </div> + </div> + ); + } + } + + return ( + <div className='row'> + {premadeThemes} + </div> + ); + } +} + +PremadeThemeChooser.propTypes = { + theme: React.PropTypes.object.isRequired, + updateTheme: React.PropTypes.func.isRequired +}; diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx index aec3b319d..7617f04d1 100644 --- a/web/react/components/user_settings/user_settings_appearance.jsx +++ b/web/react/components/user_settings/user_settings_appearance.jsx @@ -2,78 +2,119 @@ // See License.txt for license information. var UserStore = require('../../stores/user_store.jsx'); -var SettingItemMin = require('../setting_item_min.jsx'); -var SettingItemMax = require('../setting_item_max.jsx'); var Client = require('../../utils/client.jsx'); var Utils = require('../../utils/utils.jsx'); -var ThemeColors = ['#2389d7', '#008a17', '#dc4fad', '#ac193d', '#0072c6', '#d24726', '#ff8f32', '#82ba00', '#03b3b2', '#008299', '#4617b4', '#8c0095', '#004b8b', '#004b8b', '#570000', '#380000', '#585858', '#000000']; +const CustomThemeChooser = require('./custom_theme_chooser.jsx'); +const PremadeThemeChooser = require('./premade_theme_chooser.jsx'); +const AppDispatcher = require('../../dispatcher/app_dispatcher.jsx'); +const Constants = require('../../utils/constants.jsx'); +const ActionTypes = Constants.ActionTypes; export default class UserSettingsAppearance extends React.Component { constructor(props) { super(props); + this.onChange = this.onChange.bind(this); this.submitTheme = this.submitTheme.bind(this); this.updateTheme = this.updateTheme.bind(this); this.handleClose = this.handleClose.bind(this); + this.handleImportModal = this.handleImportModal.bind(this); this.state = this.getStateFromStores(); + + this.originalTheme = this.state.theme; + } + componentDidMount() { + UserStore.addChangeListener(this.onChange); + + if (this.props.activeSection === 'theme') { + $(React.findDOMNode(this.refs[this.state.theme])).addClass('active-border'); + } + $('#user_settings').on('hidden.bs.modal', this.handleClose); + } + componentDidUpdate() { + if (this.props.activeSection === 'theme') { + $('.color-btn').removeClass('active-border'); + $(React.findDOMNode(this.refs[this.state.theme])).addClass('active-border'); + } + } + componentWillUnmount() { + UserStore.removeChangeListener(this.onChange); + $('#user_settings').off('hidden.bs.modal', this.handleClose); } getStateFromStores() { - var user = UserStore.getCurrentUser(); - var theme = '#2389d7'; - if (ThemeColors != null) { - theme = ThemeColors[0]; + const user = UserStore.getCurrentUser(); + let theme = null; + + if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) { + theme = user.theme_props; + } else { + theme = $.extend(true, {}, Constants.THEMES.default); } - if (user.props && user.props.theme) { - theme = user.props.theme; + + let type = 'premade'; + if (theme.type === 'custom') { + type = 'custom'; } - return {theme: theme.toLowerCase()}; + return {theme, type}; + } + onChange() { + const newState = this.getStateFromStores(); + + if (!Utils.areStatesEqual(this.state, newState)) { + this.setState(newState); + } } submitTheme(e) { e.preventDefault(); var user = UserStore.getCurrentUser(); - if (!user.props) { - user.props = {}; - } - user.props.theme = this.state.theme; + user.theme_props = this.state.theme; Client.updateUser(user, - function success() { - this.props.updateSection(''); - window.location.reload(); - }.bind(this), - function fail(err) { + (data) => { + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_ME, + me: data + }); + + $('#user_settings').off('hidden.bs.modal', this.handleClose); + this.props.updateTab('general'); + $('#user_settings').modal('hide'); + }, + (err) => { var state = this.getStateFromStores(); state.serverError = err; this.setState(state); - }.bind(this) + } ); } - updateTheme(e) { - var hex = Utils.rgb2hex(e.target.style.backgroundColor); - this.setState({theme: hex.toLowerCase()}); + updateTheme(theme) { + this.setState({theme}); + Utils.applyTheme(theme); } - handleClose() { - this.setState({serverError: null}); - this.props.updateTab('general'); + updateType(type) { + this.setState({type}); } - componentDidMount() { - if (this.props.activeSection === 'theme') { - $(React.findDOMNode(this.refs[this.state.theme])).addClass('active-border'); - } - $('#user_settings').on('hidden.bs.modal', this.handleClose); - } - componentDidUpdate() { - if (this.props.activeSection === 'theme') { - $('.color-btn').removeClass('active-border'); - $(React.findDOMNode(this.refs[this.state.theme])).addClass('active-border'); - } + handleClose() { + const state = this.getStateFromStores(); + state.serverError = null; + + Utils.applyTheme(state.theme); + + this.setState(state); + + $('.ps-container.modal-body').scrollTop(0); + $('.ps-container.modal-body').perfectScrollbar('update'); + $('#user_settings').modal('hide'); } - componentWillUnmount() { - $('#user_settings').off('hidden.bs.modal', this.handleClose); - this.props.updateSection(''); + handleImportModal() { + $('#user_settings').modal('hide'); + AppDispatcher.handleViewAction({ + type: ActionTypes.TOGGLE_IMPORT_THEME_MODAL, + value: true + }); } render() { var serverError; @@ -81,67 +122,73 @@ export default class UserSettingsAppearance extends React.Component { serverError = this.state.serverError; } - var themeSection; - var self = this; - - if (ThemeColors != null) { - if (this.props.activeSection === 'theme') { - var themeButtons = []; - - for (var i = 0; i < ThemeColors.length; i++) { - themeButtons.push( - <button - key={ThemeColors[i] + 'key' + i} - ref={ThemeColors[i]} - type='button' - className='btn btn-lg color-btn' - style={{backgroundColor: ThemeColors[i]}} - onClick={this.updateTheme} - /> - ); - } - - var inputs = []; - - inputs.push( - <li - key='themeColorSetting' - className='setting-list-item' - > - <div - className='btn-group' - data-toggle='buttons-radio' - > - {themeButtons} - </div> - </li> - ); - - themeSection = ( - <SettingItemMax - title='Theme Color' - inputs={inputs} - submit={this.submitTheme} - serverError={serverError} - updateSection={function updateSection(e) { - self.props.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - themeSection = ( - <SettingItemMin - title='Theme Color' - describe={this.state.theme} - updateSection={function updateSection() { - self.props.updateSection('theme'); - }} - /> - ); - } + const displayCustom = this.state.type === 'custom'; + + let custom; + let premade; + if (displayCustom) { + custom = ( + <CustomThemeChooser + theme={this.state.theme} + updateTheme={this.updateTheme} + /> + ); + } else { + premade = ( + <PremadeThemeChooser + theme={this.state.theme} + updateTheme={this.updateTheme} + /> + ); } + const themeUI = ( + <div className='section-max appearance-section'> + <div className='col-sm-12'> + <div className='radio'> + <label> + <input type='radio' + checked={!displayCustom} + onChange={this.updateType.bind(this, 'premade')} + > + {'Theme Colors'} + </input> + </label> + <br/> + </div> + {premade} + <div className='radio'> + <label> + <input type='radio' + checked={displayCustom} + onChange={this.updateType.bind(this, 'custom')} + > + {'Custom Theme'} + </input> + </label> + <br/> + </div> + {custom} + <hr /> + {serverError} + <a + className='btn btn-sm btn-primary' + href='#' + onClick={this.submitTheme} + > + {'Submit'} + </a> + <a + className='btn btn-sm theme' + href='#' + onClick={this.handleClose} + > + {'Cancel'} + </a> + </div> + </div> + ); + return ( <div> <div className='modal-header'> @@ -151,21 +198,28 @@ export default class UserSettingsAppearance extends React.Component { data-dismiss='modal' aria-label='Close' > - <span aria-hidden='true'>×</span> + <span aria-hidden='true'>{'×'}</span> </button> <h4 className='modal-title' ref='title' > - <i className='modal-back'></i>Appearance Settings + <i className='modal-back'></i>{'Appearance Settings'} </h4> </div> <div className='user-settings'> - <h3 className='tab-header'>Appearance Settings</h3> + <h3 className='tab-header'>{'Appearance Settings'}</h3> <div className='divider-dark first'/> - {themeSection} + {themeUI} <div className='divider-dark'/> </div> + <br/> + <a + className='theme' + onClick={this.handleImportModal} + > + {'Import from Slack'} + </a> </div> ); } @@ -176,6 +230,5 @@ UserSettingsAppearance.defaultProps = { }; UserSettingsAppearance.propTypes = { activeSection: React.PropTypes.string, - updateSection: React.PropTypes.func, updateTab: React.PropTypes.func }; diff --git a/web/react/components/user_settings/user_settings_developer.jsx b/web/react/components/user_settings/user_settings_developer.jsx index 1694aaa79..d9fb43902 100644 --- a/web/react/components/user_settings/user_settings_developer.jsx +++ b/web/react/components/user_settings/user_settings_developer.jsx @@ -64,7 +64,7 @@ export default class DeveloperTab extends React.Component { data-dismiss='modal' aria-label='Close' > - <span aria-hidden='true'>{'x'}</span> + <span aria-hidden='true'>{'×'}</span> </button> <h4 className='modal-title' diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx index 1b22e6045..430a7ec7c 100644 --- a/web/react/components/user_settings/user_settings_modal.jsx +++ b/web/react/components/user_settings/user_settings_modal.jsx @@ -60,7 +60,7 @@ export default class UserSettingsModal extends React.Component { data-dismiss='modal' aria-label='Close' > - <span aria-hidden='true'>{'x'}</span> + <span aria-hidden='true'>{'×'}</span> </button> <h4 className='modal-title' diff --git a/web/react/package.json b/web/react/package.json index dd7d45f8a..a9eba6c6c 100644 --- a/web/react/package.json +++ b/web/react/package.json @@ -4,13 +4,14 @@ "private": true, "dependencies": { "autolinker": "0.18.1", + "babel-runtime": "5.8.24", + "bootstrap-colorpicker": "2.2.0", "flux": "2.1.1", "keymirror": "0.1.1", + "marked": "0.3.5", "object-assign": "3.0.0", "react-zeroclipboard-mixin": "0.1.0", - "twemoji": "1.4.1", - "babel-runtime": "5.8.24", - "marked": "0.3.5" + "twemoji": "1.4.1" }, "devDependencies": { "browserify": "11.0.1", @@ -28,8 +29,15 @@ }, "browserify": { "transform": [ - ["babelify", { "optional": ["runtime"] }], - "envify" + [ + "babelify", + { + "optional": [ + "runtime" + ] + } + ], + "envify" ] }, "jest": { diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index d24fe0b98..07207c556 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -34,6 +34,7 @@ var ActivityLogModal = require('../components/activity_log_modal.jsx'); var RemovedFromChannelModal = require('../components/removed_from_channel_modal.jsx'); var FileUploadOverlay = require('../components/file_upload_overlay.jsx'); var RegisterAppModal = require('../components/register_app_modal.jsx'); +var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; @@ -85,6 +86,11 @@ function setupChannelPage(props) { ); React.render( + <ImportThemeModal />, + document.getElementById('import_theme_modal') + ); + + React.render( <TeamSettingsModal teamDisplayName={props.TeamDisplayName} />, document.getElementById('team_settings_modal') ); diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx index f75c1d4c3..8842263fa 100644 --- a/web/react/stores/user_store.jsx +++ b/web/react/stores/user_store.jsx @@ -14,6 +14,7 @@ var CHANGE_EVENT_SESSIONS = 'change_sessions'; var CHANGE_EVENT_AUDITS = 'change_audits'; var CHANGE_EVENT_TEAMS = 'change_teams'; var CHANGE_EVENT_STATUSES = 'change_statuses'; +var TOGGLE_IMPORT_MODAL_EVENT = 'toggle_import_modal'; class UserStoreClass extends EventEmitter { constructor() { @@ -34,6 +35,9 @@ class UserStoreClass extends EventEmitter { this.emitStatusesChange = this.emitStatusesChange.bind(this); this.addStatusesChangeListener = this.addStatusesChangeListener.bind(this); this.removeStatusesChangeListener = this.removeStatusesChangeListener.bind(this); + this.emitToggleImportModal = this.emitToggleImportModal.bind(this); + this.addImportModalListener = this.addImportModalListener.bind(this); + this.removeImportModalListener = this.removeImportModalListener.bind(this); this.setCurrentId = this.setCurrentId.bind(this); this.getCurrentId = this.getCurrentId.bind(this); this.getCurrentUser = this.getCurrentUser.bind(this); @@ -114,6 +118,15 @@ class UserStoreClass extends EventEmitter { removeStatusesChangeListener(callback) { this.removeListener(CHANGE_EVENT_STATUSES, callback); } + emitToggleImportModal(value) { + this.emit(TOGGLE_IMPORT_MODAL_EVENT, value); + } + addImportModalListener(callback) { + this.on(TOGGLE_IMPORT_MODAL_EVENT, callback); + } + removeImportModalListener(callback) { + this.removeListener(TOGGLE_IMPORT_MODAL_EVENT, callback); + } setCurrentId(id) { this.gCurrentId = id; if (id == null) { @@ -321,6 +334,9 @@ UserStore.dispatchToken = AppDispatcher.register(function registry(payload) { UserStore.pSetStatuses(action.statuses); UserStore.emitStatusesChange(); break; + case ActionTypes.TOGGLE_IMPORT_THEME_MODAL: + UserStore.emitToggleImportModal(action.value); + break; default: } diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 03e4635b5..41e9e9ca6 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -36,7 +36,9 @@ module.exports = { RECIEVED_CONFIG: null, - RECIEVED_LOGS: null + RECIEVED_LOGS: null, + + TOGGLE_IMPORT_THEME_MODAL: null }), PayloadSources: keyMirror({ @@ -110,5 +112,138 @@ module.exports = { ONLINE_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns#' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' sodipodi:docname='TRASH_1_4.svg' inkscape:version='0.48.4 r9939' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='12px' height='12px' viewBox='0 0 12 12' enable-background='new 0 0 12 12' xml:space='preserve'><sodipodi:namedview inkscape:cy='139.7898' inkscape:cx='26.358185' inkscape:zoom='1.18' showguides='true' showgrid='false' id='namedview6' guidetolerance='10' gridtolerance='10' objecttolerance='10' borderopacity='1' bordercolor='#666666' pagecolor='#ffffff' inkscape:current-layer='Layer_1' inkscape:window-maximized='1' inkscape:window-y='-8' inkscape:window-x='-8' inkscape:window-height='705' inkscape:window-width='1366' inkscape:guide-bbox='true' inkscape:pageshadow='2' inkscape:pageopacity='0'><sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide><sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide></sodipodi:namedview><g><g><path class='online--icon' d='M6,5.487c1.371,0,2.482-1.116,2.482-2.493c0-1.378-1.111-2.495-2.482-2.495S3.518,1.616,3.518,2.994C3.518,4.371,4.629,5.487,6,5.487z M10.452,8.545c-0.101-0.829-0.36-1.968-0.726-2.541C9.475,5.606,8.5,5.5,8.5,5.5S8.43,7.521,6,7.521C3.507,7.521,3.5,5.5,3.5,5.5S2.527,5.606,2.273,6.004C1.908,6.577,1.648,7.716,1.547,8.545C1.521,8.688,1.49,9.082,1.498,9.142c0.161,1.295,2.238,2.322,4.375,2.358C5.916,11.501,5.958,11.501,6,11.501c0.043,0,0.084,0,0.127-0.001c2.076-0.026,4.214-1.063,4.375-2.358C10.509,9.082,10.471,8.696,10.452,8.545z'/></g></g></svg>", OFFLINE_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns#' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' sodipodi:docname='TRASH_1_4.svg' inkscape:version='0.48.4 r9939' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='12px' height='12px' viewBox='0 0 12 12' enable-background='new 0 0 12 12' xml:space='preserve'><sodipodi:namedview inkscape:cy='139.7898' inkscape:cx='26.358185' inkscape:zoom='1.18' showguides='true' showgrid='false' id='namedview6' guidetolerance='10' gridtolerance='10' objecttolerance='10' borderopacity='1' bordercolor='#666666' pagecolor='#ffffff' inkscape:current-layer='Layer_1' inkscape:window-maximized='1' inkscape:window-y='-8' inkscape:window-x='-8' inkscape:window-height='705' inkscape:window-width='1366' inkscape:guide-bbox='true' inkscape:pageshadow='2' inkscape:pageopacity='0'><sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide><sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide></sodipodi:namedview><g><g><path fill='#cccccc' d='M6.002,7.143C5.645,7.363,5.167,7.52,4.502,7.52c-2.493,0-2.5-2.02-2.5-2.02S1.029,5.607,0.775,6.004C0.41,6.577,0.15,7.716,0.049,8.545c-0.025,0.145-0.057,0.537-0.05,0.598c0.162,1.295,2.237,2.321,4.375,2.357c0.043,0.001,0.085,0.001,0.127,0.001c0.043,0,0.084,0,0.127-0.001c1.879-0.023,3.793-0.879,4.263-2h-2.89L6.002,7.143L6.002,7.143z M4.501,5.488c1.372,0,2.483-1.117,2.483-2.494c0-1.378-1.111-2.495-2.483-2.495c-1.371,0-2.481,1.117-2.481,2.495C2.02,4.371,3.13,5.488,4.501,5.488z M7.002,6.5v2h5v-2H7.002z'/></g></g></svg>", MENU_ICON: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>", - COMMENT_ICON: "<svg version='1.1' id='Layer_2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='15px' height='15px' viewBox='1 1.5 15 15' enable-background='new 1 1.5 15 15' xml:space='preserve'> <g> <g> <path fill='#211B1B' d='M14,1.5H3c-1.104,0-2,0.896-2,2v8c0,1.104,0.896,2,2,2h1.628l1.884,3l1.866-3H14c1.104,0,2-0.896,2-2v-8 C16,2.396,15.104,1.5,14,1.5z M15,11.5c0,0.553-0.447,1-1,1H8l-1.493,2l-1.504-1.991L5,12.5H3c-0.552,0-1-0.447-1-1v-8 c0-0.552,0.448-1,1-1h11c0.553,0,1,0.448,1,1V11.5z'/> </g> </g> </svg>" + COMMENT_ICON: "<svg version='1.1' id='Layer_2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='15px' height='15px' viewBox='1 1.5 15 15' enable-background='new 1 1.5 15 15' xml:space='preserve'> <g> <g> <path fill='#211B1B' d='M14,1.5H3c-1.104,0-2,0.896-2,2v8c0,1.104,0.896,2,2,2h1.628l1.884,3l1.866-3H14c1.104,0,2-0.896,2-2v-8 C16,2.396,15.104,1.5,14,1.5z M15,11.5c0,0.553-0.447,1-1,1H8l-1.493,2l-1.504-1.991L5,12.5H3c-0.552,0-1-0.447-1-1v-8 c0-0.552,0.448-1,1-1h11c0.553,0,1,0.448,1,1V11.5z'/> </g> </g> </svg>", + THEMES: { + default: { + type: 'Mattermost', + sidebarBg: '#fafafa', + sidebarText: '#999999', + sidebarUnreadText: '#333333', + sidebarTextHoverBg: '#e6f2fa', + sidebarTextHoverColor: '#999999', + sidebarTextActiveBg: '#e1e1e1', + sidebarTextActiveColor: '#111111', + sidebarHeaderBg: '#2389d7', + sidebarHeaderTextColor: '#ffffff', + onlineIndicator: '#7DBE00', + mentionBj: '#2389d7', + mentionColor: '#ffffff', + centerChannelBg: '#ffffff', + centerChannelColor: '#333333', + linkColor: '#2389d7', + buttonBg: '#2389d7', + buttonColor: '#FFFFFF' + }, + slack: { + type: 'Slack', + sidebarBg: '#4D394B', + sidebarText: '#ab9ba9', + sidebarUnreadText: '#FFFFFF', + sidebarTextHoverBg: '#3e313c', + sidebarTextHoverColor: '#ab9ba9', + sidebarTextActiveBg: '#4c9689', + sidebarTextActiveColor: '#FFFFFF', + sidebarHeaderBg: '#4D394B', + sidebarHeaderTextColor: '#FFFFFF', + onlineIndicator: '#4c9689', + mentionBj: '#eb4d5c', + mentionColor: '#FFFFFF', + centerChannelBg: '#FFFFFF', + centerChannelColor: '#333333', + linkColor: '#2389d7', + buttonBg: '#26a970', + buttonColor: '#FFFFFF' + }, + dark: { + type: 'Dark', + sidebarBg: '#1B2C3E', + sidebarText: '#bbbbbb', + sidebarUnreadText: '#fff', + sidebarTextHoverBg: '#4A5664', + sidebarTextHoverColor: '#bbbbbb', + sidebarTextActiveBg: '#39769C', + sidebarTextActiveColor: '#FFFFFF', + sidebarHeaderBg: '#1B2C3E', + sidebarHeaderTextColor: '#FFFFFF', + onlineIndicator: '#4c9689', + mentionBj: '#B74A4A', + mentionColor: '#FFFFFF', + centerChannelBg: '#2F3E4E', + centerChannelColor: '#DDDDDD', + linkColor: '#A4FFEB', + buttonBg: '#2B9C99', + buttonColor: '#FFFFFF' + } + }, + THEME_ELEMENTS: [ + { + id: 'sidebarBg', + uiName: 'Sidebar BG' + }, + { + id: 'sidebarText', + uiName: 'Sidebar text color' + }, + { + id: 'sidebarHeaderBg', + uiName: 'Sidebar Header BG' + }, + { + id: 'sidebarHeaderTextColor', + uiName: 'Sidebar Header text color' + }, + { + id: 'sidebarUnreadText', + uiName: 'Sidebar unread text color' + }, + { + id: 'sidebarTextHoverBg', + uiName: 'Sidebar text hover BG' + }, + { + id: 'sidebarTextHoverColor', + uiName: 'Sidebar text hover color' + }, + { + id: 'sidebarTextActiveBg', + uiName: 'Sidebar text active BG' + }, + { + id: 'sidebarTextActiveColor', + uiName: 'Sidebar text active color' + }, + { + id: 'onlineIndicator', + uiName: 'Online indicator' + }, + { + id: 'mentionBj', + uiName: 'Mention jewel BG' + }, + { + id: 'mentionColor', + uiName: 'Mention jewel text color' + }, + { + id: 'centerChannelBg', + uiName: 'Center channel BG' + }, + { + id: 'centerChannelColor', + uiName: 'Center channel text color' + }, + { + id: 'linkColor', + uiName: 'Link color' + }, + { + id: 'buttonBg', + uiName: 'Button BG' + }, + + { + id: 'buttonColor', + uiName: 'Button Color' + } + ] }; diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 074591489..a8860eb94 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -542,7 +542,119 @@ export function toTitleCase(str) { return str.replace(/\w\S*/g, doTitleCase); } -export function changeCss(className, classValue) { +export function applyTheme(theme) { + if (theme.sidebarBg) { + changeCss('.sidebar--left', 'background:' + theme.sidebarBg, 1); + changeCss('@media(max-width: 768px){.search-bar__container', 'background:' + theme.sidebarBg, 1); + } + + if (theme.sidebarText) { + changeCss('.sidebar--left .nav li>a, .sidebar--right', 'color:' + theme.sidebarText, 1); + changeCss('.sidebar--left .nav li>h4, .sidebar--left .add-channel-btn', 'color:' + changeOpacity(theme.sidebarText, 0.8), 1); + changeCss('.sidebar--left .add-channel-btn:hover, .sidebar--left .add-channel-btn:focus', 'color:' + theme.sidebarText, 1); + changeCss('.sidebar--left, .sidebar--right .sidebar--right__header', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 1); + changeCss('.sidebar--right .sidebar-right__body', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 2); + changeCss('.sidebar--left .status path', 'fill:' + changeOpacity(theme.sidebarText, 0.5), 1); + } + + if (theme.sidebarUnreadText) { + changeCss('.sidebar--left .nav li>a.unread-title', 'color:' + theme.sidebarUnreadText + '!important;', 1); + } + + if (theme.sidebarTextHoverBg) { + changeCss('.sidebar--left .nav li>a:hover, .sidebar--left .nav li>a:focus', 'background:' + theme.sidebarTextHoverBg, 1); + } + + if (theme.sidebarTextHoverColor) { + changeCss('.sidebar--left .nav li>a:hover, .sidebar--left .nav li>a:focus', 'color:' + theme.sidebarTextHoverColor, 2); + } + + if (theme.sidebarTextActiveBg) { + changeCss('.sidebar--left .nav li.active a, .sidebar--left .nav li.active a:hover, .sidebar--left .nav li.active a:focus', 'background:' + theme.sidebarTextActiveBg, 1); + } + + if (theme.sidebarTextActiveColor) { + changeCss('.sidebar--left .nav li.active a, .sidebar--left .nav li.active a:hover, .sidebar--left .nav li.active a:focus', 'color:' + theme.sidebarTextActiveColor, 2); + } + + if (theme.sidebarHeaderBg) { + changeCss('.sidebar--left .team__header, .sidebar--menu .team__header', 'background:' + theme.sidebarHeaderBg, 1); + changeCss('.modal .modal-header', 'background:' + theme.sidebarHeaderBg, 1); + changeCss('#navbar .navbar-default', 'background:' + theme.sidebarHeaderBg, 1); + } + + if (theme.sidebarHeaderTextColor) { + changeCss('.sidebar--left .team__header .header__info, .sidebar--menu .team__header .header__info', 'color:' + theme.sidebarHeaderTextColor, 1); + changeCss('.sidebar--left .team__header .user__name, .sidebar--menu .team__header .user__name', 'color:' + changeOpacity(theme.sidebarHeaderTextColor, 0.8), 1); + changeCss('.sidebar--left .team__header:hover .user__name, .sidebar--menu .team__header:hover .user__name', 'color:' + theme.sidebarHeaderTextColor, 1); + changeCss('.modal .modal-header .modal-title, .modal .modal-header .modal-title .name, .modal .modal-header button.close', 'color:' + theme.sidebarHeaderTextColor, 1); + changeCss('#navbar .navbar-default .navbar-brand .heading, ', 'color:' + theme.sidebarHeaderTextColor, 1); + changeCss('#navbar .navbar-default .navbar-toggle .icon-bar, ', 'background:' + theme.sidebarHeaderTextColor, 1); + } + + if (theme.onlineIndicator) { + changeCss('.sidebar--left .status .online--icon', 'fill:' + theme.onlineIndicator, 1); + } + + if (theme.mentionBj) { + changeCss('.sidebar--left .nav-pills__unread-indicator', 'background:' + theme.mentionBj, 1); + changeCss('.sidebar--left .badge', 'background:' + theme.mentionBj, 1); + } + + if (theme.mentionColor) { + changeCss('.sidebar--left .nav-pills__unread-indicator', 'color:' + theme.mentionColor, 2); + changeCss('.sidebar--left .badge', 'color:' + theme.mentionColor, 2); + } + + if (theme.centerChannelBg) { + changeCss('.app__content', 'background:' + theme.centerChannelBg, 1); + changeCss('#post-list .post-list-holder-by-time', 'background:' + theme.centerChannelBg, 1); + changeCss('#post-create', 'background:' + theme.centerChannelBg, 1); + changeCss('.search-bar__container .search__form .search-bar', 'background:' + theme.centerChannelBg, 1); + changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1); + changeCss('.sidebar--right', 'background:' + theme.centerChannelBg, 1); + } + + if (theme.centerChannelColor) { + changeCss('.app__content', 'color:' + theme.centerChannelColor, 2); + changeCss('#post-create', 'color:' + theme.centerChannelColor, 2); + changeCss('.channel-header .heading', 'color:' + theme.centerChannelColor, 1); + changeCss('.channel-header__info>div.dropdown .header-dropdown__icon', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1); + changeCss('.channel-header #member_popover', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1); + changeCss('.custom-textarea', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2) + '!important; color: ' + theme.centerChannelColor, 1); + changeCss('.search-bar__container .search__form .search-bar', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2) + '; color: ' + theme.centerChannelColor, 2); + changeCss('.search-bar__container .search__form', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); + changeCss('.channel-intro .channel-intro__content', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1); + changeCss('.date-separator .separator__text, .new-separator .separator__text', 'color:' + theme.centerChannelColor, 2); + changeCss('.date-separator .separator__hr, .new-separator .separator__hr, .post-right__container .post.post--root hr, .search-item-container', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); + changeCss('.channel-intro', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); + changeCss('.post.current--user .post-body, .post.post--comment.other--root.current--user .post-comment', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); + changeCss('.post.current--user .post-body, .post.post--comment.other--root.current--user .post-comment', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); + changeCss('@media(max-width: 1440px){.post.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); + changeCss('@media(max-width: 1800px){.inner__wrap.move--left .post.post--comment.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); + changeCss('.post:hover, .sidebar--right .sidebar--right__header', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); + changeCss('.date-separator.hovered--before:after, .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); + changeCss('.date-separator.hovered--after:before, .new-separator.hovered--after:before', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); + changeCss('.post.current--user:hover .post-body ', 'background: none;', 1); + changeCss('.sidebar--right', 'color:' + theme.centerChannelColor, 2); + } + + if (theme.linkColor) { + changeCss('a, a:focus, a:hover', 'color:' + theme.linkColor, 1); + changeCss('.post .comment-icon__container', 'fill:' + theme.linkColor, 1); + } + + if (theme.buttonBg) { + changeCss('.btn.btn-primary', 'background:' + theme.buttonBg, 1); + changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background:' + changeColor(theme.buttonBg, -0.25), 1); + } + + if (theme.buttonColor) { + changeCss('.btn.btn-primary', 'color:' + theme.buttonColor, 2); + } +} + +export function changeCss(className, classValue, classRepeat) { // we need invisible container to store additional css definitions var cssMainContainer = $('#css-modifier-container'); if (cssMainContainer.length === 0) { @@ -552,9 +664,9 @@ export function changeCss(className, classValue) { } // and we need one div for each class - var classContainer = cssMainContainer.find('div[data-class="' + className + '"]'); + var classContainer = cssMainContainer.find('div[data-class="' + className + classRepeat + '"]'); if (classContainer.length === 0) { - classContainer = $('<div data-class="' + className + '"></div>'); + classContainer = $('<div data-class="' + className + classRepeat + '"></div>'); classContainer.appendTo(cssMainContainer); } @@ -760,57 +872,47 @@ Image.prototype.load = function imageLoad(url, progressCallback) { Image.prototype.completedPercentage = 0; export function changeColor(colourIn, amt) { - var usePound = false; - var col = colourIn; + var hex = colourIn; + var lum = amt; - if (col[0] === '#') { - col = col.slice(1); - usePound = true; + // validate hex string + hex = String(hex).replace(/[^0-9a-f]/gi, ''); + if (hex.length < 6) { + hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } + lum = lum || 0; - var num = parseInt(col, 16); - - var r = (num >> 16) + amt; - - if (r > 255) { - r = 255; - } else if (r < 0) { - r = 0; - } - - var b = ((num >> 8) & 0x00FF) + amt; - - if (b > 255) { - b = 255; - } else if (b < 0) { - b = 0; + // convert to decimal and change luminosity + var rgb = '#'; + var c; + var i; + for (i = 0; i < 3; i++) { + c = parseInt(hex.substr(i * 2, 2), 16); + c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); + rgb += ('00' + c).substr(c.length); } - var g = (num & 0x0000FF) + amt; - - if (g > 255) { - g = 255; - } else if (g < 0) { - g = 0; - } + return rgb; +} - var pound = '#'; - if (!usePound) { - pound = ''; +export function changeOpacity(oldColor, opacity) { + var color = oldColor; + if (color[0] === '#') { + color = color.slice(1); } - return pound + String('000000' + (g | (b << 8) | (r << 16)).toString(16)).slice(-6); -} + if (color.length === 3) { + const tempColor = color; + color = ''; -export function changeOpacity(oldColor, opacity) { - var col = oldColor; - if (col[0] === '#') { - col = col.slice(1); + color += tempColor[0] + tempColor[0]; + color += tempColor[1] + tempColor[1]; + color += tempColor[2] + tempColor[2]; } - var r = parseInt(col.substring(0, 2), 16); - var g = parseInt(col.substring(2, 4), 16); - var b = parseInt(col.substring(4, 6), 16); + var r = parseInt(color.substring(0, 2), 16); + var g = parseInt(color.substring(2, 4), 16); + var b = parseInt(color.substring(4, 6), 16); return 'rgba(' + r + ',' + g + ',' + b + ',' + opacity + ')'; } diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss index cdc2152e9..592d5e62e 100644 --- a/web/sass-files/sass/partials/_base.scss +++ b/web/sass-files/sass/partials/_base.scss @@ -40,14 +40,11 @@ b, strong { a { word-break: break-word; -} - -a.theme { color: $primary-color; } -div.theme { - background-color: $primary-color; +a:focus, a:hover { + color: $primary-color; } .tooltip { diff --git a/web/sass-files/sass/partials/_colorpicker.scss b/web/sass-files/sass/partials/_colorpicker.scss new file mode 100644 index 000000000..431f9d8d0 --- /dev/null +++ b/web/sass-files/sass/partials/_colorpicker.scss @@ -0,0 +1,251 @@ +/*! + * Bootstrap Colorpicker + * http://mjolnic.github.io/bootstrap-colorpicker/ + * + * Originally written by (c) 2012 Stefan Petre + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + * + */ + +.colorpicker-saturation { + float: left; + width: 100px; + height: 100px; + cursor: crosshair; + background-image: url("../images/bootstrap-colorpicker/saturation.png"); +} + +.colorpicker-saturation i { + position: absolute; + top: 0; + left: 0; + display: block; + width: 5px; + height: 5px; + margin: -4px 0 0 -4px; + border: 1px solid #000; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.colorpicker-saturation i b { + display: block; + width: 5px; + height: 5px; + border: 1px solid #fff; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.colorpicker-hue, +.colorpicker-alpha { + float: left; + width: 15px; + height: 100px; + margin-bottom: 4px; + margin-left: 4px; + cursor: row-resize; +} + +.colorpicker-hue i, +.colorpicker-alpha i { + position: absolute; + top: 0; + left: 0; + display: block; + width: 100%; + height: 1px; + margin-top: -1px; + background: #000; + border-top: 1px solid #fff; +} + +.colorpicker-hue { + background-image: url("../images/bootstrap-colorpicker/hue.png"); +} + +.colorpicker-alpha { + display: none; + background-image: url("../images/bootstrap-colorpicker/alpha.png"); +} + +.colorpicker-saturation, +.colorpicker-hue, +.colorpicker-alpha { + background-size: contain; +} + +.colorpicker { + top: 0; + left: 0; + z-index: 2500; + min-width: 130px; + padding: 4px; + margin-top: 1px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *zoom: 1; +} + +.colorpicker:before, +.colorpicker:after { + display: table; + line-height: 0; + content: ""; +} + +.colorpicker:after { + clear: both; +} + +.colorpicker:before { + position: absolute; + top: -7px; + left: 6px; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.colorpicker:after { + position: absolute; + top: -6px; + left: 7px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + border-left: 6px solid transparent; + content: ''; +} + +.colorpicker div { + position: relative; +} + +.colorpicker.colorpicker-with-alpha { + min-width: 140px; +} + +.colorpicker.colorpicker-with-alpha .colorpicker-alpha { + display: block; +} + +.colorpicker-color { + height: 10px; + margin-top: 5px; + clear: both; + background-image: url("../images/bootstrap-colorpicker/alpha.png"); + background-position: 0 100%; +} + +.colorpicker-color div { + height: 10px; +} + +.colorpicker-selectors { + display: none; + height: 10px; + margin-top: 5px; + clear: both; +} + +.colorpicker-selectors i { + float: left; + width: 10px; + height: 10px; + cursor: pointer; +} + +.colorpicker-selectors i + i { + margin-left: 3px; +} + +.colorpicker-element .input-group-addon i, +.colorpicker-element .add-on i { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: text-top; + cursor: pointer; +} + +.colorpicker.colorpicker-inline { + position: relative; + z-index: auto; + display: inline-block; + float: none; +} + +.colorpicker.colorpicker-horizontal { + width: 110px; + height: auto; + min-width: 110px; +} + +.colorpicker.colorpicker-horizontal .colorpicker-saturation { + margin-bottom: 4px; +} + +.colorpicker.colorpicker-horizontal .colorpicker-color { + width: 100px; +} + +.colorpicker.colorpicker-horizontal .colorpicker-hue, +.colorpicker.colorpicker-horizontal .colorpicker-alpha { + float: left; + width: 100px; + height: 15px; + margin-bottom: 4px; + margin-left: 0; + cursor: col-resize; +} + +.colorpicker.colorpicker-horizontal .colorpicker-hue i, +.colorpicker.colorpicker-horizontal .colorpicker-alpha i { + position: absolute; + top: 0; + left: 0; + display: block; + width: 1px; + height: 15px; + margin-top: 0; + background: #ffffff; + border: none; +} + +.colorpicker.colorpicker-horizontal .colorpicker-hue { + background-image: url("../images/bootstrap-colorpicker/hue-horizontal.png"); +} + +.colorpicker.colorpicker-horizontal .colorpicker-alpha { + background-image: url("../images/bootstrap-colorpicker/alpha-horizontal.png"); +} + +.colorpicker.colorpicker-hidden { + display: none; +} + +.colorpicker.colorpicker-visible { + display: block; +} + +.colorpicker-inline.colorpicker-visible { + display: inline-block; +} + +.colorpicker-right:before { + right: 6px; + left: auto; +} + +.colorpicker-right:after { + right: 7px; + left: auto; +}
\ No newline at end of file diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss index 702f0fd60..a20d1f48b 100644 --- a/web/sass-files/sass/partials/_headers.scss +++ b/web/sass-files/sass/partials/_headers.scss @@ -231,6 +231,10 @@ width: 45px; color: #999; cursor: pointer; + .fa { + margin-left: 3px; + font-size: 16px; + } } &.alt { margin: 0; diff --git a/web/sass-files/sass/partials/_modal.scss b/web/sass-files/sass/partials/_modal.scss index a046cd904..38e9b4174 100644 --- a/web/sass-files/sass/partials/_modal.scss +++ b/web/sass-files/sass/partials/_modal.scss @@ -8,6 +8,16 @@ @include opacity(0.7); } } + a, a:focus, a:hover { + color: #2389D7; + } + .btn.btn-primary { + background: #4285f4; + &:hover, &:focus, &:active { + background: $primary-color--hover; + color: #fff; + } + } .info__label { font-weight: 600; text-align: right; diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index 6940cf2fb..d4f02cf4b 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -275,7 +275,7 @@ body.ios { &.current--user { .post-body { @include border-radius(4px); - background: #f5f5f5; + background: rgba(#000, 0.05); } } &.post--comment { diff --git a/web/sass-files/sass/partials/_search.scss b/web/sass-files/sass/partials/_search.scss index 9ae41ebb0..9abdd40da 100644 --- a/web/sass-files/sass/partials/_search.scss +++ b/web/sass-files/sass/partials/_search.scss @@ -107,4 +107,5 @@ .search-highlight.theme, .search-highlight { background-color: #FFF2BB; + color: #333; } diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss index 2b59a943b..8dcd8f35c 100644 --- a/web/sass-files/sass/partials/_settings.scss +++ b/web/sass-files/sass/partials/_settings.scss @@ -79,6 +79,36 @@ } } + .appearance-section { + .premade-themes { + .theme-label { + font-weight: 400; + margin-top: 5px; + } + img { + border: 3px solid transparent; + } + .active { + img { + border-color: $primary-color; + } + } + } + .custom-label { + font-weight: normal; + font-size: 13px; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 0; + } + .radio { + label { + font-weight: 600; + } + } + } + .section-title { margin-bottom: 5px; font-weight: 600; diff --git a/web/sass-files/sass/partials/_sidebar--left.scss b/web/sass-files/sass/partials/_sidebar--left.scss index 94583b153..6a418e270 100644 --- a/web/sass-files/sass/partials/_sidebar--left.scss +++ b/web/sass-files/sass/partials/_sidebar--left.scss @@ -16,6 +16,12 @@ overflow-y: auto; max-width: 200px; width: 200px; + a { + color: #262626 !important; + &:hover, &:focus { + background: #f5f5f5 !important; + } + } } .search__form { margin: 0; diff --git a/web/sass-files/sass/partials/_sidebar--right.scss b/web/sass-files/sass/partials/_sidebar--right.scss index d02a92448..b37dbf421 100644 --- a/web/sass-files/sass/partials/_sidebar--right.scss +++ b/web/sass-files/sass/partials/_sidebar--right.scss @@ -51,9 +51,10 @@ margin: 11px 0 0 0; width: 22px; height: 22px; - background: url("../images/closeSidebar.png"); - @include background-size(100% 100%); opacity: 0.5; + font-size: 22px; + line-height: 0; + background: none; float: right; outline: none; border: none; @@ -61,11 +62,15 @@ &:hover, &:active { opacity: 0.8; } + i { + position: relative; + top: -2px; + } } .sidebar--right__header { font-size: 1em; text-transform: uppercase; - color: #444; + color: inherit; height: 44px; padding: 0 1em; line-height: 44px; diff --git a/web/sass-files/sass/styles.scss b/web/sass-files/sass/styles.scss index de1db57e8..923a6e99b 100644 --- a/web/sass-files/sass/styles.scss +++ b/web/sass-files/sass/styles.scss @@ -10,6 +10,7 @@ @import "partials/perfect-scrollbar"; @import "partials/font-awesome"; @import "partials/base"; +@import "partials/colorpicker"; // Channel Css @import "partials/headers"; diff --git a/web/static/css/bootstrap-colorpicker.min.css b/web/static/css/bootstrap-colorpicker.min.css new file mode 100755 index 000000000..5656e768f --- /dev/null +++ b/web/static/css/bootstrap-colorpicker.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap Colorpicker + * http://mjolnic.github.io/bootstrap-colorpicker/ + * + * Originally written by (c) 2012 Stefan Petre + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + * + */.colorpicker-saturation{float:left;width:100px;height:100px;cursor:crosshair;background-image:url("../images/bootstrap-colorpicker/saturation.png")}.colorpicker-saturation i{position:absolute;top:0;left:0;display:block;width:5px;height:5px;margin:-4px 0 0 -4px;border:1px solid #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-saturation i b{display:block;width:5px;height:5px;border:1px solid #fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-hue,.colorpicker-alpha{float:left;width:15px;height:100px;margin-bottom:4px;margin-left:4px;cursor:row-resize}.colorpicker-hue i,.colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:100%;height:1px;margin-top:-1px;background:#000;border-top:1px solid #fff}.colorpicker-hue{background-image:url("../images/bootstrap-colorpicker/hue.png")}.colorpicker-alpha{display:none;background-image:url("../images/bootstrap-colorpicker/alpha.png")}.colorpicker-saturation,.colorpicker-hue,.colorpicker-alpha{background-size:contain}.colorpicker{top:0;left:0;z-index:2500;min-width:130px;padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1}.colorpicker:before,.colorpicker:after{display:table;line-height:0;content:""}.colorpicker:after{clear:both}.colorpicker:before{position:absolute;top:-7px;left:6px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.colorpicker:after{position:absolute;top:-6px;left:7px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.colorpicker div{position:relative}.colorpicker.colorpicker-with-alpha{min-width:140px}.colorpicker.colorpicker-with-alpha .colorpicker-alpha{display:block}.colorpicker-color{height:10px;margin-top:5px;clear:both;background-image:url("../images/bootstrap-colorpicker/alpha.png");background-position:0 100%}.colorpicker-color div{height:10px}.colorpicker-selectors{display:none;height:10px;margin-top:5px;clear:both}.colorpicker-selectors i{float:left;width:10px;height:10px;cursor:pointer}.colorpicker-selectors i+i{margin-left:3px}.colorpicker-element .input-group-addon i,.colorpicker-element .add-on i{display:inline-block;width:16px;height:16px;vertical-align:text-top;cursor:pointer}.colorpicker.colorpicker-inline{position:relative;z-index:auto;display:inline-block;float:none}.colorpicker.colorpicker-horizontal{width:110px;height:auto;min-width:110px}.colorpicker.colorpicker-horizontal .colorpicker-saturation{margin-bottom:4px}.colorpicker.colorpicker-horizontal .colorpicker-color{width:100px}.colorpicker.colorpicker-horizontal .colorpicker-hue,.colorpicker.colorpicker-horizontal .colorpicker-alpha{float:left;width:100px;height:15px;margin-bottom:4px;margin-left:0;cursor:col-resize}.colorpicker.colorpicker-horizontal .colorpicker-hue i,.colorpicker.colorpicker-horizontal .colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:1px;height:15px;margin-top:0;background:#fff;border:0}.colorpicker.colorpicker-horizontal .colorpicker-hue{background-image:url("../images/bootstrap-colorpicker/hue-horizontal.png")}.colorpicker.colorpicker-horizontal .colorpicker-alpha{background-image:url("../images/bootstrap-colorpicker/alpha-horizontal.png")}.colorpicker.colorpicker-hidden{display:none}.colorpicker.colorpicker-visible{display:block}.colorpicker-inline.colorpicker-visible{display:inline-block}.colorpicker-right:before{right:6px;left:auto}.colorpicker-right:after{right:7px;left:auto} diff --git a/web/static/images/bootstrap-colorpicker/alpha-horizontal.png b/web/static/images/bootstrap-colorpicker/alpha-horizontal.png Binary files differnew file mode 100755 index 000000000..d0a65c08b --- /dev/null +++ b/web/static/images/bootstrap-colorpicker/alpha-horizontal.png diff --git a/web/static/images/bootstrap-colorpicker/alpha.png b/web/static/images/bootstrap-colorpicker/alpha.png Binary files differnew file mode 100755 index 000000000..38043f1c8 --- /dev/null +++ b/web/static/images/bootstrap-colorpicker/alpha.png diff --git a/web/static/images/bootstrap-colorpicker/hue-horizontal.png b/web/static/images/bootstrap-colorpicker/hue-horizontal.png Binary files differnew file mode 100755 index 000000000..a0d9add8e --- /dev/null +++ b/web/static/images/bootstrap-colorpicker/hue-horizontal.png diff --git a/web/static/images/bootstrap-colorpicker/hue.png b/web/static/images/bootstrap-colorpicker/hue.png Binary files differnew file mode 100755 index 000000000..d89560e99 --- /dev/null +++ b/web/static/images/bootstrap-colorpicker/hue.png diff --git a/web/static/images/bootstrap-colorpicker/saturation.png b/web/static/images/bootstrap-colorpicker/saturation.png Binary files differnew file mode 100755 index 000000000..594ae50ed --- /dev/null +++ b/web/static/images/bootstrap-colorpicker/saturation.png diff --git a/web/static/images/themes/dark.png b/web/static/images/themes/dark.png Binary files differnew file mode 100644 index 000000000..832f64d2e --- /dev/null +++ b/web/static/images/themes/dark.png diff --git a/web/static/images/themes/mattermost.png b/web/static/images/themes/mattermost.png Binary files differnew file mode 100644 index 000000000..4a321adcb --- /dev/null +++ b/web/static/images/themes/mattermost.png diff --git a/web/static/images/themes/slack.png b/web/static/images/themes/slack.png Binary files differnew file mode 100644 index 000000000..dc70c7dc2 --- /dev/null +++ b/web/static/images/themes/slack.png diff --git a/web/static/js/bootstrap-colorpicker.min.js b/web/static/js/bootstrap-colorpicker.min.js new file mode 100755 index 000000000..05c0e0744 --- /dev/null +++ b/web/static/js/bootstrap-colorpicker.min.js @@ -0,0 +1 @@ +!function(a){"use strict";"object"==typeof exports?module.exports=a(window.jQuery):"function"==typeof define&&define.amd?define(["jquery"],a):window.jQuery&&!window.jQuery.fn.colorpicker&&a(window.jQuery)}(function(a){"use strict";var b=function(b,c){this.value={h:0,s:0,b:0,a:1},this.origFormat=null,c&&a.extend(this.colors,c),b&&(void 0!==b.toLowerCase?(b+="",this.setColor(b)):void 0!==b.h&&(this.value=b))};b.prototype={constructor:b,colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",transparent:"transparent"},_sanitizeNumber:function(a){return"number"==typeof a?a:isNaN(a)||null===a||""===a||void 0===a?1:void 0!==a.toLowerCase?parseFloat(a):1},isTransparent:function(a){return a?(a=a.toLowerCase().trim(),"transparent"===a||a.match(/#?00000000/)||a.match(/(rgba|hsla)\(0,0,0,0?\.?0\)/)):!1},rgbaIsTransparent:function(a){return 0===a.r&&0===a.g&&0===a.b&&0===a.a},setColor:function(a){a=a.toLowerCase().trim(),a&&(this.isTransparent(a)?this.value={h:0,s:0,b:0,a:0}:this.value=this.stringToHSB(a)||{h:0,s:0,b:0,a:1})},stringToHSB:function(b){b=b.toLowerCase();var c;"undefined"!=typeof this.colors[b]&&(b=this.colors[b],c="alias");var d=this,e=!1;return a.each(this.stringParsers,function(a,f){var g=f.re.exec(b),h=g&&f.parse.apply(d,[g]),i=c||f.format||"rgba";return h?(e=i.match(/hsla?/)?d.RGBtoHSB.apply(d,d.HSLtoRGB.apply(d,h)):d.RGBtoHSB.apply(d,h),d.origFormat=i,!1):!0}),e},setHue:function(a){this.value.h=1-a},setSaturation:function(a){this.value.s=a},setBrightness:function(a){this.value.b=1-a},setAlpha:function(a){this.value.a=parseInt(100*(1-a),10)/100},toRGB:function(a,b,c,d){a||(a=this.value.h,b=this.value.s,c=this.value.b),a*=360;var e,f,g,h,i;return a=a%360/60,i=c*b,h=i*(1-Math.abs(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],{r:Math.round(255*e),g:Math.round(255*f),b:Math.round(255*g),a:d||this.value.a}},toHex:function(a,b,c,d){var e=this.toRGB(a,b,c,d);return this.rgbaIsTransparent(e)?"transparent":"#"+(1<<24|parseInt(e.r)<<16|parseInt(e.g)<<8|parseInt(e.b)).toString(16).substr(1)},toHSL:function(a,b,c,d){a=a||this.value.h,b=b||this.value.s,c=c||this.value.b,d=d||this.value.a;var e=a,f=(2-b)*c,g=b*c;return g/=f>0&&1>=f?f:2-f,f/=2,g>1&&(g=1),{h:isNaN(e)?0:e,s:isNaN(g)?0:g,l:isNaN(f)?0:f,a:isNaN(d)?0:d}},toAlias:function(a,b,c,d){var e=this.toHex(a,b,c,d);for(var f in this.colors)if(this.colors[f]===e)return f;return!1},RGBtoHSB:function(a,b,c,d){a/=255,b/=255,c/=255;var e,f,g,h;return g=Math.max(a,b,c),h=g-Math.min(a,b,c),e=0===h?null:g===a?(b-c)/h:g===b?(c-a)/h+2:(a-b)/h+4,e=(e+360)%6*60/360,f=0===h?0:h/g,{h:this._sanitizeNumber(e),s:f,b:g,a:this._sanitizeNumber(d)}},HueToRGB:function(a,b,c){return 0>c?c+=1:c>1&&(c-=1),1>6*c?a+(b-a)*c*6:1>2*c?b:2>3*c?a+(b-a)*(2/3-c)*6:a},HSLtoRGB:function(a,b,c,d){0>b&&(b=0);var e;e=.5>=c?c*(1+b):c+b-c*b;var f=2*c-e,g=a+1/3,h=a,i=a-1/3,j=Math.round(255*this.HueToRGB(f,e,g)),k=Math.round(255*this.HueToRGB(f,e,h)),l=Math.round(255*this.HueToRGB(f,e,i));return[j,k,l,this._sanitizeNumber(d)]},toString:function(a){a=a||"rgba";var b=!1;switch(a){case"rgb":return b=this.toRGB(),this.rgbaIsTransparent(b)?"transparent":"rgb("+b.r+","+b.g+","+b.b+")";case"rgba":return b=this.toRGB(),"rgba("+b.r+","+b.g+","+b.b+","+b.a+")";case"hsl":return b=this.toHSL(),"hsl("+Math.round(360*b.h)+","+Math.round(100*b.s)+"%,"+Math.round(100*b.l)+"%)";case"hsla":return b=this.toHSL(),"hsla("+Math.round(360*b.h)+","+Math.round(100*b.s)+"%,"+Math.round(100*b.l)+"%,"+b.a+")";case"hex":return this.toHex();case"alias":return this.toAlias()||this.toHex();default:return b}},stringParsers:[{re:/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/,format:"rgb",parse:function(a){return[a[1],a[2],a[3],1]}},{re:/rgb\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,format:"rgb",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],1]}},{re:/rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[a[1],a[2],a[3],a[4]]}},{re:/rgba\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],a[4]]}},{re:/hsl\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,format:"hsl",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/hsla\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"hsla",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/#?([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,format:"hex",parse:function(a){return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16),1]}},{re:/#?([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,format:"hex",parse:function(a){return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16),1]}}],colorNameToHex:function(a){return"undefined"!=typeof this.colors[a.toLowerCase()]?this.colors[a.toLowerCase()]:!1}};var c={horizontal:!1,inline:!1,color:!1,format:!1,input:"input",container:!1,component:".add-on, .input-group-addon",sliders:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setHue"},alpha:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setAlpha"}},slidersHorz:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:100,maxTop:0,callLeft:"setHue",callTop:!1},alpha:{maxLeft:100,maxTop:0,callLeft:"setAlpha",callTop:!1}},template:'<div class="colorpicker dropdown-menu"><div class="colorpicker-saturation"><i><b></b></i></div><div class="colorpicker-hue"><i></i></div><div class="colorpicker-alpha"><i></i></div><div class="colorpicker-color"><div /></div><div class="colorpicker-selectors"></div></div>',align:"right",customClass:null,colorSelectors:null},d=function(d,e){if(this.element=a(d).addClass("colorpicker-element"),this.options=a.extend(!0,{},c,this.element.data(),e),this.component=this.options.component,this.component=this.component!==!1?this.element.find(this.component):!1,this.component&&0===this.component.length&&(this.component=!1),this.container=this.options.container===!0?this.element:this.options.container,this.container=this.container!==!1?a(this.container):!1,this.input=this.element.is("input")?this.element:this.options.input?this.element.find(this.options.input):!1,this.input&&0===this.input.length&&(this.input=!1),this.color=new b(this.options.color!==!1?this.options.color:this.getValue(),this.options.colorSelectors),this.format=this.options.format!==!1?this.options.format:this.color.origFormat,this.picker=a(this.options.template),this.options.customClass&&this.picker.addClass(this.options.customClass),this.options.inline?this.picker.addClass("colorpicker-inline colorpicker-visible"):this.picker.addClass("colorpicker-hidden"),this.options.horizontal&&this.picker.addClass("colorpicker-horizontal"),("rgba"===this.format||"hsla"===this.format||this.options.format===!1)&&this.picker.addClass("colorpicker-with-alpha"),"right"===this.options.align&&this.picker.addClass("colorpicker-right"),this.options.colorSelectors){var f=this;a.each(this.options.colorSelectors,function(b,c){var d=a("<i />").css("background-color",c).data("class",b);d.click(function(){f.setValue(a(this).css("background-color"))}),f.picker.find(".colorpicker-selectors").append(d)}),this.picker.find(".colorpicker-selectors").show()}this.picker.on("mousedown.colorpicker touchstart.colorpicker",a.proxy(this.mousedown,this)),this.picker.appendTo(this.container?this.container:a("body")),this.input!==!1&&(this.input.on({"keyup.colorpicker":a.proxy(this.keyup,this)}),this.input.on({"change.colorpicker":a.proxy(this.change,this)}),this.component===!1&&this.element.on({"focus.colorpicker":a.proxy(this.show,this)}),this.options.inline===!1&&this.element.on({"focusout.colorpicker":a.proxy(this.hide,this)})),this.component!==!1&&this.component.on({"click.colorpicker":a.proxy(this.show,this)}),this.input===!1&&this.component===!1&&this.element.on({"click.colorpicker":a.proxy(this.show,this)}),this.input!==!1&&this.component!==!1&&"color"===this.input.attr("type")&&this.input.on({"click.colorpicker":a.proxy(this.show,this),"focus.colorpicker":a.proxy(this.show,this)}),this.update(),a(a.proxy(function(){this.element.trigger("create")},this))};d.Color=b,d.prototype={constructor:d,destroy:function(){this.picker.remove(),this.element.removeData("colorpicker").off(".colorpicker"),this.input!==!1&&this.input.off(".colorpicker"),this.component!==!1&&this.component.off(".colorpicker"),this.element.removeClass("colorpicker-element"),this.element.trigger({type:"destroy"})},reposition:function(){if(this.options.inline!==!1||this.options.container)return!1;var a=this.container&&this.container[0]!==document.body?"position":"offset",b=this.component||this.element,c=b[a]();"right"===this.options.align&&(c.left-=this.picker.outerWidth()-b.outerWidth()),this.picker.css({top:c.top+b.outerHeight(),left:c.left})},show:function(b){return this.isDisabled()?!1:(this.picker.addClass("colorpicker-visible").removeClass("colorpicker-hidden"),this.reposition(),a(window).on("resize.colorpicker",a.proxy(this.reposition,this)),!b||this.hasInput()&&"color"!==this.input.attr("type")||b.stopPropagation&&b.preventDefault&&(b.stopPropagation(),b.preventDefault()),this.options.inline===!1&&a(window.document).on({"mousedown.colorpicker":a.proxy(this.hide,this)}),void this.element.trigger({type:"showPicker",color:this.color}))},hide:function(){this.picker.addClass("colorpicker-hidden").removeClass("colorpicker-visible"),a(window).off("resize.colorpicker",this.reposition),a(document).off({"mousedown.colorpicker":this.hide}),this.update(),this.element.trigger({type:"hidePicker",color:this.color})},updateData:function(a){return a=a||this.color.toString(this.format),this.element.data("color",a),a},updateInput:function(a){if(a=a||this.color.toString(this.format),this.input!==!1){if(this.options.colorSelectors){var c=new b(a,this.options.colorSelectors),d=c.toAlias();"undefined"!=typeof this.options.colorSelectors[d]&&(a=d)}this.input.prop("value",a)}return a},updatePicker:function(a){void 0!==a&&(this.color=new b(a,this.options.colorSelectors));var c=this.options.horizontal===!1?this.options.sliders:this.options.slidersHorz,d=this.picker.find("i");return 0!==d.length?(this.options.horizontal===!1?(c=this.options.sliders,d.eq(1).css("top",c.hue.maxTop*(1-this.color.value.h)).end().eq(2).css("top",c.alpha.maxTop*(1-this.color.value.a))):(c=this.options.slidersHorz,d.eq(1).css("left",c.hue.maxLeft*(1-this.color.value.h)).end().eq(2).css("left",c.alpha.maxLeft*(1-this.color.value.a))),d.eq(0).css({top:c.saturation.maxTop-this.color.value.b*c.saturation.maxTop,left:this.color.value.s*c.saturation.maxLeft}),this.picker.find(".colorpicker-saturation").css("backgroundColor",this.color.toHex(this.color.value.h,1,1,1)),this.picker.find(".colorpicker-alpha").css("backgroundColor",this.color.toHex()),this.picker.find(".colorpicker-color, .colorpicker-color div").css("backgroundColor",this.color.toString(this.format)),a):void 0},updateComponent:function(a){if(a=a||this.color.toString(this.format),this.component!==!1){var b=this.component.find("i").eq(0);b.length>0?b.css({backgroundColor:a}):this.component.css({backgroundColor:a})}return a},update:function(a){var b;return(this.getValue(!1)!==!1||a===!0)&&(b=this.updateComponent(),this.updateInput(b),this.updateData(b),this.updatePicker()),b},setValue:function(a){this.color=new b(a,this.options.colorSelectors),this.update(!0),this.element.trigger({type:"changeColor",color:this.color,value:a})},getValue:function(a){a=void 0===a?"#000000":a;var b;return b=this.hasInput()?this.input.val():this.element.data("color"),(void 0===b||""===b||null===b)&&(b=a),b},hasInput:function(){return this.input!==!1},isDisabled:function(){return this.hasInput()?this.input.prop("disabled")===!0:!1},disable:function(){return this.hasInput()?(this.input.prop("disabled",!0),this.element.trigger({type:"disable",color:this.color,value:this.getValue()}),!0):!1},enable:function(){return this.hasInput()?(this.input.prop("disabled",!1),this.element.trigger({type:"enable",color:this.color,value:this.getValue()}),!0):!1},currentSlider:null,mousePointer:{left:0,top:0},mousedown:function(b){b.pageX||b.pageY||!b.originalEvent||(b.pageX=b.originalEvent.touches[0].pageX,b.pageY=b.originalEvent.touches[0].pageY),b.stopPropagation(),b.preventDefault();var c=a(b.target),d=c.closest("div"),e=this.options.horizontal?this.options.slidersHorz:this.options.sliders;if(!d.is(".colorpicker")){if(d.is(".colorpicker-saturation"))this.currentSlider=a.extend({},e.saturation);else if(d.is(".colorpicker-hue"))this.currentSlider=a.extend({},e.hue);else{if(!d.is(".colorpicker-alpha"))return!1;this.currentSlider=a.extend({},e.alpha)}var f=d.offset();this.currentSlider.guide=d.find("i")[0].style,this.currentSlider.left=b.pageX-f.left,this.currentSlider.top=b.pageY-f.top,this.mousePointer={left:b.pageX,top:b.pageY},a(document).on({"mousemove.colorpicker":a.proxy(this.mousemove,this),"touchmove.colorpicker":a.proxy(this.mousemove,this),"mouseup.colorpicker":a.proxy(this.mouseup,this),"touchend.colorpicker":a.proxy(this.mouseup,this)}).trigger("mousemove")}return!1},mousemove:function(a){a.pageX||a.pageY||!a.originalEvent||(a.pageX=a.originalEvent.touches[0].pageX,a.pageY=a.originalEvent.touches[0].pageY),a.stopPropagation(),a.preventDefault();var b=Math.max(0,Math.min(this.currentSlider.maxLeft,this.currentSlider.left+((a.pageX||this.mousePointer.left)-this.mousePointer.left))),c=Math.max(0,Math.min(this.currentSlider.maxTop,this.currentSlider.top+((a.pageY||this.mousePointer.top)-this.mousePointer.top)));return this.currentSlider.guide.left=b+"px",this.currentSlider.guide.top=c+"px",this.currentSlider.callLeft&&this.color[this.currentSlider.callLeft].call(this.color,b/this.currentSlider.maxLeft),this.currentSlider.callTop&&this.color[this.currentSlider.callTop].call(this.color,c/this.currentSlider.maxTop),"setAlpha"===this.currentSlider.callTop&&this.options.format===!1&&(1!==this.color.value.a?(this.format="rgba",this.color.origFormat="rgba"):(this.format="hex",this.color.origFormat="hex")),this.update(!0),this.element.trigger({type:"changeColor",color:this.color}),!1},mouseup:function(b){return b.stopPropagation(),b.preventDefault(),a(document).off({"mousemove.colorpicker":this.mousemove,"touchmove.colorpicker":this.mousemove,"mouseup.colorpicker":this.mouseup,"touchend.colorpicker":this.mouseup}),!1},change:function(a){this.keyup(a)},keyup:function(a){38===a.keyCode?(this.color.value.a<1&&(this.color.value.a=Math.round(100*(this.color.value.a+.01))/100),this.update(!0)):40===a.keyCode?(this.color.value.a>0&&(this.color.value.a=Math.round(100*(this.color.value.a-.01))/100),this.update(!0)):(this.color=new b(this.input.val(),this.options.colorSelectors),this.color.origFormat&&this.options.format===!1&&(this.format=this.color.origFormat),this.getValue(!1)!==!1&&(this.updateData(),this.updateComponent(),this.updatePicker())),this.element.trigger({type:"changeColor",color:this.color,value:this.input.val()})}},a.colorpicker=d,a.fn.colorpicker=function(b){var c,e=arguments,f=this.each(function(){var f=a(this),g=f.data("colorpicker"),h="object"==typeof b?b:{};g||"string"==typeof b?"string"==typeof b&&(c=g[b].apply(g,Array.prototype.slice.call(e,1))):f.data("colorpicker",new d(this,h))});return"getValue"===b?c:f},a.fn.colorpicker.constructor=d});
\ No newline at end of file diff --git a/web/templates/channel.html b/web/templates/channel.html index 92aaaf02f..2af94e415 100644 --- a/web/templates/channel.html +++ b/web/templates/channel.html @@ -29,6 +29,7 @@ <div id="edit_mention_tab"></div> <div id="get_link_modal"></div> <div id="user_settings_modal"></div> + <div id="import_theme_modal"></div> <div id="team_settings_modal"></div> <div id="invite_member_modal"></div> <div id="edit_channel_modal"></div> diff --git a/web/templates/head.html b/web/templates/head.html index af5c86bba..36af56a21 100644 --- a/web/templates/head.html +++ b/web/templates/head.html @@ -25,10 +25,12 @@ <link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css"> <link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css" rel="stylesheet"> + <link rel="stylesheet" href="/static/css/bootstrap-colorpicker.min.css" rel="stylesheet"> <script src="/static/js/react-with-addons-0.13.3.js"></script> <script src="/static/js/jquery-1.11.1.js"></script> <script src="/static/js/bootstrap-3.3.5.js"></script> + <script src="/static/js/bootstrap-colorpicker.min.js"></script> <script src="/static/js/react-bootstrap-0.25.1.js"></script> <link id="favicon" rel="icon" href="/static/images/favicon.ico" type="image/x-icon"> |