From 45776b55b0d97d6a6b8b202d3076f19f1d055573 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 12 Oct 2016 13:17:17 +0200 Subject: [PATCH] Responsively changing layout to single-column + nav on smaller screens --- .../components/containers/mastodon.jsx | 8 ++- .../components/features/compose/index.jsx | 28 ++++++++ .../features/home_timeline/index.jsx | 19 ++++++ .../features/mentions_timeline/index.jsx | 19 ++++++ .../features/public_timeline/index.jsx | 67 ++----------------- .../features/ui/components/column.jsx | 13 +++- .../features/ui/components/columns_area.jsx | 10 ++- .../features/ui/components/drawer.jsx | 12 +++- .../features/ui/components/tabs_bar.jsx | 38 +++++++++++ .../components/features/ui/index.jsx | 53 ++++++--------- app/assets/stylesheets/components.scss | 28 ++++++++ package.json | 3 + yarn.lock | 22 ++++++ 13 files changed, 220 insertions(+), 100 deletions(-) create mode 100644 app/assets/javascripts/components/features/compose/index.jsx create mode 100644 app/assets/javascripts/components/features/home_timeline/index.jsx create mode 100644 app/assets/javascripts/components/features/mentions_timeline/index.jsx create mode 100644 app/assets/javascripts/components/features/ui/components/tabs_bar.jsx diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index 90fb469085..1327dba3e1 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -15,12 +15,15 @@ import { hashHistory, IndexRoute } from 'react-router'; +import UI from '../features/ui'; import Account from '../features/account'; import Status from '../features/status'; import GettingStarted from '../features/getting_started'; import PublicTimeline from '../features/public_timeline'; -import UI from '../features/ui'; import AccountTimeline from '../features/account_timeline'; +import HomeTimeline from '../features/home_timeline'; +import MentionsTimeline from '../features/mentions_timeline'; +import Compose from '../features/compose'; const store = configureStore(); @@ -77,6 +80,9 @@ const Mastodon = React.createClass({ + + + diff --git a/app/assets/javascripts/components/features/compose/index.jsx b/app/assets/javascripts/components/features/compose/index.jsx new file mode 100644 index 0000000000..4be9381585 --- /dev/null +++ b/app/assets/javascripts/components/features/compose/index.jsx @@ -0,0 +1,28 @@ +import Drawer from '../ui/components/drawer'; +import ComposeFormContainer from '../ui/containers/compose_form_container'; +import FollowFormContainer from '../ui/containers/follow_form_container'; +import UploadFormContainer from '../ui/containers/upload_form_container'; +import NavigationContainer from '../ui/containers/navigation_container'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; + +const Compose = React.createClass({ + + mixins: [PureRenderMixin], + + render () { + return ( + +
+ + + +
+ + +
+ ); + } + +}); + +export default Compose; diff --git a/app/assets/javascripts/components/features/home_timeline/index.jsx b/app/assets/javascripts/components/features/home_timeline/index.jsx new file mode 100644 index 0000000000..1f4b25450d --- /dev/null +++ b/app/assets/javascripts/components/features/home_timeline/index.jsx @@ -0,0 +1,19 @@ +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import StatusListContainer from '../ui/containers/status_list_container'; +import Column from '../ui/components/column'; + +const HomeTimeline = React.createClass({ + + mixins: [PureRenderMixin], + + render () { + return ( + + + + ); + }, + +}); + +export default HomeTimeline; diff --git a/app/assets/javascripts/components/features/mentions_timeline/index.jsx b/app/assets/javascripts/components/features/mentions_timeline/index.jsx new file mode 100644 index 0000000000..d9d0963d06 --- /dev/null +++ b/app/assets/javascripts/components/features/mentions_timeline/index.jsx @@ -0,0 +1,19 @@ +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import StatusListContainer from '../ui/containers/status_list_container'; +import Column from '../ui/components/column'; + +const MentionsTimeline = React.createClass({ + + mixins: [PureRenderMixin], + + render () { + return ( + + + + ); + }, + +}); + +export default MentionsTimeline; diff --git a/app/assets/javascripts/components/features/public_timeline/index.jsx b/app/assets/javascripts/components/features/public_timeline/index.jsx index 450725af69..7d3739214c 100644 --- a/app/assets/javascripts/components/features/public_timeline/index.jsx +++ b/app/assets/javascripts/components/features/public_timeline/index.jsx @@ -1,43 +1,14 @@ import { connect } from 'react-redux'; import PureRenderMixin from 'react-addons-pure-render-mixin'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import StatusList from '../../components/status_list'; +import StatusListContainer from '../ui/containers/status_list_container'; import Column from '../ui/components/column'; -import Immutable from 'immutable'; -import { makeGetTimeline } from '../../selectors'; import { - updateTimeline, refreshTimeline, - expandTimeline + updateTimeline } from '../../actions/timelines'; -import { deleteStatus } from '../../actions/statuses'; -import { replyCompose } from '../../actions/compose'; -import { - favourite, - reblog, - unreblog, - unfavourite -} from '../../actions/interactions'; - -const makeMapStateToProps = () => { - const getTimeline = makeGetTimeline(); - - const mapStateToProps = (state) => ({ - statuses: getTimeline(state, 'public'), - me: state.getIn(['timelines', 'me']) - }); - - return mapStateToProps; -}; const PublicTimeline = React.createClass({ - propTypes: { - statuses: ImmutablePropTypes.list.isRequired, - me: React.PropTypes.number.isRequired, - dispatch: React.PropTypes.func.isRequired - }, - mixins: [PureRenderMixin], componentWillMount () { @@ -62,44 +33,14 @@ const PublicTimeline = React.createClass({ } }, - handleReply (status) { - this.props.dispatch(replyCompose(status)); - }, - - handleReblog (status) { - if (status.get('reblogged')) { - this.props.dispatch(unreblog(status)); - } else { - this.props.dispatch(reblog(status)); - } - }, - - handleFavourite (status) { - if (status.get('favourited')) { - this.props.dispatch(unfavourite(status)); - } else { - this.props.dispatch(favourite(status)); - } - }, - - handleDelete (status) { - this.props.dispatch(deleteStatus(status.get('id'))); - }, - - handleScrollToBottom () { - this.props.dispatch(expandTimeline('public')); - }, - render () { - const { statuses, me } = this.props; - return ( - + ); }, }); -export default connect(makeMapStateToProps)(PublicTimeline); +export default connect()(PublicTimeline); diff --git a/app/assets/javascripts/components/features/ui/components/column.jsx b/app/assets/javascripts/components/features/ui/components/column.jsx index bb9331267e..be4fa89089 100644 --- a/app/assets/javascripts/components/features/ui/components/column.jsx +++ b/app/assets/javascripts/components/features/ui/components/column.jsx @@ -29,6 +29,15 @@ const scrollTop = (node) => { }; }; +const style = { + height: '100%', + boxSizing: 'border-box', + flex: '0 0 auto', + background: '#282c37', + display: 'flex', + flexDirection: 'column' +}; + const Column = React.createClass({ propTypes: { @@ -56,10 +65,8 @@ const Column = React.createClass({ header = ; } - const style = { width: '330px', flex: '0 0 auto', background: '#282c37', margin: '10px', marginRight: '0', marginBottom: '0', display: 'flex', flexDirection: 'column' }; - return ( -
+
{header} {this.props.children}
diff --git a/app/assets/javascripts/components/features/ui/components/columns_area.jsx b/app/assets/javascripts/components/features/ui/components/columns_area.jsx index 94433539bf..3f88b1ea32 100644 --- a/app/assets/javascripts/components/features/ui/components/columns_area.jsx +++ b/app/assets/javascripts/components/features/ui/components/columns_area.jsx @@ -1,12 +1,20 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; +const style = { + display: 'flex', + flex: '1 1 auto', + flexDirection: 'row', + justifyContent: 'flex-start', + overflowX: 'auto' +}; + const ColumnsArea = React.createClass({ mixins: [PureRenderMixin], render () { return ( -
+
{this.props.children}
); diff --git a/app/assets/javascripts/components/features/ui/components/drawer.jsx b/app/assets/javascripts/components/features/ui/components/drawer.jsx index dfba11ad2f..1fbb9333e3 100644 --- a/app/assets/javascripts/components/features/ui/components/drawer.jsx +++ b/app/assets/javascripts/components/features/ui/components/drawer.jsx @@ -1,12 +1,22 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; +const style = { + height: '100%', + flex: '0 0 auto', + boxSizing: 'border-box', + background: '#454b5e', + padding: '0', + display: 'flex', + flexDirection: 'column' +}; + const Drawer = React.createClass({ mixins: [PureRenderMixin], render () { return ( -
+
{this.props.children}
); diff --git a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx new file mode 100644 index 0000000000..f5d9859969 --- /dev/null +++ b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx @@ -0,0 +1,38 @@ +import { Link } from 'react-router'; + +const outerStyle = { + background: '#373b4a', + margin: '10px', + flex: '0 0 auto', + marginBottom: '0', + display: 'flex' +}; + +const tabStyle = { + display: 'block', + flex: '1 1 auto', + padding: '10px', + color: '#fff', + textDecoration: 'none', + fontSize: '12px', + fontWeight: '500', + borderBottom: '2px solid #373b4a' +}; + +const tabActiveStyle = { + borderBottom: '2px solid #2b90d9', + color: '#2b90d9' +}; + +const TabsBar = () => { + return ( +
+ Compose + Home + Mentions + Public +
+ ); +}; + +export default TabsBar; diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx index 0bc235b534..fab32a31e2 100644 --- a/app/assets/javascripts/components/features/ui/index.jsx +++ b/app/assets/javascripts/components/features/ui/index.jsx @@ -1,47 +1,38 @@ import ColumnsArea from './components/columns_area'; -import Column from './components/column'; -import Drawer from './components/drawer'; -import ComposeFormContainer from './containers/compose_form_container'; -import FollowFormContainer from './containers/follow_form_container'; -import UploadFormContainer from './containers/upload_form_container'; -import StatusListContainer from './containers/status_list_container'; import NotificationsContainer from './containers/notifications_container'; -import NavigationContainer from './containers/navigation_container'; import PureRenderMixin from 'react-addons-pure-render-mixin'; import LoadingBarContainer from './containers/loading_bar_container'; +import HomeTimeline from '../home_timeline'; +import MentionsTimeline from '../mentions_timeline'; +import Compose from '../compose'; +import MediaQuery from 'react-responsive'; +import TabsBar from './components/tabs_bar'; const UI = React.createClass({ - propTypes: { - router: React.PropTypes.object - }, - mixins: [PureRenderMixin], render () { + const layoutBreakpoint = 1024; + return ( -
- -
- - - -
- - -
- - - - - - - - - +
+ + + + {this.props.children} - + + + + + + + + {this.props.children} + + diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index 78419988d3..ef39a87edf 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -227,3 +227,31 @@ margin-bottom: 20px; } } + +.columns-area { + margin: 10px; + margin-left: 0; +} + +.column { + width: 330px; +} + +.drawer { + width: 280px; +} + +.column, .drawer { + margin-left: 10px; +} + +@media screen and (max-width: 1024px) { + .column, .drawer { + width: 100%; + margin: 0; + } + + .columns-area { + margin: 10px; + } +} diff --git a/package.json b/package.json index 990124985a..133244650f 100644 --- a/package.json +++ b/package.json @@ -38,5 +38,8 @@ "redux-thunk": "^2.1.0", "reselect": "^2.5.4", "sinon": "^1.17.6" + }, + "dependencies": { + "react-responsive": "^1.1.5" } } diff --git a/yarn.lock b/yarn.lock index 35203c7873..32f496d459 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1541,6 +1541,10 @@ css-loader@0.25.0: postcss-modules-values "^1.1.0" source-list-map "^0.1.4" +css-mediaquery@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/css-mediaquery/-/css-mediaquery-0.1.2.tgz#6a2c37344928618631c54bd33cedd301da18bea0" + css-select@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" @@ -2359,6 +2363,10 @@ https-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.0.tgz#b3ffdfe734b2a3d4a9efd58e8654c91fce86eafd" +hyphenate-style-name@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.1.tgz#bc49b9446e02b4570641afdd29c1ce7609d1b9cc" + iconv-lite@^0.4.13, iconv-lite@~0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" @@ -2928,6 +2936,12 @@ mantra-core@^1.6.1: react-komposer "^1.9.0" react-simple-di "^1.2.0" +matchmedia@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/matchmedia/-/matchmedia-0.1.2.tgz#cfd47f2bf68fbc7f5ea1bd3a3cf1715ecba3c1bd" + dependencies: + css-mediaquery "^0.1.2" + math-expression-evaluator@^1.2.14: version "1.2.14" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.14.tgz#39511771ed9602405fba9affff17eb4d2a3843ab" @@ -3823,6 +3837,14 @@ react-redux@^5.0.0-beta.3: lodash-es "^4.2.0" loose-envify "^1.1.0" +react-responsive: + version "1.1.5" + resolved "https://registry.yarnpkg.com/react-responsive/-/react-responsive-1.1.5.tgz#a7019a28817dcb601ef31d10d72f798a0d710a17" + dependencies: + hyphenate-style-name "^1.0.0" + matchmedia "^0.1.2" + object-assign "^4.0.1" + react-router@^2.8.0: version "2.8.1" resolved "https://registry.yarnpkg.com/react-router/-/react-router-2.8.1.tgz#73e9491f6ceb316d0f779829081863e378ee4ed7"