Improve the profile section a bit and add a highlight to the temp tab
parent
c3692aa9ae
commit
15a56fa90b
|
@ -45,6 +45,11 @@ limitations under the License.
|
||||||
color: $tab-label-active-fg-color;
|
color: $tab-label-active-fg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove temporary hack alongside "visit old settings" tab
|
||||||
|
.mx_TabbedView_tabLabel_TEMP_HACK {
|
||||||
|
background-color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabLabel_icon {
|
.mx_TabbedView_tabLabel_icon {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
|
@ -89,4 +94,5 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_TabbedView_tabPanelContent {
|
.mx_TabbedView_tabPanelContent {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
min-width: 560px;
|
||||||
}
|
}
|
|
@ -21,3 +21,24 @@ limitations under the License.
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_disabled {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_hasKind {
|
||||||
|
padding: 10px 25px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_kind_primary {
|
||||||
|
color: $button-primary-fg-color;
|
||||||
|
background-color: $button-primary-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_kind_primary.mx_AccessibleButton_disabled {
|
||||||
|
color: $button-primary-disabled-fg-color;
|
||||||
|
background-color: $button-primary-disabled-bg-color;
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,25 @@
|
||||||
.mx_GeneralSettingsTab_profile input {
|
.mx_GeneralSettingsTab_profile {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_GeneralSettingsTab_profileControls {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_GeneralSettingsTab_profileControls .mx_Field #profileDisplayName {
|
||||||
|
width: calc(100% - 20px); // subtract 10px padding on left and right
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_GeneralSettingsTab_profileAvatar {
|
||||||
|
width: 88px;
|
||||||
|
height: 88px;
|
||||||
|
margin-left: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_GeneralSettingsTab_profileAvatar div {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 14px;
|
width: 88px;
|
||||||
padding: 5px;
|
height: 88px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
background-color: #ccc;
|
||||||
}
|
}
|
|
@ -200,6 +200,12 @@ $tab-label-active-bg-color: #7ac9a1;
|
||||||
$tab-label-icon-bg-color: #454545;
|
$tab-label-icon-bg-color: #454545;
|
||||||
$tab-label-active-icon-bg-color: #ffffff;
|
$tab-label-active-icon-bg-color: #ffffff;
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
$button-primary-fg-color: #ffffff;
|
||||||
|
$button-primary-bg-color: #7ac9a1;
|
||||||
|
$button-primary-disabled-fg-color: #ffffff;
|
||||||
|
$button-primary-disabled-bg-color: #bce4d0;
|
||||||
|
|
||||||
// unused?
|
// unused?
|
||||||
$progressbar-color: #000;
|
$progressbar-color: #000;
|
||||||
|
|
||||||
|
|
|
@ -196,6 +196,12 @@ $tab-label-active-bg-color: #7ac9a1;
|
||||||
$tab-label-icon-bg-color: #454545;
|
$tab-label-icon-bg-color: #454545;
|
||||||
$tab-label-active-icon-bg-color: #ffffff;
|
$tab-label-active-icon-bg-color: #ffffff;
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
$button-primary-fg-color: #ffffff;
|
||||||
|
$button-primary-bg-color: #7ac9a1;
|
||||||
|
$button-primary-disabled-fg-color: #ffffff;
|
||||||
|
$button-primary-disabled-bg-color: #bce4d0;
|
||||||
|
|
||||||
// unused?
|
// unused?
|
||||||
$progressbar-color: #000;
|
$progressbar-color: #000;
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ export class Tab {
|
||||||
export class TabbedView extends React.Component {
|
export class TabbedView extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
// The tabs to show
|
// The tabs to show
|
||||||
tabs: PropTypes.arrayOf(Tab).isRequired,
|
tabs: PropTypes.arrayOf(PropTypes.instanceOf(Tab)).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -74,6 +74,7 @@ export class TabbedView extends React.Component {
|
||||||
|
|
||||||
const idx = this.props.tabs.indexOf(tab);
|
const idx = this.props.tabs.indexOf(tab);
|
||||||
if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active";
|
if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active";
|
||||||
|
if (tab.label === "Visit old settings") classes += "mx_TabbedView_tabLabel_TEMP_HACK";
|
||||||
|
|
||||||
let tabIcon = null;
|
let tabIcon = null;
|
||||||
if (tab.icon) {
|
if (tab.icon) {
|
||||||
|
|
|
@ -28,41 +28,56 @@ import { KeyCode } from '../../../Keyboard';
|
||||||
* @returns {Object} rendered react
|
* @returns {Object} rendered react
|
||||||
*/
|
*/
|
||||||
export default function AccessibleButton(props) {
|
export default function AccessibleButton(props) {
|
||||||
const {element, onClick, children, ...restProps} = props;
|
const {element, onClick, children, kind, disabled, ...restProps} = props;
|
||||||
restProps.onClick = onClick;
|
|
||||||
// We need to consume enter onKeyDown and space onKeyUp
|
if (!disabled) {
|
||||||
// otherwise we are risking also activating other keyboard focusable elements
|
restProps.onClick = onClick;
|
||||||
// that might receive focus as a result of the AccessibleButtonClick action
|
// We need to consume enter onKeyDown and space onKeyUp
|
||||||
// It's because we are using html buttons at a few places e.g. inside dialogs
|
// otherwise we are risking also activating other keyboard focusable elements
|
||||||
// And divs which we report as role button to assistive technologies.
|
// that might receive focus as a result of the AccessibleButtonClick action
|
||||||
// Browsers handle space and enter keypresses differently and we are only adjusting to the
|
// It's because we are using html buttons at a few places e.g. inside dialogs
|
||||||
// inconsistencies here
|
// And divs which we report as role button to assistive technologies.
|
||||||
restProps.onKeyDown = function(e) {
|
// Browsers handle space and enter keypresses differently and we are only adjusting to the
|
||||||
if (e.keyCode === KeyCode.ENTER) {
|
// inconsistencies here
|
||||||
e.stopPropagation();
|
restProps.onKeyDown = function (e) {
|
||||||
e.preventDefault();
|
if (e.keyCode === KeyCode.ENTER) {
|
||||||
return onClick(e);
|
e.stopPropagation();
|
||||||
}
|
e.preventDefault();
|
||||||
if (e.keyCode === KeyCode.SPACE) {
|
return onClick(e);
|
||||||
e.stopPropagation();
|
}
|
||||||
e.preventDefault();
|
if (e.keyCode === KeyCode.SPACE) {
|
||||||
}
|
e.stopPropagation();
|
||||||
};
|
e.preventDefault();
|
||||||
restProps.onKeyUp = function(e) {
|
}
|
||||||
if (e.keyCode === KeyCode.SPACE) {
|
};
|
||||||
e.stopPropagation();
|
restProps.onKeyUp = function (e) {
|
||||||
e.preventDefault();
|
if (e.keyCode === KeyCode.SPACE) {
|
||||||
return onClick(e);
|
e.stopPropagation();
|
||||||
}
|
e.preventDefault();
|
||||||
if (e.keyCode === KeyCode.ENTER) {
|
return onClick(e);
|
||||||
e.stopPropagation();
|
}
|
||||||
e.preventDefault();
|
if (e.keyCode === KeyCode.ENTER) {
|
||||||
}
|
e.stopPropagation();
|
||||||
};
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
restProps.tabIndex = restProps.tabIndex || "0";
|
restProps.tabIndex = restProps.tabIndex || "0";
|
||||||
restProps.role = "button";
|
restProps.role = "button";
|
||||||
restProps.className = (restProps.className ? restProps.className + " " : "") +
|
restProps.className = (restProps.className ? restProps.className + " " : "") +
|
||||||
"mx_AccessibleButton";
|
"mx_AccessibleButton";
|
||||||
|
|
||||||
|
if (kind) {
|
||||||
|
// We apply a hasKind class to maintain backwards compatibility with
|
||||||
|
// buttons which might not know about kind and break
|
||||||
|
restProps.className += " mx_AccessibleButton_hasKind mx_AccessibleButton_kind_" + kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disabled) {
|
||||||
|
restProps.className += " mx_AccessibleButton_disabled";
|
||||||
|
}
|
||||||
|
|
||||||
return React.createElement(element, restProps, children);
|
return React.createElement(element, restProps, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +91,12 @@ AccessibleButton.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
element: PropTypes.string,
|
element: PropTypes.string,
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
|
|
||||||
|
// The kind of button, similar to how Bootstrap works.
|
||||||
|
// See available classes for AccessibleButton for options.
|
||||||
|
kind: PropTypes.string,
|
||||||
|
|
||||||
|
disabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
AccessibleButton.defaultProps = {
|
AccessibleButton.defaultProps = {
|
||||||
|
|
|
@ -16,32 +16,82 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {_t} from "../../../../languageHandler";
|
import {_t} from "../../../../languageHandler";
|
||||||
|
import MatrixClientPeg from "../../../../MatrixClientPeg";
|
||||||
|
import Field from "../../elements/Field";
|
||||||
|
import AccessibleButton from "../../elements/AccessibleButton";
|
||||||
|
|
||||||
export default class GeneralSettingsTab extends React.Component {
|
export default class GeneralSettingsTab extends React.Component {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
this.state = {
|
||||||
|
userId: client.getUserId(),
|
||||||
|
displayName: client.getUser(client.getUserId()).displayName,
|
||||||
|
enableProfileSave: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveProfile = async (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!this.state.enableProfileSave) return;
|
||||||
|
this.setState({enableProfileSave: false});
|
||||||
|
|
||||||
|
// TODO: What do we do about errors?
|
||||||
|
await MatrixClientPeg.get().setDisplayName(this.state.displayName);
|
||||||
|
|
||||||
|
// TODO: Support avatars
|
||||||
|
|
||||||
|
this.setState({enableProfileSave: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
_onDisplayNameChanged = (e) => {
|
||||||
|
this.setState({
|
||||||
|
displayName: e.target.value,
|
||||||
|
enableProfileSave: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
_renderProfileSection() {
|
||||||
|
const form = (
|
||||||
|
<form onSubmit={this._saveProfile} autoComplete={false} noValidate={true}>
|
||||||
|
<div className="mx_GeneralSettingsTab_profile">
|
||||||
|
<div className="mx_GeneralSettingsTab_profileControls">
|
||||||
|
<p className="mx_GeneralSettingsTab_profileUsername">{this.state.userId}</p>
|
||||||
|
<Field id="profileDisplayName" label={_t("Display Name")}
|
||||||
|
type="text" value={this.state.displayName} autocomplete="off"
|
||||||
|
onChange={this._onDisplayNameChanged}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mx_GeneralSettingsTab_profileAvatar">
|
||||||
|
{/*TODO: Ditch avatar placeholder and use the real thing*/}
|
||||||
|
<div/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AccessibleButton onClick={this._saveProfile} kind="primary"
|
||||||
|
disabled={!this.state.enableProfileSave}
|
||||||
|
>
|
||||||
|
{_t("Save")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_SettingsTab_section">
|
||||||
|
<span className="mx_SettingsTab_subheading">{_t("Profile")}</span>
|
||||||
|
{form}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="mx_SettingsTab">
|
<div className="mx_SettingsTab">
|
||||||
<div className="mx_SettingsTab_heading">{_t("General")}</div>
|
<div className="mx_SettingsTab_heading">{_t("General")}</div>
|
||||||
<div className="mx_GeneralSettingsTab_profile mx_SettingsTab_section">
|
{this._renderProfileSection()}
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Profile")}</span>
|
|
||||||
<input type="text" value="travis"/>
|
|
||||||
<input type="text" value="TravisR"/>
|
|
||||||
</div>
|
|
||||||
<div className="mx_GeneralSettingsTab_profile mx_SettingsTab_section">
|
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Profile")}</span>
|
|
||||||
<input type="text" value="travis"/>
|
|
||||||
<input type="text" value="TravisR"/>
|
|
||||||
</div>
|
|
||||||
<div className="mx_GeneralSettingsTab_profile mx_SettingsTab_section">
|
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Profile")}</span>
|
|
||||||
<input type="text" value="travis"/>
|
|
||||||
<input type="text" value="TravisR"/>
|
|
||||||
</div>
|
|
||||||
<div className="mx_GeneralSettingsTab_profile mx_SettingsTab_section">
|
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Profile")}</span>
|
|
||||||
<input type="text" value="travis"/>
|
|
||||||
<input type="text" value="TravisR"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -403,6 +403,7 @@
|
||||||
"Noisy": "Noisy",
|
"Noisy": "Noisy",
|
||||||
"General": "General",
|
"General": "General",
|
||||||
"Profile": "Profile",
|
"Profile": "Profile",
|
||||||
|
"Display Name": "Display Name",
|
||||||
"Cannot add any more widgets": "Cannot add any more widgets",
|
"Cannot add any more widgets": "Cannot add any more widgets",
|
||||||
"The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.",
|
"The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.",
|
||||||
"Add a widget": "Add a widget",
|
"Add a widget": "Add a widget",
|
||||||
|
|
Loading…
Reference in New Issue