update sidebar styling, add new entry links (#276)

* udpate sidebar styling, add new entry links

* make sidebar new entry links always visible

* simplify app bar implementation, findbar
This commit is contained in:
Shawn Erquhart
2017-03-15 14:32:41 -04:00
committed by David Calavera
parent e7dc2c4e84
commit 705e348138
16 changed files with 217 additions and 639 deletions

View File

@ -1,8 +1,4 @@
.root {
margin-top: 64px;
.entriesPanel {
padding: 0 40px;
}
.sidebar {
width: 200px;
background-color: #fff;
}

View File

@ -2,27 +2,28 @@ import React, { PropTypes } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import pluralize from 'pluralize';
import { connect } from 'react-redux';
import { IndexLink } from "react-router";
import FontIcon from 'react-toolbox/lib/font_icon';
import { Layout, Panel } from 'react-toolbox/lib/layout';
import { Navigation } from 'react-toolbox/lib/navigation';
import { Link } from 'react-toolbox/lib/link';
import { Notifs } from 'redux-notifications';
import TopBarProgress from 'react-topbar-progress-indicator';
import Sidebar from './Sidebar';
import { loadConfig } from '../actions/config';
import { loginUser, logoutUser } from '../actions/auth';
import { toggleSidebar } from '../actions/globalUI';
import { loadConfig as actionLoadConfig } from '../actions/config';
import { loginUser as actionLoginUser, logoutUser as actionLogoutUser } from '../actions/auth';
import { toggleSidebar as actionToggleSidebar } from '../actions/globalUI';
import { currentBackend } from '../backends/backend';
import {
SHOW_COLLECTION,
CREATE_COLLECTION,
HELP,
runCommand,
navigateToCollection,
createNewEntryInCollection,
runCommand as actionRunCommand,
navigateToCollection as actionNavigateToCollection,
createNewEntryInCollection as actionCreateNewEntryInCollection,
} from '../actions/findbar';
import AppHeader from '../components/AppHeader/AppHeader';
import { Loader, Toast } from '../components/UI/index';
import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper';
import { SIMPLE, EDITORIAL_WORKFLOW } from '../constants/publishModes';
import styles from './App.css';
import sidebarStyles from './Sidebar.css';
TopBarProgress.config({
barColors: {
@ -49,6 +50,7 @@ class App extends React.Component {
user: ImmutablePropTypes.map,
runCommand: PropTypes.func.isRequired,
isFetching: PropTypes.bool.isRequired,
publishMode: PropTypes.oneOf([SIMPLE, EDITORIAL_WORKFLOW]),
};
static configError(config) {
@ -63,11 +65,11 @@ class App extends React.Component {
}
componentDidMount() {
this.props.dispatch(loadConfig());
this.props.dispatch(actionLoadConfig());
}
handleLogin(credentials) {
this.props.dispatch(loginUser(credentials));
this.props.dispatch(actionLoginUser(credentials));
}
authenticating() {
@ -91,35 +93,9 @@ class App extends React.Component {
);
}
generateFindBarCommands() {
// Generate command list
const commands = [];
const defaultCommands = [];
this.props.collections.forEach((collection) => {
commands.push({
id: `show_${ collection.get('name') }`,
pattern: `Show ${ pluralize(collection.get('label')) }`,
type: SHOW_COLLECTION,
payload: { collectionName: collection.get('name') },
});
if (defaultCommands.length < 5) defaultCommands.push(`show_${ collection.get('name') }`);
// if (collection.get('create') === true) {
// commands.push({
// id: `create_${ collection.get('name') }`,
// pattern: `Create new ${ pluralize(collection.get('label'), 1) }(:itemName as ${ pluralize(collection.get('label'), 1) } Name)`,
// type: CREATE_COLLECTION,
// payload: { collectionName: collection.get('name') },
// });
// }
});
// commands.push({ id: HELP, type: HELP, pattern: 'Help' });
// defaultCommands.push(HELP);
return { commands, defaultCommands };
handleLinkClick(event, handler, ...args) {
event.preventDefault();
handler(...args);
}
render() {
@ -134,6 +110,7 @@ class App extends React.Component {
createNewEntryInCollection,
logoutUser,
isFetching,
publishMode,
} = this.props;
@ -153,45 +130,67 @@ class App extends React.Component {
return this.authenticating();
}
const { commands, defaultCommands } = this.generateFindBarCommands();
const sidebarContent = (
<nav className={styles.nav}>
<h1 className={styles.heading}>Collections</h1>
<Navigation type="vertical">
<div>
<Navigation type="vertical" className={sidebarStyles.nav}>
{
collections.valueSeq().map(collection =>
<Link
key={collection.get('name')}
onClick={navigateToCollection.bind(this, collection.get('name'))} // eslint-disable-line
>
{collection.get('label')}
</Link>
)
publishMode === SIMPLE ? null :
<section>
<h1 className={sidebarStyles.heading}>Publishing</h1>
<div className={sidebarStyles.linkWrapper}>
<IndexLink to="/" className={sidebarStyles.viewEntriesLink}>Editorial Workflow</IndexLink>
</div>
</section>
}
<section>
<h1 className={sidebarStyles.heading}>Collections</h1>
{
collections.valueSeq().map((collection) => {
const collectionName = collection.get('name');
return (
<div key={collectionName} className={sidebarStyles.linkWrapper}>
<a
href={getCollectionUrl(collectionName, true)}
className={sidebarStyles.viewEntriesLink}
onClick={e => this.handleLinkClick(e, navigateToCollection, collectionName)}
>
{pluralize(collection.get('label'))}
</a>
{
collection.get('create') ? (
<a
href={getNewEntryUrl(collectionName, true)}
className={sidebarStyles.createEntryLink}
onClick={e => this.handleLinkClick(e, createNewEntryInCollection, collectionName)}
>
<FontIcon value="add_circle_outline" />
</a>
) : null
}
</div>
);
})
}
</section>
</Navigation>
</nav>
</div>
);
return (
<Sidebar content={sidebarContent}>
<Layout theme={styles}>
<Notifs
className={styles.notifsContainer}
CustomComponent={Toast}
/>
<Layout>
<Notifs CustomComponent={Toast} />
<AppHeader
user={user}
collections={collections}
commands={commands}
defaultCommands={defaultCommands}
runCommand={runCommand}
onCreateEntryClick={createNewEntryInCollection}
onLogoutClick={logoutUser}
toggleDrawer={toggleSidebar}
/>
<Panel scrollY>
<Panel scrollY className={styles.entriesPanel}>
{ isFetching && <TopBarProgress /> }
<div className={styles.main}>
<div>
{children}
</div>
</Panel>
@ -206,24 +205,25 @@ function mapStateToProps(state) {
const { auth, config, collections, globalUI } = state;
const user = auth && auth.get('user');
const isFetching = globalUI.get('isFetching');
return { auth, config, collections, user, isFetching };
const publishMode = config && config.get('publish_mode');
return { auth, config, collections, user, isFetching, publishMode };
}
function mapDispatchToProps(dispatch) {
return {
dispatch,
toggleSidebar: () => dispatch(toggleSidebar()),
toggleSidebar: () => dispatch(actionToggleSidebar()),
runCommand: (type, payload) => {
dispatch(runCommand(type, payload));
dispatch(actionRunCommand(type, payload));
},
navigateToCollection: (collection) => {
dispatch(navigateToCollection(collection));
dispatch(actionNavigateToCollection(collection));
},
createNewEntryInCollection: (collectionName) => {
dispatch(createNewEntryInCollection(collectionName));
dispatch(actionCreateNewEntryInCollection(collectionName));
},
logoutUser: () => {
dispatch(logoutUser());
dispatch(actionLogoutUser());
},
};
}

View File

@ -5,7 +5,6 @@ import { loadEntries } from '../actions/entries';
import { selectEntries } from '../reducers';
import { Loader } from '../components/UI';
import EntryListing from '../components/EntryListing/EntryListing';
import styles from './breakpoints.css';
class CollectionPage extends React.Component {
@ -42,7 +41,7 @@ class CollectionPage extends React.Component {
if (collections == null) {
return <h1>No collections defined in your config.yml</h1>;
}
return (<div className={styles.root}>
return (<div>
{entries ?
<EntryListing
collections={collection}
@ -54,7 +53,7 @@ class CollectionPage extends React.Component {
{collection.get('label')}
</EntryListing>
:
<Loader active>{['Loading Entries', 'Caching Entries', 'This might take several minutes']}</Loader>
<Loader active>{['Loading Entries', 'Caching Entries', 'This might take several minutes']}</Loader>
}
</div>);
}

View File

@ -3,7 +3,6 @@ import { connect } from 'react-redux';
import { SIMPLE, EDITORIAL_WORKFLOW } from '../constants/publishModes';
import history from '../routing/history';
import UnpublishedEntriesPanel from './editorialWorkflow/UnpublishedEntriesPanel';
import styles from './breakpoints.css';
class DashboardPage extends Component {
@ -15,7 +14,7 @@ class DashboardPage extends Component {
render() {
return (
<div className={styles.root}>
<div>
<h1>Dashboard</h1>
<UnpublishedEntriesPanel />
</div>

View File

@ -2,10 +2,9 @@ import React, { PropTypes } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { selectSearchedEntries } from '../reducers';
import { searchEntries, clearSearch } from '../actions/search';
import { searchEntries as actionSearchEntries, clearSearch as actionClearSearch } from '../actions/search';
import { Loader } from '../components/UI';
import EntryListing from '../components/EntryListing/EntryListing';
import styles from './breakpoints.css';
class SearchPage extends React.Component {
@ -42,17 +41,17 @@ class SearchPage extends React.Component {
render() {
const { collections, searchTerm, entries, isFetching, page, publicFolder } = this.props;
return (<div className={styles.root}>
return (<div>
{(isFetching === true || !entries) ?
<Loader active>{['Loading Entries', 'Caching Entries', 'This might take several minutes']}</Loader>
:
<EntryListing
collections={collections}
entries={entries}
page={page}
publicFolder={publicFolder}
onPaginate={this.handleLoadMore}
>
<EntryListing
collections={collections}
entries={entries}
page={page}
publicFolder={publicFolder}
onPaginate={this.handleLoadMore}
>
Results for {searchTerm}
</EntryListing>
}
@ -76,7 +75,7 @@ function mapStateToProps(state, ownProps) {
export default connect(
mapStateToProps,
{
searchEntries,
clearSearch,
searchEntries: actionSearchEntries,
clearSearch: actionClearSearch,
}
)(SearchPage);

View File

@ -1,8 +1,47 @@
@import '../components/UI/theme';
.root {
margin-top: 64px;
}
.sidebar {
width: 200px;
background-color: #fff
width: 220px;
background-color: #fff;
padding: 0 16px;
}
.heading {
margin-bottom: 8px;
}
.nav {
padding: 0;
}
.linkWrapper {
color: var(--defaultColor);
border-radius: var(--borderRadius);
display: flex;
align-items: center;
justify-content: space-between;
&:hover {
background-color: var(--defaultColorLight);
}
}
.viewEntriesLink {
font-size: 18px;
color: inherit;
padding: 10px 6px;
display: block;
width: 100%;
}
.createEntryLink {
color: inherit;
padding: 0 6px;
display: block;
position: relative;
top: 2px;
}

View File

@ -1,38 +0,0 @@
.root {
margin: auto;
}
@media (max-width: 749px) and (min-width: 495px) {
.root {
margin: auto;
width: 495px;
}
}
@media (max-width: 1004px) and (min-width: 750px) {
.root {
margin: auto;
width: 750px;
}
}
@media (max-width: 1514px) and (min-width: 1005px) {
.root {
margin: auto;
width: 1005px;
}
}
@media (max-width: 1769px) and (min-width: 1515px) {
.root {
margin: auto;
width: 1515px;
}
}
@media (min-width: 1770px) {
.root {
margin: auto;
width: 1770px;
}
}