Make tabs be their own panels
parent
5adfc09237
commit
15709040e7
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Travis Ralston
|
Copyright 2017 Travis Ralston
|
||||||
|
Copyright 2019 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -20,40 +21,22 @@ limitations under the License.
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: $tab-panel-bg-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabLabels {
|
.mx_TabbedView_tabLabels {
|
||||||
width: 300px;
|
width: 136px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: $tab-list-bg-color;
|
color: $tab-label-fg-color;
|
||||||
color: $tab-list-fg-color;
|
|
||||||
border-right: 1px solid $tab-border-color;
|
|
||||||
border-left: 1px solid $tab-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_TabbedView_tabPanels {
|
|
||||||
width: calc(100% - 320px);
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
padding-left: 20px;
|
|
||||||
scroll-snap-type: block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabLabel {
|
.mx_TabbedView_tabLabel {
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
text-transform: uppercase;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 20px;
|
border-radius: 3px;
|
||||||
width: calc(100% - 40px);
|
font-size: 12px;
|
||||||
border-bottom: 1px solid $tab-border-color;
|
font-weight: 600;
|
||||||
}
|
height: 20px;
|
||||||
|
|
||||||
.mx_TabbedView_exit {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabLabel:hover {
|
.mx_TabbedView_tabLabel:hover {
|
||||||
|
@ -61,16 +44,17 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabLabel_active {
|
.mx_TabbedView_tabLabel_active {
|
||||||
font-weight: 700;
|
background-color: $tab-label-active-bg-color;
|
||||||
background-color: $tab-list-active-bg-color;
|
color: $tab-label-active-fg-color;
|
||||||
color: $tab-list-active-fg-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabPanel {
|
.mx_TabbedView_tabPanel {
|
||||||
|
width: calc(100% - 320px);
|
||||||
|
display: inline-block;
|
||||||
height: 100vh; // 100% of viewport height
|
height: 100vh; // 100% of viewport height
|
||||||
scroll-snap-align: start;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabPanelContent {
|
.mx_TabbedView_tabPanelContent {
|
||||||
width: 600px;
|
flex-grow: 1;
|
||||||
}
|
}
|
|
@ -187,12 +187,10 @@ $lightbox-fg-color: #ffffff;
|
||||||
$lightbox-border-color: #ffffff;
|
$lightbox-border-color: #ffffff;
|
||||||
|
|
||||||
// Tabbed views
|
// Tabbed views
|
||||||
$tab-list-bg-color: $secondary-accent-color;
|
$tab-label-fg-color: #45474a;
|
||||||
$tab-list-fg-color: $accent-color;
|
$tab-label-active-fg-color: #ffffff;
|
||||||
$tab-list-active-bg-color: $tertiary-accent-color;
|
$tab-label-bg-color: transparent;
|
||||||
$tab-list-active-fg-color: $accent-color;
|
$tab-label-active-bg-color: #7ac9a1;
|
||||||
$tab-border-color: $tertiary-accent-color;
|
|
||||||
$tab-panel-bg-color: #fff;
|
|
||||||
|
|
||||||
// unused?
|
// unused?
|
||||||
$progressbar-color: #000;
|
$progressbar-color: #000;
|
||||||
|
|
|
@ -183,12 +183,10 @@ $imagebody-giflabel-border: rgba(0, 0, 0, 0.2);
|
||||||
$imagebody-giflabel-color: rgba(255, 255, 255, 1);
|
$imagebody-giflabel-color: rgba(255, 255, 255, 1);
|
||||||
|
|
||||||
// Tabbed views
|
// Tabbed views
|
||||||
$tab-list-bg-color: $secondary-accent-color;
|
$tab-label-fg-color: #45474a;
|
||||||
$tab-list-fg-color: $accent-color;
|
$tab-label-active-fg-color: #ffffff;
|
||||||
$tab-list-active-bg-color: $tertiary-accent-color;
|
$tab-label-bg-color: transparent;
|
||||||
$tab-list-active-fg-color: $accent-color;
|
$tab-label-active-bg-color: #7ac9a1;
|
||||||
$tab-border-color: $tertiary-accent-color;
|
|
||||||
$tab-panel-bg-color: #fff;
|
|
||||||
|
|
||||||
// unused?
|
// unused?
|
||||||
$progressbar-color: #000;
|
$progressbar-color: #000;
|
||||||
|
|
|
@ -16,45 +16,42 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {_t, _td} from '../../languageHandler';
|
import {_t} from '../../languageHandler';
|
||||||
import GeminiScrollbar from 'react-gemini-scrollbar';
|
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
//import scrollSnapPolyfill from 'css-scroll-snap-polyfill';
|
|
||||||
|
|
||||||
const DEFAULT_EXIT_STRING = _td("Return to app");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a tab for the TabbedView
|
* Represents a tab for the TabbedView.
|
||||||
*/
|
*/
|
||||||
export class Tab {
|
export class Tab {
|
||||||
/**
|
/**
|
||||||
* Creates a new tab
|
* Creates a new tab.
|
||||||
* @param {string} tabLabel The untranslated tab label
|
* @param {string} tabLabel The untranslated tab label.
|
||||||
|
* @param {string} tabIconRef The relative path to the tab's icon.
|
||||||
* @param {string} tabJsx The JSX for the tab container.
|
* @param {string} tabJsx The JSX for the tab container.
|
||||||
*/
|
*/
|
||||||
constructor(tabLabel, tabJsx) {
|
constructor(tabLabel, tabIconRef, tabJsx) {
|
||||||
this.label = tabLabel;
|
this.label = tabLabel;
|
||||||
this.body = tabJsx;
|
this.body = tabJsx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TabbedView extends React.Component {
|
export class TabbedView extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
// The tabs to show
|
||||||
|
tabs: PropTypes.arrayOf(Tab).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// This is used to track when the user has scrolled all the way up or down so we
|
this.state = {
|
||||||
// don't immediately start flipping between tabs.
|
|
||||||
this._reachedEndAt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
activeTabIndex: 0,
|
activeTabIndex: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_getActiveTabIndex() {
|
_getActiveTabIndex() {
|
||||||
return this.state ? this.state.activeTabIndex : 0;
|
if (!this.state || !this.state.activeTabIndex) return 0;
|
||||||
|
return this.state.activeTabIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,28 +63,12 @@ export class TabbedView extends React.Component {
|
||||||
const idx = this.props.tabs.indexOf(tab);
|
const idx = this.props.tabs.indexOf(tab);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
this.setState({activeTabIndex: idx});
|
this.setState({activeTabIndex: idx});
|
||||||
this._reachedEndAt = 0; // reset scroll timer
|
} else {
|
||||||
}
|
console.error("Could not find tab " + tab.label + " in tabs");
|
||||||
else console.error("Could not find tab " + tab.label + " in tabs");
|
|
||||||
}
|
|
||||||
|
|
||||||
_nextTab() {
|
|
||||||
let targetIndex = this._getActiveTabIndex() + 1;
|
|
||||||
if (targetIndex < this.props.tabs.length) {
|
|
||||||
this.setState({activeTabIndex: targetIndex});
|
|
||||||
this._reachedEndAt = 0; // reset scroll timer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_previousTab() {
|
_renderTabLabel(tab) {
|
||||||
let targetIndex = this._getActiveTabIndex() - 1;
|
|
||||||
if (targetIndex >= 0) {
|
|
||||||
this.setState({activeTabIndex: targetIndex});
|
|
||||||
this._reachedEndAt = 0; // reset scroll timer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_getTabLabel(tab) {
|
|
||||||
let classes = "mx_TabbedView_tabLabel ";
|
let classes = "mx_TabbedView_tabLabel ";
|
||||||
|
|
||||||
const idx = this.props.tabs.indexOf(tab);
|
const idx = this.props.tabs.indexOf(tab);
|
||||||
|
@ -101,7 +82,7 @@ export class TabbedView extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getTabPanel(tab) {
|
_renderTabPanel(tab) {
|
||||||
return (
|
return (
|
||||||
<div className="mx_TabbedView_tabPanel" key={"mx_tabpanel_" + tab.label}>
|
<div className="mx_TabbedView_tabPanel" key={"mx_tabpanel_" + tab.label}>
|
||||||
{tab.body}
|
{tab.body}
|
||||||
|
@ -109,57 +90,17 @@ export class TabbedView extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
console.log("SCROLL SNAP POLYFILL: UPDATE");
|
|
||||||
//scrollSnapPolyfill();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
console.log("SCROLL SNAP POLYFILL: MOUNT");
|
|
||||||
//scrollSnapPolyfill();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const labels = [];
|
const labels = this.props.tabs.map(tab => this._renderTabLabel(tab));
|
||||||
const tabs = [];
|
const panel = this._renderTabPanel(this.props.tabs[this._getActiveTabIndex()]);
|
||||||
|
|
||||||
for (const tab of this.props.tabs) {
|
|
||||||
labels.push(this._getTabLabel(tab));
|
|
||||||
tabs.push(this._getTabPanel(tab));
|
|
||||||
}
|
|
||||||
|
|
||||||
const returnToApp = (
|
|
||||||
<span className="mx_TabbedView_tabLabel mx_TabbedView_exit" onClick={this.props.onExit}>
|
|
||||||
{_t(this.props.exitLabel || DEFAULT_EXIT_STRING)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_TabbedView">
|
<div className="mx_TabbedView">
|
||||||
<div className="mx_TabbedView_tabLabels">
|
<div className="mx_TabbedView_tabLabels">
|
||||||
{returnToApp}
|
|
||||||
{labels}
|
{labels}
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_TabbedView_tabPanels">
|
{panel}
|
||||||
{tabs}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TabbedView.PropTypes = {
|
|
||||||
// Called when the user clicks the "Exit" or "Return to app" button
|
|
||||||
onExit: PropTypes.func.isRequired,
|
|
||||||
|
|
||||||
// The untranslated label for the "Return to app" button.
|
|
||||||
// Default: "Return to app"
|
|
||||||
exitLabel: PropTypes.string,
|
|
||||||
|
|
||||||
// The tabs to show
|
|
||||||
tabs: PropTypes.arrayOf(Tab).isRequired,
|
|
||||||
};
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
|
||||||
import {Tab, TabbedView} from "../../structures/TabbedView";
|
import {Tab, TabbedView} from "../../structures/TabbedView";
|
||||||
import {_td} from "../../../languageHandler";
|
import {_td} from "../../../languageHandler";
|
||||||
|
|
||||||
|
@ -27,14 +26,14 @@ export default class UserSettingsDialog extends React.Component {
|
||||||
|
|
||||||
_getTabs() {
|
_getTabs() {
|
||||||
return [
|
return [
|
||||||
new Tab(_td("General"), <div>General Test</div>),
|
new Tab(_td("General"), "", <div>General Test</div>),
|
||||||
new Tab(_td("Account"), <div>Account Test</div>),
|
new Tab(_td("Account"), "", <div>Account Test</div>),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<TabbedView onExit={this.props.onFinished} tabs={this._getTabs()} />
|
<TabbedView tabs={this._getTabs()} />
|
||||||
// <UserSettings
|
// <UserSettings
|
||||||
// onClose={this.props.onFinished}
|
// onClose={this.props.onFinished}
|
||||||
// brand={SdkConfig.get().brand}
|
// brand={SdkConfig.get().brand}
|
||||||
|
|
Loading…
Reference in New Issue