diff --git a/res/css/_components.scss b/res/css/_components.scss index b26998b9d3..d70e1700d3 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -79,6 +79,7 @@ @import "./views/elements/_RoleButton.scss"; @import "./views/elements/_Spinner.scss"; @import "./views/elements/_SyntaxHighlight.scss"; +@import "./views/elements/_ToggleSwitch.scss"; @import "./views/elements/_ToolTipButton.scss"; @import "./views/globals/_MatrixToolbar.scss"; @import "./views/groups/_GroupPublicityToggle.scss"; diff --git a/res/css/structures/_UserSettings.scss b/res/css/structures/_UserSettings.scss index 74d8c2c718..b078a4e242 100644 --- a/res/css/structures/_UserSettings.scss +++ b/res/css/structures/_UserSettings.scss @@ -255,3 +255,15 @@ input.mx_UserSettings_phoneNumberField { .mx_UserSettings_analyticsModal table { margin: 10px 0px; } + + +// Temp styles to keep the layout moderately usable. Not perfect, but better +// than 30 options being impossible to understand. +.mx_UserSettings .mx_SettingsFlag { + height: 30px; +} + +.mx_UserSettings .mx_SettingsFlag .mx_ToggleSwitch { + float: left; + margin-right: 5px; +} \ No newline at end of file diff --git a/res/css/views/elements/_ToggleSwitch.scss b/res/css/views/elements/_ToggleSwitch.scss new file mode 100644 index 0000000000..0955648d50 --- /dev/null +++ b/res/css/views/elements/_ToggleSwitch.scss @@ -0,0 +1,51 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// TODO: Fancy transitions + +.mx_ToggleSwitch { + width: 48px; + height: 24px; + border-radius: 14px; + background-color: $togglesw-off-color; + position: relative; +} + +.mx_ToggleSwitch_enabled { + cursor: pointer; +} + +.mx_ToggleSwitch.mx_ToggleSwitch_on { + background-color: $togglesw-on-color; +} + +.mx_ToggleSwitch_ball { + margin: 2px; + width: 20px; + height: 20px; + border-radius: 20px; + background-color: $togglesw-ball-color; + position: absolute; + top: 0; +} + +.mx_ToggleSwitch:not(.mx_ToggleSwitch_on) > .mx_ToggleSwitch_ball { + left: 2px; +} + +.mx_ToggleSwitch_on > .mx_ToggleSwitch_ball { + right: 2px; +} diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index 3ea9d616ba..795cacd1c9 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -36,3 +36,17 @@ limitations under the License. margin: 0; display: block; } + +.mx_SettingsTab_section .mx_SettingsFlag { + margin-right: 100px; + height: 25px; + margin-bottom: 10px; +} + +.mx_SettingsTab_section .mx_SettingsFlag .mx_SettingsFlag_label { + vertical-align: middle; +} + +.mx_SettingsTab_section .mx_SettingsFlag .mx_ToggleSwitch { + float: right; +} diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 03d59cfc9d..476b265699 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -216,6 +216,11 @@ $button-danger-bg-color: #f56679; $button-danger-disabled-fg-color: #ffffff; $button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color +// Toggle switch +$togglesw-off-color: #c1c9d6; +$togglesw-on-color: #7ac9a1; +$togglesw-ball-color: #fff; + // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index d9d9bff2d3..c5029c64b5 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -212,6 +212,11 @@ $button-danger-bg-color: #f56679; $button-danger-disabled-fg-color: #ffffff; $button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color +// Toggle switch +$togglesw-off-color: #c1c9d6; +$togglesw-on-color: #7ac9a1; +$togglesw-ball-color: #fff; + // unused? $progressbar-color: #000; diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index f8d9e2dd84..180d05e77e 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -637,11 +637,14 @@ module.exports = React.createClass({ // to rebind the onChange each time we render const onChange = (e) => SettingsStore.setValue("autocompleteDelay", null, SettingLevel.DEVICE, e.target.value); + // HACK: Lack of translations for themes header. We're removing this view in the very near future, + // and the header is really only there to maintain some semblance of the UX the section once was. return (

{ _t("User Interface") }

{ SIMPLE_SETTINGS.map( this._renderAccountSetting ) } +
Themes
{ THEMES.map( this._renderThemeOption ) } @@ -676,18 +679,12 @@ module.exports = React.createClass({ }, _renderThemeOption: function(setting) { - const SettingsFlag = sdk.getComponent("elements.SettingsFlag"); - const onChange = (v) => dis.dispatch({action: 'set_theme', value: setting.value}); - return ( -
- -
- ); + // HACK: Temporary disablement of theme selection. + // We don't support changing themes on experimental anyways, and radio groups aren't + // a thing anymore for setting flags. We're also dropping this view in the very near + // future, so just replace the theme selection with placeholder text. + const currentTheme = SettingsStore.getValue("theme"); + return
{_t(setting.label)} {currentTheme === setting.value ? '(current)' : null}
; }, _renderCryptoInfo: function() { diff --git a/src/components/views/elements/SettingsFlag.js b/src/components/views/elements/SettingsFlag.js index 7f6c74538a..f1bd72f53d 100644 --- a/src/components/views/elements/SettingsFlag.js +++ b/src/components/views/elements/SettingsFlag.js @@ -18,6 +18,7 @@ import React from "react"; import PropTypes from 'prop-types'; import SettingsStore from "../../../settings/SettingsStore"; import { _t } from '../../../languageHandler'; +import ToggleSwitch from "./ToggleSwitch"; module.exports = React.createClass({ displayName: 'SettingsFlag', @@ -29,10 +30,6 @@ module.exports = React.createClass({ onChange: PropTypes.func, isExplicit: PropTypes.bool, manualSave: PropTypes.bool, - - // If group is supplied, then this will create a radio button instead. - group: PropTypes.string, - value: PropTypes.any, // the value for the radio button }, getInitialState: function() { @@ -46,13 +43,12 @@ module.exports = React.createClass({ }; }, - onChange: function(e) { - if (this.props.group && !e.target.checked) return; + onChange: function(checked) { + if (this.props.group && !checked) return; - const newState = this.props.group ? this.props.value : e.target.checked; - if (!this.props.manualSave) this.save(newState); - else this.setState({ value: newState }); - if (this.props.onChange) this.props.onChange(newState); + if (!this.props.manualSave) this.save(checked); + else this.setState({ value: checked }); + if (this.props.onChange) this.props.onChange(checked); }, save: function(val = undefined) { @@ -78,34 +74,11 @@ module.exports = React.createClass({ if (!label) label = SettingsStore.getDisplayName(this.props.name, this.props.level); else label = _t(label); - // We generate a relatively complex ID to avoid conflicts - const id = this.props.name + "_" + this.props.group + "_" + this.props.value + "_" + this.props.level; - let checkbox = ( - - ); - if (this.props.group) { - checkbox = ( - - ); - } - return ( - +
+ {label} + +
); }, }); diff --git a/src/components/views/elements/ToggleSwitch.js b/src/components/views/elements/ToggleSwitch.js new file mode 100644 index 0000000000..5c3aaeb323 --- /dev/null +++ b/src/components/views/elements/ToggleSwitch.js @@ -0,0 +1,66 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from "classnames"; + +export default class ToggleSwitch extends React.Component { + static propTypes = { + // Whether or not this toggle is in the 'on' position. Default false (off). + checked: PropTypes.bool, + + // Whether or not the user can interact with the switch + disabled: PropTypes.bool, + + // Called when the checked state changes. First argument will be the new state. + onChange: PropTypes.func, + }; + + constructor(props) { + super(props); + + this.state = { + checked: props.checked || false, // default false + }; + } + + _onClick = (e) => { + e.stopPropagation(); + e.preventDefault(); + + if (this.props.disabled) return; + + const newState = !this.state.checked; + this.setState({checked: newState}); + if (this.props.onChange) { + this.props.onChange(newState); + } + }; + + render() { + const classes = classNames({ + "mx_ToggleSwitch": true, + "mx_ToggleSwitch_on": this.state.checked, + "mx_ToggleSwitch_enabled": !this.props.disabled, + }); + return ( +
+
+
+ ) + } +} diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js index 6f4ebed848..fef6abfb17 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -154,6 +154,7 @@ export default class GeneralSettingsTab extends React.Component { _renderThemeSection() { // TODO: Re-enable theme selection once the themes actually work + const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); return (
{_t("Theme")} @@ -164,6 +165,7 @@ export default class GeneralSettingsTab extends React.Component { +
); }