From f362b21961914c55d369b3bbb26f7b7a516d7b3f Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Mon, 16 Oct 2017 20:17:11 -0400 Subject: [PATCH 001/134] update top bar and collections sidebar UI --- package.json | 2 +- src/App/App.css | 12 + src/App/App.js | 172 +++++++++ src/App/Header.css | 71 ++++ src/App/Header.js | 75 ++++ src/Collection/Collection.css | 15 + .../Collection.js} | 46 +-- src/Collection/Entries.js | 38 ++ src/Collection/Sidebar.css | 65 ++++ src/Collection/Sidebar.js | 53 +++ src/actions/collections.js | 14 + src/actions/findbar.js | 38 -- src/actions/globalUI.js | 13 - src/{components/UI/theme.css => base.css} | 65 +++- src/components/AppHeader/AppHeader.css | 37 -- src/components/AppHeader/AppHeader.js | 89 ----- src/components/EntryEditor/EntryEditor.css | 2 + src/components/EntryEditor/ReactSplitPane.css | 31 ++ src/components/EntryListing/EntryListing.css | 2 +- src/components/EntryListing/EntryListing.js | 8 +- src/components/FindBar/FindBar.css | 17 - src/components/FindBar/FindBar.js | 56 --- src/components/Widgets.js | 8 +- src/components/Widgets/DateTime/DateTime.css | 1 + .../Widgets/{ => DateTime}/DateTimeControl.js | 0 .../Widgets/{ => DateTime}/DateTimePreview.js | 2 +- .../Widgets/DateTime/ReactDatetime.css | 210 +++++++++++ .../Widgets/Relation/ReactAutosuggest.css | 57 +++ src/components/Widgets/Relation/Relation.css | 1 + .../Widgets/{ => Relation}/RelationControl.js | 4 +- .../Widgets/{ => Relation}/RelationPreview.js | 2 +- src/components/stories/FindBar.js | 16 - src/components/stories/index.js | 1 - src/containers/App.css | 4 - src/containers/CollectionPage.css | 10 - src/containers/EntryPage.js | 4 - src/containers/Sidebar.css | 47 --- src/containers/Sidebar.js | 63 ---- src/icons/Icon.css | 10 + src/icons/Icon.js | 45 +++ src/icons/icon-add.svg | 3 + src/icons/icon-circle.svg | 3 + src/icons/icon-folder.svg | 3 + src/icons/icon-grid.svg | 3 + src/icons/icon-home.svg | 3 + src/icons/icon-list.svg | 3 + src/icons/icon-media-alt.svg | 3 + src/icons/icon-media.svg | 3 + src/icons/icon-page.svg | 3 + src/icons/icon-pages-alt.svg | 4 + src/icons/icon-pages.svg | 4 + src/icons/icon-settings.svg | 3 + src/icons/icon-workflow.svg | 3 + src/icons/icon-write.svg | 3 + src/index.css | 354 +----------------- src/reducers/globalUI.js | 13 +- src/root.js | 2 +- webpack.base.js | 8 +- .../site/content/docs/editorial-workflow.md | 6 - 59 files changed, 1029 insertions(+), 804 deletions(-) create mode 100644 src/App/App.css create mode 100644 src/App/App.js create mode 100644 src/App/Header.css create mode 100644 src/App/Header.js create mode 100644 src/Collection/Collection.css rename src/{containers/CollectionPage.js => Collection/Collection.js} (68%) create mode 100644 src/Collection/Entries.js create mode 100644 src/Collection/Sidebar.css create mode 100644 src/Collection/Sidebar.js create mode 100644 src/actions/collections.js delete mode 100644 src/actions/findbar.js delete mode 100644 src/actions/globalUI.js rename src/{components/UI/theme.css => base.css} (65%) delete mode 100644 src/components/AppHeader/AppHeader.css delete mode 100644 src/components/AppHeader/AppHeader.js create mode 100644 src/components/EntryEditor/ReactSplitPane.css delete mode 100644 src/components/FindBar/FindBar.css delete mode 100644 src/components/FindBar/FindBar.js create mode 100644 src/components/Widgets/DateTime/DateTime.css rename src/components/Widgets/{ => DateTime}/DateTimeControl.js (100%) rename src/components/Widgets/{ => DateTime}/DateTimePreview.js (83%) create mode 100644 src/components/Widgets/DateTime/ReactDatetime.css create mode 100644 src/components/Widgets/Relation/ReactAutosuggest.css create mode 100644 src/components/Widgets/Relation/Relation.css rename src/components/Widgets/{ => Relation}/RelationControl.js (97%) rename src/components/Widgets/{ => Relation}/RelationPreview.js (81%) delete mode 100644 src/components/stories/FindBar.js delete mode 100644 src/containers/App.css delete mode 100644 src/containers/CollectionPage.css delete mode 100644 src/containers/Sidebar.css delete mode 100644 src/containers/Sidebar.js create mode 100644 src/icons/Icon.css create mode 100644 src/icons/Icon.js create mode 100644 src/icons/icon-add.svg create mode 100644 src/icons/icon-circle.svg create mode 100644 src/icons/icon-folder.svg create mode 100644 src/icons/icon-grid.svg create mode 100644 src/icons/icon-home.svg create mode 100644 src/icons/icon-list.svg create mode 100644 src/icons/icon-media-alt.svg create mode 100644 src/icons/icon-media.svg create mode 100644 src/icons/icon-page.svg create mode 100644 src/icons/icon-pages-alt.svg create mode 100644 src/icons/icon-pages.svg create mode 100644 src/icons/icon-settings.svg create mode 100644 src/icons/icon-workflow.svg create mode 100644 src/icons/icon-write.svg diff --git a/package.json b/package.json index 84abe699b1c9..0d6a9a117d7d 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "stylelint-config-standard": "^13.0.2", "stylelint-declaration-block-order": "^0.1.0", "stylelint-declaration-use-variable": "^1.6.0", + "svg-inline-loader": "^0.8.0", "uglifyjs-webpack-plugin": "^1.0.1", "url-loader": "^0.5.9", "webpack": "^3.6.0", @@ -157,7 +158,6 @@ "react-redux": "^4.4.0", "react-router-dom": "^4.2.2", "react-router-redux": "^5.0.0-alpha.8", - "react-sidebar": "^2.2.1", "react-sortable-hoc": "^0.6.8", "react-split-pane": "^0.1.66", "react-toolbox": "^2.0.0-beta.12", diff --git a/src/App/App.css b/src/App/App.css new file mode 100644 index 000000000000..0424d6d58e9c --- /dev/null +++ b/src/App/App.css @@ -0,0 +1,12 @@ +@import "./Header.css"; + +.nc-app-container { + display: grid; + grid-template-columns: 1440px; + grid-template-rows: 56px auto; + grid-gap: 28px; + grid-template-areas: + "header" + "main"; + justify-content: center; +} diff --git a/src/App/App.js b/src/App/App.js new file mode 100644 index 000000000000..aa29e34e2992 --- /dev/null +++ b/src/App/App.js @@ -0,0 +1,172 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { connect } from 'react-redux'; +import { Route, Switch, Link, Redirect } from 'react-router-dom'; +import FontIcon from 'react-toolbox/lib/font_icon'; +import { Navigation } from 'react-toolbox/lib/navigation'; +import { Notifs } from 'redux-notifications'; +import TopBarProgress from 'react-topbar-progress-indicator'; +import { loadConfig as actionLoadConfig } from '../actions/config'; +import { loginUser as actionLoginUser, logoutUser as actionLogoutUser } from '../actions/auth'; +import { currentBackend } from '../backends/backend'; +import { showCollection, createNewEntry } from '../actions/collections'; +import { openMediaLibrary as actionOpenMediaLibrary } from '../actions/mediaLibrary'; +import Header from './Header'; +import MediaLibrary from '../components/MediaLibrary/MediaLibrary'; +import { Loader, Toast } from '../components/UI/index'; +import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper'; +import { SIMPLE, EDITORIAL_WORKFLOW } from '../constants/publishModes'; +import Collection from '../Collection/Collection'; +import EntryPage from '../containers/EntryPage'; +import SearchPage from '../containers/SearchPage'; +import NotFoundPage from '../containers/NotFoundPage'; + +TopBarProgress.config({ + barColors: { + "0": '#3a69c8', + '1.0': '#3a69c8', + }, + shadowBlur: 0, + barThickness: 2, +}); + +class App extends React.Component { + + static propTypes = { + auth: ImmutablePropTypes.map, + config: ImmutablePropTypes.map, + collections: ImmutablePropTypes.orderedMap, + logoutUser: PropTypes.func.isRequired, + dispatch: PropTypes.func.isRequired, + user: ImmutablePropTypes.map, + isFetching: PropTypes.bool.isRequired, + publishMode: PropTypes.oneOf([SIMPLE, EDITORIAL_WORKFLOW]), + siteId: PropTypes.string, + }; + + static configError(config) { + return (
+

Error loading the CMS configuration

+ +
+

The config.yml file could not be loaded or failed to parse properly.

+

Error message: {config.get('error')}

+
+
); + } + + componentDidMount() { + this.props.dispatch(actionLoadConfig()); + } + + handleLogin(credentials) { + this.props.dispatch(actionLoginUser(credentials)); + } + + authenticating() { + const { auth } = this.props; + const backend = currentBackend(this.props.config); + + if (backend == null) { + return

Waiting for backend...

; + } + + return ( +
+ { + React.createElement(backend.authComponent(), { + onLogin: this.handleLogin.bind(this), + error: auth && auth.get('error'), + isFetching: auth && auth.get('isFetching'), + siteId: this.props.config.getIn(["backend", "site_domain"]), + base_url: this.props.config.getIn(["backend", "base_url"], null) + }) + } +
+ ); + } + + handleLinkClick(event, handler, ...args) { + event.preventDefault(); + handler(...args); + } + + render() { + const { + user, + config, + collections, + logoutUser, + isFetching, + publishMode, + openMediaLibrary, + } = this.props; + + + if (config === null) { + return null; + } + + if (config.get('error')) { + return App.configError(config); + } + + if (config.get('isFetching')) { + return Loading configuration...; + } + + if (user == null) { + return this.authenticating(); + } + + return ( +
+ +
+
+ { isFetching && } +
+ + + + ()} /> + + + + + +
+
+
+ ); + } +} + +function mapStateToProps(state) { + const { auth, config, collections, globalUI } = state; + const user = auth && auth.get('user'); + const isFetching = globalUI.get('isFetching'); + const publishMode = config && config.get('publish_mode'); + return { auth, config, collections, user, isFetching, publishMode }; +} + +function mapDispatchToProps(dispatch) { + return { + dispatch, + openMediaLibrary: () => { + dispatch(actionOpenMediaLibrary()); + }, + logoutUser: () => { + dispatch(actionLogoutUser()); + }, + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(App); diff --git a/src/App/Header.css b/src/App/Header.css new file mode 100644 index 000000000000..b6f4e3e61087 --- /dev/null +++ b/src/App/Header.css @@ -0,0 +1,71 @@ +.nc-appHeader-container { + background-color: var(--backgroundColorSecondary); + grid-area: header; + display: flex; + justify-content: space-between; + padding: 0 12px; + box-shadow: var(--dropShadow); +} + +.nc-appHeader-button { + background-color: transparent; + color: #7b8290; + font-size: 15px; + font-weight: 500; + display: inline-flex; + padding: 16px 20px; + align-items: center; + + & .nc-icon { + margin-right: 4px; + color: #b3b9c4; + } + + &:hover, + &:active, + &:focus, + &.nc-appHeader-button-active { + color: var(--activeColor); + + & .nc-icon { + color: var(--activeColor); + } + } +} + +.nc-appHeader-actions { + display: inline-flex; + align-items: center; +} + +.nc-appHeader-siteLink { + font-size: 14px; + color: #7b8290; + padding: 10px 16px; +} + +.nc-appHeader-quickNew { + font-size: 12px; + font-weight: 600; + background-color: #7d8190; + padding: 6px 12px; + margin-right: 8px; + color: #fff; + + &:hover, + &:active, + &:focus { + color: #fff; + } +} + +.nc-appHeader-avatar { + border: 0; + padding: 8px; + cursor: pointer; + + & img { + width: 32px; + border-radius: 40px; + } +} diff --git a/src/App/Header.js b/src/App/Header.js new file mode 100644 index 000000000000..a160248f0c18 --- /dev/null +++ b/src/App/Header.js @@ -0,0 +1,75 @@ +import PropTypes from 'prop-types'; +import React from "react"; +import ImmutablePropTypes from "react-immutable-proptypes"; +import { NavLink } from 'react-router-dom'; +import Icon from '../icons/Icon'; + +export default class Header extends React.Component { + + static propTypes = { + user: ImmutablePropTypes.map.isRequired, + collections: ImmutablePropTypes.orderedMap.isRequired, + onCreateEntryClick: PropTypes.func.isRequired, + onLogoutClick: PropTypes.func.isRequired, + }; + + render() { + const { + user, + collections, + toggleDrawer, + onLogoutClick, + openMediaLibrary, + } = this.props; + + /** + * preserve the Quick New dropdown code + { + collections.filter(collection => collection.get('create')).toList().map(collection => + + ) + } + + + + handleCreatePostClick = (collectionName) => { + const { onCreateEntryClick } = this.props; + if (onCreateEntryClick) { + onCreateEntryClick(collectionName); + } + }; + + */ + + return ( +
+ +
+ + + olddvdscreensaver.com + + +
+
+ ); + } +} diff --git a/src/Collection/Collection.css b/src/Collection/Collection.css new file mode 100644 index 000000000000..eace33917122 --- /dev/null +++ b/src/Collection/Collection.css @@ -0,0 +1,15 @@ +@import "./Sidebar.css"; + +.nc-collectionPage-container { + display: grid; + grid-template-columns: 250px auto; + grid-template-rows: auto; + grid-gap: 28px; + grid-template-areas: + "sidebar main"; + margin: 0 18px; +} + +.nc-collectionPage-main { + grid-area: main; +} diff --git a/src/containers/CollectionPage.js b/src/Collection/Collection.js similarity index 68% rename from src/containers/CollectionPage.js rename to src/Collection/Collection.js index cd625c1b92a3..7f7c5195ce32 100644 --- a/src/containers/CollectionPage.js +++ b/src/Collection/Collection.js @@ -3,11 +3,14 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; import { loadEntries } from '../actions/entries'; +import { showCollection } from '../actions/collections'; import { selectEntries } from '../reducers'; import { Loader } from '../components/UI'; +import Sidebar from './Sidebar'; +import Entries from './Entries'; import EntryListing from '../components/EntryListing/EntryListing'; -class CollectionPage extends React.Component { +class Collection extends React.Component { static propTypes = { collection: ImmutablePropTypes.map.isRequired, @@ -19,6 +22,8 @@ class CollectionPage extends React.Component { isFetching: PropTypes.bool.isRequired, }; + state = { query: '' }; + componentDidMount() { const { collection, dispatch } = this.props; if (collection) { @@ -40,27 +45,24 @@ class CollectionPage extends React.Component { render() { const { collections, collection, publicFolder, page, entries, isFetching } = this.props; - if (collections == null) { - return

No collections defined in your config.yml

; - } - - const entriesContent = ( - {collection.get('label')} - ); - - const fetchingEntriesContent = ( - {['Loading Entries', 'Caching Entries', 'This might take several minutes']} - ); - const noEntriesContent =
No Entries
; - const fallbackContent = isFetching ? fetchingEntriesContent : noEntriesContent; + const { query } = this.state; - return (
{entries ? entriesContent : fallbackContent}
); + return ( +
+ +
+ +
+
+ ); } } @@ -78,4 +80,4 @@ function mapStateToProps(state, ownProps) { return { publicFolder, collection, collections, page, entries, isFetching }; } -export default connect(mapStateToProps)(CollectionPage); +export default connect(mapStateToProps)(Collection); diff --git a/src/Collection/Entries.js b/src/Collection/Entries.js new file mode 100644 index 000000000000..1e7034888a71 --- /dev/null +++ b/src/Collection/Entries.js @@ -0,0 +1,38 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { loadEntries } from '../actions/entries'; +import { showCollection } from '../actions/collections'; +import { selectEntries } from '../reducers'; +import { Loader } from '../components/UI'; +import Sidebar from './Sidebar'; +import EntryListing from '../components/EntryListing/EntryListing'; + +const Entries = props => { + const { entries, isFetching } = props; + const loadingMessages = [ + 'Loading Entries', + 'Caching Entries', + 'This might take several minutes', + ]; + + if (entries) { + return + } + + if (isFetching) { + return {loadingMessages}; + } + + return
No Entries
; +} + +Entries.propTypes = { + collection: ImmutablePropTypes.map.isRequired, + collections: ImmutablePropTypes.orderedMap.isRequired, + publicFolder: PropTypes.string.isRequired, + page: PropTypes.number, + entries: ImmutablePropTypes.list, +}; + +export default Entries; diff --git a/src/Collection/Sidebar.css b/src/Collection/Sidebar.css new file mode 100644 index 000000000000..ede0e5f94b93 --- /dev/null +++ b/src/Collection/Sidebar.css @@ -0,0 +1,65 @@ +.nc-collectionPage-sidebar { + @apply(--card); + padding: 8px 0 12px; + grid-area: sidebar; + align-self: start; +} + +.nc-collectionPage-sidebarHeading { + font-size: 16px; + font-weight: 600; + padding: 0; + margin: 8px 12px 12px; + color: var(--textLeadColor); +} + +.nc-collectionPage-sidebarSearch { + display: flex; + align-items: center; + background-color: #eff0f4; + border-radius: var(--borderRadius); + margin: 0 8px; + position: relative; + + & .nc-icon { + width: 18px; + height: 18px; + position: absolute; + left: 6px; + } + + & input { + border: 0; + font-size: 14px; + background-color: transparent; + padding: 10px 6px 10px 32px; + width: 100%; + position: relative; + z-index: 1; + } +} + +.nc-collectionPage-sidebarLink { + display: flex; + font-size: 14px; + font-weight: 500; + align-items: center; + padding: 8px 12px; + border-left: 2px solid #fff; + + & .nc-icon { + margin-right: 8px; + } + + &:hover, + &:active, + &.nc-collectionPage-sidebarLink-active { + color: #3a69c8; + background-color: #e8f5fe; + border-left-color: #4863c6; + } + + &:first-of-type { + margin-top: 16px; + } +} diff --git a/src/Collection/Sidebar.js b/src/Collection/Sidebar.js new file mode 100644 index 000000000000..9feeb0cf58ea --- /dev/null +++ b/src/Collection/Sidebar.js @@ -0,0 +1,53 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { NavLink } from 'react-router-dom'; +import { searchCollections } from '../actions/collections'; +import { getCollectionUrl } from '../lib/urlHelper'; +import Icon from '../icons/Icon'; + +export default class Collection extends React.Component { + + static propTypes = { + collections: ImmutablePropTypes.orderedMap.isRequired, + }; + + state = { query: '' }; + + renderLink = collection => { + const collectionName = collection.get('name'); + return ( + + + {collection.get('label')} + + ); + }; + + + render() { + const { collections } = this.props; + const { query } = this.state; + + return ( +
+

Collections

+
+ + this.setState({ query: e.target.value })} + onKeyDown={e => e.key === 'Enter' && searchCollections(query)} + placeholder="Search all" + value={query} + /> +
+ {collections.toList().map(this.renderLink)} +
+ ); + } +} diff --git a/src/actions/collections.js b/src/actions/collections.js new file mode 100644 index 000000000000..499a3d52c37c --- /dev/null +++ b/src/actions/collections.js @@ -0,0 +1,14 @@ +import history from '../routing/history'; +import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper'; + +export function searchCollections(query) { + history.push(`/search/${query}`); +} + +export function showCollection(collectionName) { + history.push(getCollectionUrl(collectionName)); +} + +export function createNewEntry(collectionName) { + history.push(getNewEntryUrl(collectionName)); +} diff --git a/src/actions/findbar.js b/src/actions/findbar.js deleted file mode 100644 index d257abc286e2..000000000000 --- a/src/actions/findbar.js +++ /dev/null @@ -1,38 +0,0 @@ -import history from '../routing/history'; -import { SEARCH } from '../components/FindBar/FindBar'; -import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper'; - -export const RUN_COMMAND = 'RUN_COMMAND'; -export const SHOW_COLLECTION = 'SHOW_COLLECTION'; -export const CREATE_COLLECTION = 'CREATE_COLLECTION'; -export const HELP = 'HELP'; - -export function runCommand(command, payload) { - return (dispatch) => { - switch (command) { - case SHOW_COLLECTION: - history.push(getCollectionUrl(payload.collectionName)); - break; - case CREATE_COLLECTION: - history.push(getNewEntryUrl(payload.collectionName)); - break; - case HELP: - window.alert('Find Bar Help (PLACEHOLDER)\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit.'); - break; - case SEARCH: - history.push(`/search/${ payload.searchTerm }`); - break; - default: - break; - } - dispatch({ type: RUN_COMMAND, command, payload }); - }; -} - -export function navigateToCollection(collectionName) { - return runCommand(SHOW_COLLECTION, { collectionName }); -} - -export function createNewEntryInCollection(collectionName) { - return runCommand(CREATE_COLLECTION, { collectionName }); -} diff --git a/src/actions/globalUI.js b/src/actions/globalUI.js deleted file mode 100644 index ca9e4088cfbf..000000000000 --- a/src/actions/globalUI.js +++ /dev/null @@ -1,13 +0,0 @@ -export const TOGGLE_SIDEBAR = 'TOGGLE_SIDEBAR'; -export const OPEN_SIDEBAR = 'OPEN_SIDEBAR'; - -export function toggleSidebar() { - return { type: TOGGLE_SIDEBAR }; -} - -export function openSidebar(open = false) { - return { - type: OPEN_SIDEBAR, - payload: { open }, - }; -} diff --git a/src/components/UI/theme.css b/src/base.css similarity index 65% rename from src/components/UI/theme.css rename to src/base.css index 29b8d5f31a9b..c2fa01cac02c 100644 --- a/src/components/UI/theme.css +++ b/src/base.css @@ -3,23 +3,26 @@ --fontFamilyMono: 'SFMono-Regular', Consolas, "Liberation Mono", Menlo, Courier, monospace; --defaultColor: #333; --defaultColorLight: #fff; - --backgroundColor: #fff; + --backgroundColor: #eff0f4; --backgroundColorShaded: #eee; + --backgroundColorSecondary: #fff; --shadowColor: rgba(19, 39, 48, .24); --infoColor: #69c; + --activeColor: #3a69c8; --primaryColor: #4990e2; --successColor: #1c7; --warningColor: #fa0; --errorColor: #f52; - --textColor: #191919; + --textColor: #798291; + --textLeadColor: #313d3e; --textMutedColor: #8c8c8c; - --borderRadius: 4px; + --borderRadius: 6px; --borderRadiusLarge: 8px; --secondaryColor: #d9d9d9; - --dropShadow: 0 2px 8px 0 var(--secondaryColor); + --dropShadow: 0 2px 4px 0 rgba(19, 39, 48, .12); --topmostZindex: 99999; --foregroundAltColor: #fff; - --backgroundAltColor: #232528; + --backgroundAltColor: #fff; --textFieldBorderColor: var(--secondaryColor); --highlightFGColor: #fff; --highlightBGColor: #3ab7a5; @@ -33,6 +36,12 @@ --border: solid var(--borderWidth) var(--secondaryColor); --textFieldBorder: var(--border); + --card: { + box-shadow: var(--dropShadow); + border-radius: 8px; + background-color: #fff; + } + --input: { font-family: 'SFMono-Regular', Consolas, "Liberation Mono", Menlo, Courier, monospace; display: block; @@ -55,6 +64,52 @@ } } +html { + box-sizing: border-box; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + margin: 0; + height: 100%; +} + +*, *:before, *:after { + box-sizing: inherit; +} + +body { + font-family: var(--fontFamilyPrimary); + height: 100%; + background-color: var(--backgroundColor); + color: var(--textColor); + margin: 0; +} + +h1, h2, h3, h4, h5, h6, p { + margin: 0; + font-family: var(--fontFamilyPrimary); +} + +h1 { + margin: 30px auto 25px; + padding-bottom: 15px; + font-size: 24px; + font-weight: 800; + color: var(--textLeadColor); + letter-spacing: 0; + line-height: 24px; +} + +a, +button { + color: var(--textColor); + cursor: pointer; + border: 0; +} + +img { + max-width: 100%; +} + .nc-theme-base { box-sizing: border-box; } diff --git a/src/components/AppHeader/AppHeader.css b/src/components/AppHeader/AppHeader.css deleted file mode 100644 index 8ee8475dc150..000000000000 --- a/src/components/AppHeader/AppHeader.css +++ /dev/null @@ -1,37 +0,0 @@ -.nc-appHeader-appBar { - padding: 8px 24px; - height: auto; - background-color: var(--backgroundAltColor); - color: var(--defaultColorLight); -} - -/* Gross stuff below, React Toolbox hacks */ - -.nc-appHeader-button, -.nc-appHeader-iconMenu { - margin-left: 16px; -} - -.nc-appHeader-button { - cursor: pointer; - border: 0; - background-color: transparent; - width: 36px; - padding: 6px 0; - text-align: center; - - & .nc-appHeader-icon { - vertical-align: top; - } -} - -.nc-appHeader-icon, -.nc-appHeader-icon span, -.nc-appHeader-leftIcon span { - /* stylelint-disable */ - - color: var(--defaultColorLight) !important; - font-size: 24px !important; - - /* stylelint-enable */ -} diff --git a/src/components/AppHeader/AppHeader.js b/src/components/AppHeader/AppHeader.js deleted file mode 100644 index 486b98e77cbc..000000000000 --- a/src/components/AppHeader/AppHeader.js +++ /dev/null @@ -1,89 +0,0 @@ -import PropTypes from 'prop-types'; -import React from "react"; -import ImmutablePropTypes from "react-immutable-proptypes"; -import { Link } from 'react-router-dom'; -import { IconMenu, Menu, MenuItem } from "react-toolbox/lib/menu"; -import Avatar from "react-toolbox/lib/avatar"; -import AppBar from "react-toolbox/lib/app_bar"; -import FontIcon from "react-toolbox/lib/font_icon"; -import FindBar from "../FindBar/FindBar"; -import { stringToRGB } from "../../lib/textHelper"; - -export default class AppHeader extends React.Component { - - static propTypes = { - user: ImmutablePropTypes.map.isRequired, - collections: ImmutablePropTypes.orderedMap.isRequired, - runCommand: PropTypes.func.isRequired, - toggleDrawer: PropTypes.func.isRequired, - onCreateEntryClick: PropTypes.func.isRequired, - onLogoutClick: PropTypes.func.isRequired, - }; - - handleCreatePostClick = (collectionName) => { - const { onCreateEntryClick } = this.props; - if (onCreateEntryClick) { - onCreateEntryClick(collectionName); - } - }; - - render() { - const { - user, - collections, - runCommand, - toggleDrawer, - onLogoutClick, - openMediaLibrary, - } = this.props; - - const avatarStyle = { - backgroundColor: `#${ stringToRGB(user.get("name")) }`, - }; - - const theme = { - appBar: 'nc-appHeader-appBar', - iconMenu: 'nc-appHeader-iconMenu', - icon: 'nc-appHeader-icon', - leftIcon: 'nc-appHeader-leftIcon', - base: 'nc-theme-base', - container: 'nc-theme-container', - rounded: 'nc-theme-rounded', - depth: 'nc-theme-depth', - clearfix: 'nc-theme-clearfix', - }; - - return ( - - - - - - - { - collections.filter(collection => collection.get('create')).toList().map(collection => - - ) - } - - - - - - - - ); - } -} diff --git a/src/components/EntryEditor/EntryEditor.css b/src/components/EntryEditor/EntryEditor.css index cefd0094af65..9457aaf780a7 100644 --- a/src/components/EntryEditor/EntryEditor.css +++ b/src/components/EntryEditor/EntryEditor.css @@ -1,3 +1,5 @@ +@import "./ReactSplitPane.css"; + /* Quick fix for preview pane not fully displaying in Safari */ .SplitPane .Pane { height: 100%; diff --git a/src/components/EntryEditor/ReactSplitPane.css b/src/components/EntryEditor/ReactSplitPane.css new file mode 100644 index 000000000000..7c8c0ed9d684 --- /dev/null +++ b/src/components/EntryEditor/ReactSplitPane.css @@ -0,0 +1,31 @@ +.Resizer { + background: #000; + opacity: .2; + z-index: 1; + box-sizing: border-box; + background-clip: padding-box; +} + +.Resizer:hover { + -webkit-transition: all 2s ease; + transition: all 2s ease; +} + +.Resizer.vertical { + width: 11px; + margin: 0 -5px; + border-left: 5px solid rgba(255, 255, 255, 0); + border-right: 5px solid rgba(255, 255, 255, 0); + cursor: col-resize; +} + +.Resizer.vertical:hover { + border-left: 5px solid rgba(0, 0, 0, 0.5); + border-right: 5px solid rgba(0, 0, 0, 0.5); +} +.Resizer.disabled { + cursor: not-allowed; +} +.Resizer.disabled:hover { + border-color: transparent; +} diff --git a/src/components/EntryListing/EntryListing.css b/src/components/EntryListing/EntryListing.css index 34ff7d71f6bd..58602141f89c 100644 --- a/src/components/EntryListing/EntryListing.css +++ b/src/components/EntryListing/EntryListing.css @@ -1,5 +1,5 @@ .nc-entryListing-card { - flex: 0 300px; + flex: 0 335px; overflow: hidden; margin-bottom: 16px; margin-left: 16px; diff --git a/src/components/EntryListing/EntryListing.js b/src/components/EntryListing/EntryListing.js index b0c12b706731..9c8ed951bbd8 100644 --- a/src/components/EntryListing/EntryListing.js +++ b/src/components/EntryListing/EntryListing.js @@ -6,11 +6,9 @@ import { Map } from 'immutable'; import history from '../../routing/history'; import { resolvePath } from '../../lib/pathHelper'; import { selectFields, selectInferedField } from '../../reducers/collections'; -import { Card } from '../UI'; export default class EntryListing extends React.Component { static propTypes = { - children: PropTypes.node.isRequired, publicFolder: PropTypes.string.isRequired, collections: PropTypes.oneOfType([ ImmutablePropTypes.map, @@ -46,7 +44,7 @@ export default class EntryListing extends React.Component { } return ( - history.push(path)} // eslint-disable-line className="nc-entryListing-card" @@ -64,7 +62,7 @@ export default class EntryListing extends React.Component {

)) } -
+ ); } renderCards = () => { @@ -83,10 +81,8 @@ export default class EntryListing extends React.Component { }; render() { - const { children } = this.props; return (
-

{children}

{ this.renderCards() } diff --git a/src/components/FindBar/FindBar.css b/src/components/FindBar/FindBar.css deleted file mode 100644 index 057a3fd55b53..000000000000 --- a/src/components/FindBar/FindBar.css +++ /dev/null @@ -1,17 +0,0 @@ -.nc-findBar-root { - flex: 1; - background-color: var(--backgroundAltColor); -} - -.nc-findBar-inputField { - width: 100%; - max-width: 500px; - display: block; - margin: 0 24px; - padding: 10px 14px; - border: 0; - border-radius: var(--borderRadius); - box-shadow: none; - outline: none; - font-size: 14px; -} diff --git a/src/components/FindBar/FindBar.js b/src/components/FindBar/FindBar.js deleted file mode 100644 index eb7e6f696214..000000000000 --- a/src/components/FindBar/FindBar.js +++ /dev/null @@ -1,56 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -export const SEARCH = 'SEARCH'; -const PLACEHOLDER = 'Search entry titles...'; - -class FindBar extends Component { - static propTypes = { - runCommand: PropTypes.func.isRequired, - }; - - constructor() { - super(); - this._searchCommand = { - regexp: `(?:${ SEARCH })?(.*)`, - param: { name: 'searchTerm', display: '' }, - }; - this.state = { - value: '', - placeholder: PLACEHOLDER, - }; - } - - search = () => { - const string = this.state.value; - const command = this._searchCommand; - const match = string.match(RegExp(`^${ this._searchCommand.regexp }`, 'i')); - const enteredParamValue = command && command.param && match[1] ? match[1].trim() : null; - - if (enteredParamValue) { - this.props.runCommand(SEARCH, { searchTerm: enteredParamValue }); - } - }; - - handleKeyDown = event => (event.key === 'Enter' && this.search()); - handleChange = event => this.setState({ value: event.target.value }); - - render() { - return ( -
-
- ); - } -} - -export default FindBar; diff --git a/src/components/Widgets.js b/src/components/Widgets.js index db0d6d18a359..daf840a3d343 100644 --- a/src/components/Widgets.js +++ b/src/components/Widgets.js @@ -17,14 +17,14 @@ import FileControl from './Widgets/FileControl'; import FilePreview from './Widgets/FilePreview'; import DateControl from './Widgets/DateControl'; import DatePreview from './Widgets/DatePreview'; -import DateTimeControl from './Widgets/DateTimeControl'; -import DateTimePreview from './Widgets/DateTimePreview'; +import DateTimeControl from './Widgets/DateTime/DateTimeControl'; +import DateTimePreview from './Widgets/DateTime/DateTimePreview'; import SelectControl from './Widgets/SelectControl'; import SelectPreview from './Widgets/SelectPreview'; import ObjectControl from './Widgets/ObjectControl'; import ObjectPreview from './Widgets/ObjectPreview'; -import RelationControl from './Widgets/RelationControl'; -import RelationPreview from './Widgets/RelationPreview'; +import RelationControl from './Widgets/Relation/RelationControl'; +import RelationPreview from './Widgets/Relation/RelationPreview'; import BooleanControl from './Widgets/BooleanControl'; diff --git a/src/components/Widgets/DateTime/DateTime.css b/src/components/Widgets/DateTime/DateTime.css new file mode 100644 index 000000000000..41b83e5c917a --- /dev/null +++ b/src/components/Widgets/DateTime/DateTime.css @@ -0,0 +1 @@ +@import "./ReactDatetime.css"; diff --git a/src/components/Widgets/DateTimeControl.js b/src/components/Widgets/DateTime/DateTimeControl.js similarity index 100% rename from src/components/Widgets/DateTimeControl.js rename to src/components/Widgets/DateTime/DateTimeControl.js diff --git a/src/components/Widgets/DateTimePreview.js b/src/components/Widgets/DateTime/DateTimePreview.js similarity index 83% rename from src/components/Widgets/DateTimePreview.js rename to src/components/Widgets/DateTime/DateTimePreview.js index c6cc85892144..28399433596e 100644 --- a/src/components/Widgets/DateTimePreview.js +++ b/src/components/Widgets/DateTime/DateTimePreview.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import previewStyle from './defaultPreviewStyle'; +import previewStyle from '../defaultPreviewStyle'; export default function DateTimePreview({ value }) { return
{value ? value.toString() : null}
; diff --git a/src/components/Widgets/DateTime/ReactDatetime.css b/src/components/Widgets/DateTime/ReactDatetime.css new file mode 100644 index 000000000000..f8df4a94fe4c --- /dev/null +++ b/src/components/Widgets/DateTime/ReactDatetime.css @@ -0,0 +1,210 @@ +.rdt { + position: relative; +} +.rdtPicker { + display: none; + position: absolute; + width: 250px; + padding: 4px; + margin-top: 1px; + z-index: 99999 !important; + background: #fff; + border: 2px solid var(--secondaryColor); + border-radius: 2px; + box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .16); +} +.rdtOpen .rdtPicker { + display: block; +} +.rdtStatic .rdtPicker { + box-shadow: none; + position: static; +} + +.rdtPicker .rdtTimeToggle { + text-align: center; +} + +.rdtPicker table { + width: 100%; + margin: 0; +} +.rdtPicker td, +.rdtPicker th { + text-align: center; + height: 28px; +} +.rdtPicker td { + cursor: pointer; +} +.rdtPicker td.rdtDay:hover, +.rdtPicker td.rdtHour:hover, +.rdtPicker td.rdtMinute:hover, +.rdtPicker td.rdtSecond:hover, +.rdtPicker .rdtTimeToggle:hover { + background: #eeeeee; + cursor: pointer; +} +.rdtPicker td.rdtOld, +.rdtPicker td.rdtNew { + color: #999999; +} +.rdtPicker td.rdtToday { + position: relative; +} +.rdtPicker td.rdtToday:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-bottom: 7px solid #428bca; + border-top-color: rgba(0, 0, 0, 0.2); + position: absolute; + bottom: 4px; + right: 4px; +} +.rdtPicker td.rdtActive, +.rdtPicker td.rdtActive:hover { + background-color: #428bca; + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.rdtPicker td.rdtActive.rdtToday:before { + border-bottom-color: #fff; +} +.rdtPicker td.rdtDisabled, +.rdtPicker td.rdtDisabled:hover { + background: none; + color: #999999; + cursor: not-allowed; +} + +.rdtPicker td span.rdtOld { + color: #999999; +} +.rdtPicker td span.rdtDisabled, +.rdtPicker td span.rdtDisabled:hover { + background: none; + color: #999999; + cursor: not-allowed; +} +.rdtPicker th { + border-bottom: 1px solid #f9f9f9; +} +.rdtPicker .dow { + width: 14.2857%; + border-bottom: none; +} +.rdtPicker th.rdtSwitch { + width: 100px; +} +.rdtPicker th.rdtNext, +.rdtPicker th.rdtPrev { + font-size: 21px; + vertical-align: top; +} + +.rdtPrev span, +.rdtNext span { + display: block; + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -khtml-user-select: none; /* Konqueror */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; +} + +.rdtPicker th.rdtDisabled, +.rdtPicker th.rdtDisabled:hover { + background: none; + color: #999999; + cursor: not-allowed; +} +.rdtPicker thead tr:first-child th { + cursor: pointer; +} +.rdtPicker thead tr:first-child th:hover { + background: #eeeeee; +} + +.rdtPicker tfoot { + border-top: 1px solid #f9f9f9; +} + +.rdtPicker button { + border: none; + background: none; + cursor: pointer; +} +.rdtPicker button:hover { + background-color: #eee; +} + +.rdtPicker thead button { + width: 100%; + height: 100%; +} + +td.rdtMonth, +td.rdtYear { + height: 50px; + width: 25%; + cursor: pointer; +} +td.rdtMonth:hover, +td.rdtYear:hover { + background: #eee; +} + +.rdtCounters { + display: inline-block; +} + +.rdtCounters > div { + float: left; +} + +.rdtCounter { + height: 100px; +} + +.rdtCounter { + width: 40px; +} + +.rdtCounterSeparator { + line-height: 100px; +} + +.rdtCounter .rdtBtn { + height: 40%; + line-height: 40px; + cursor: pointer; + display: block; + + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -khtml-user-select: none; /* Konqueror */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; +} +.rdtCounter .rdtBtn:hover { + background: #eee; +} +.rdtCounter .rdtCount { + height: 20%; + font-size: 1.2em; +} + +.rdtMilli { + vertical-align: middle; + padding-left: 8px; + width: 48px; +} + +.rdtMilli input { + width: 100%; + font-size: 1.2em; + margin-top: 37px; +} diff --git a/src/components/Widgets/Relation/ReactAutosuggest.css b/src/components/Widgets/Relation/ReactAutosuggest.css new file mode 100644 index 000000000000..324758a0c26c --- /dev/null +++ b/src/components/Widgets/Relation/ReactAutosuggest.css @@ -0,0 +1,57 @@ +.react-autosuggest__container { + position: relative; +} + +.react-autosuggest__input { + width: 240px; + height: 30px; + padding: 10px 20px; + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border: 1px solid #aaa; + border-radius: 4px; +} + +.react-autosuggest__input:focus { + outline: none; +} + +.react-autosuggest__container--open .react-autosuggest__input { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.react-autosuggest__suggestions-container { + display: none; +} + +.react-autosuggest__container--open .react-autosuggest__suggestions-container { + display: block; + position: absolute; + top: 51px; + width: 100%; + border: 1px solid #aaa; + background-color: #fff; + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + z-index: 2; +} + +.react-autosuggest__suggestions-list { + margin: 0; + padding: 0; + list-style-type: none; +} + +.react-autosuggest__suggestion { + cursor: pointer; + padding: 10px 20px; +} + +.react-autosuggest__suggestion--focused { + background-color: #ddd; +} diff --git a/src/components/Widgets/Relation/Relation.css b/src/components/Widgets/Relation/Relation.css new file mode 100644 index 000000000000..2556f6737ffa --- /dev/null +++ b/src/components/Widgets/Relation/Relation.css @@ -0,0 +1 @@ +@import "./ReactAutosuggest.css"; diff --git a/src/components/Widgets/RelationControl.js b/src/components/Widgets/Relation/RelationControl.js similarity index 97% rename from src/components/Widgets/RelationControl.js rename to src/components/Widgets/Relation/RelationControl.js index 0b7a15c99181..ccc864be9a78 100644 --- a/src/components/Widgets/RelationControl.js +++ b/src/components/Widgets/Relation/RelationControl.js @@ -5,8 +5,8 @@ import uuid from 'uuid/v4'; import { Map } from 'immutable'; import { connect } from 'react-redux'; import { debounce } from 'lodash'; -import { Loader } from '../../components/UI/index'; -import { query, clearSearch } from '../../actions/search'; +import { Loader } from '../../../components/UI/index'; +import { query, clearSearch } from '../../../actions/search'; function escapeRegexCharacters(str) { diff --git a/src/components/Widgets/RelationPreview.js b/src/components/Widgets/Relation/RelationPreview.js similarity index 81% rename from src/components/Widgets/RelationPreview.js rename to src/components/Widgets/Relation/RelationPreview.js index 1924708dc48a..7af6809b5030 100644 --- a/src/components/Widgets/RelationPreview.js +++ b/src/components/Widgets/Relation/RelationPreview.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import previewStyle from './defaultPreviewStyle'; +import previewStyle from '../defaultPreviewStyle'; export default function RelationPreview({ value }) { return
{ value }
; diff --git a/src/components/stories/FindBar.js b/src/components/stories/FindBar.js deleted file mode 100644 index 277ec61e4f19..000000000000 --- a/src/components/stories/FindBar.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { storiesOf, action } from '@kadira/storybook'; - -import FindBar from '../FindBar/FindBar'; - -const style = { - width: 800, - margin: 20, -}; - -storiesOf('FindBar', module) - .add('Default View', () => ( -
- -
- )); diff --git a/src/components/stories/index.js b/src/components/stories/index.js index c270c754a216..7fea2d84ad0c 100644 --- a/src/components/stories/index.js +++ b/src/components/stories/index.js @@ -1,5 +1,4 @@ import './Card'; import './Icon'; import './Toast'; -import './FindBar'; import './ScrollSync'; diff --git a/src/containers/App.css b/src/containers/App.css deleted file mode 100644 index 9c3d7fa80aa1..000000000000 --- a/src/containers/App.css +++ /dev/null @@ -1,4 +0,0 @@ -.nc-app-entriesPanel { - padding: 0 40px; -} - diff --git a/src/containers/CollectionPage.css b/src/containers/CollectionPage.css deleted file mode 100644 index 349676e26430..000000000000 --- a/src/containers/CollectionPage.css +++ /dev/null @@ -1,10 +0,0 @@ -.nc-collectionPage-noEntries { - position: absolute; - top: 50%; - left: 50%; - margin: 0px; - text-align: center; - z-index: 1000; - transform: translateX(-50%) translateY(-50%); - margin-top: 28px; -} diff --git a/src/containers/EntryPage.js b/src/containers/EntryPage.js index f3bfc186b21d..a0f338934ba6 100644 --- a/src/containers/EntryPage.js +++ b/src/containers/EntryPage.js @@ -17,7 +17,6 @@ import { closeEntry } from '../actions/editor'; import { deserializeValues } from '../lib/serializeEntryValues'; import { addAsset, removeAsset } from '../actions/media'; import { openMediaLibrary } from '../actions/mediaLibrary'; -import { openSidebar } from '../actions/globalUI'; import { selectEntry, getAsset } from '../reducers'; import { selectFields } from '../reducers/collections'; import EntryEditor from '../components/EntryEditor/EntryEditor'; @@ -44,7 +43,6 @@ class EntryPage extends React.Component { openMediaLibrary: PropTypes.func.isRequired, removeAsset: PropTypes.func.isRequired, closeEntry: PropTypes.func.isRequired, - openSidebar: PropTypes.func.isRequired, fields: ImmutablePropTypes.list.isRequired, slug: PropTypes.string, newEntry: PropTypes.bool.isRequired, @@ -52,7 +50,6 @@ class EntryPage extends React.Component { componentDidMount() { const { entry, newEntry, collection, slug, loadEntry, createEmptyDraft } = this.props; - this.props.openSidebar(); if (newEntry) { createEmptyDraft(collection); } else { @@ -222,6 +219,5 @@ export default connect( persistEntry, deleteEntry, closeEntry, - openSidebar, } )(entryPageHOC(EntryPage)); diff --git a/src/containers/Sidebar.css b/src/containers/Sidebar.css deleted file mode 100644 index 4591ffc29487..000000000000 --- a/src/containers/Sidebar.css +++ /dev/null @@ -1,47 +0,0 @@ -.nc-sidebar-root { - margin-top: 54px; -} - -.nc-sidebar-sidebar { - padding: 0 16px; - width: 220px; - background-color: var(--backgroundAltColor); - color: var(--defaultColorLight); -} - -.nc-sidebar-heading { - margin-bottom: 8px; - color: var(--defaultColorLight); -} - -.nc-sidebar-nav { - padding: 0; -} - -.nc-sidebar-linkWrapper { - color: #fff; - border-radius: var(--borderRadius); - display: flex; - align-items: center; - justify-content: space-between; - - &:hover { - background-color: var(--textColor); - } -} - -.nc-sidebar-viewEntriesLink { - font-size: 18px; - color: inherit; - padding: 10px 6px; - display: block; - width: 100%; -} - -.nc-sidebar-createEntryLink { - color: inherit; - padding: 0 6px; - display: block; - position: relative; - top: 2px; -} diff --git a/src/containers/Sidebar.js b/src/containers/Sidebar.js deleted file mode 100644 index b437d59827c8..000000000000 --- a/src/containers/Sidebar.js +++ /dev/null @@ -1,63 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; -import ReactSidebar from 'react-sidebar'; -import _ from 'lodash'; -import { openSidebar } from '../actions/globalUI'; - -class Sidebar extends React.Component { - - static propTypes = { - children: PropTypes.node.isRequired, - content: PropTypes.node.isRequired, - sidebarIsOpen: PropTypes.bool.isRequired, - openSidebar: PropTypes.func.isRequired, - }; - - state = { sidebarDocked: false }; - - componentWillMount() { - this.mql = window.matchMedia('(min-width: 1200px)'); - this.mql.addListener(this.mediaQueryChanged); - this.setState({ sidebarDocked: this.mql.matches }); - } - - componentWillUnmount() { - this.mql.removeListener(this.mediaQueryChanged); - } - - mediaQueryChanged = _.throttle(() => { - this.setState({ sidebarDocked: this.mql.matches }); - }, 500); - - - render() { - const { - children, - content, - sidebarIsOpen, - openSidebar, - } = this.props; - - return ( - - {children} - - ); - } -} - -function mapStateToProps(state) { - const { globalUI } = state; - const sidebarIsOpen = globalUI.get('sidebarIsOpen'); - return { sidebarIsOpen }; -} - -export default connect(mapStateToProps, { openSidebar })(Sidebar); diff --git a/src/icons/Icon.css b/src/icons/Icon.css new file mode 100644 index 000000000000..30531a7e450c --- /dev/null +++ b/src/icons/Icon.css @@ -0,0 +1,10 @@ +.nc-icon { + width: 24px; + height: 24px; + display: inline-block; + + & path, + & circle { + fill: currentColor; + } +} diff --git a/src/icons/Icon.js b/src/icons/Icon.js new file mode 100644 index 000000000000..2957c9cb885d --- /dev/null +++ b/src/icons/Icon.js @@ -0,0 +1,45 @@ +import React from 'react'; +import iconAdd from './icon-add.svg'; +import iconCircle from './icon-circle.svg'; +import iconFolder from './icon-folder.svg'; +import iconGrid from './icon-grid.svg'; +import iconHome from './icon-home.svg'; +import iconList from './icon-list.svg'; +import iconMedia from './icon-media.svg'; +import iconMediaAlt from './icon-media-alt.svg'; +import iconPage from './icon-page.svg'; +import iconPages from './icon-pages.svg'; +import iconPagesAlt from './icon-pages-alt.svg'; +import iconSettings from './icon-settings.svg'; +import iconWorkflow from './icon-workflow.svg'; +import iconWrite from './icon-write.svg'; + +const svgIcons = { + 'add': iconAdd, + 'circle': iconCircle, + 'folder': iconFolder, + 'grid': iconGrid, + 'home': iconHome, + 'list': iconList, + 'media': iconMedia, + 'media-alt': iconMediaAlt, + 'page': iconPage, + 'pages': iconPages, + 'pages-alt': iconPagesAlt, + 'settings': iconSettings, + 'workflow': iconWorkflow, + 'write': iconWrite, +}; + +const Icon = props => { + const { type, className = '', ...remainingProps } = props; + return ( + + ); +} + +export default Icon; diff --git a/src/icons/icon-add.svg b/src/icons/icon-add.svg new file mode 100644 index 000000000000..f3b243470795 --- /dev/null +++ b/src/icons/icon-add.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-circle.svg b/src/icons/icon-circle.svg new file mode 100644 index 000000000000..4c797c797fd3 --- /dev/null +++ b/src/icons/icon-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-folder.svg b/src/icons/icon-folder.svg new file mode 100644 index 000000000000..019402797a32 --- /dev/null +++ b/src/icons/icon-folder.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-grid.svg b/src/icons/icon-grid.svg new file mode 100644 index 000000000000..68f1150dbf4c --- /dev/null +++ b/src/icons/icon-grid.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-home.svg b/src/icons/icon-home.svg new file mode 100644 index 000000000000..2198f782dbc5 --- /dev/null +++ b/src/icons/icon-home.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-list.svg b/src/icons/icon-list.svg new file mode 100644 index 000000000000..118886529ab6 --- /dev/null +++ b/src/icons/icon-list.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-media-alt.svg b/src/icons/icon-media-alt.svg new file mode 100644 index 000000000000..f36d1ca8d0a9 --- /dev/null +++ b/src/icons/icon-media-alt.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-media.svg b/src/icons/icon-media.svg new file mode 100644 index 000000000000..0a402deb59cc --- /dev/null +++ b/src/icons/icon-media.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-page.svg b/src/icons/icon-page.svg new file mode 100644 index 000000000000..947013c3385f --- /dev/null +++ b/src/icons/icon-page.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-pages-alt.svg b/src/icons/icon-pages-alt.svg new file mode 100644 index 000000000000..c884fc850572 --- /dev/null +++ b/src/icons/icon-pages-alt.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/icons/icon-pages.svg b/src/icons/icon-pages.svg new file mode 100644 index 000000000000..8a18c946bb02 --- /dev/null +++ b/src/icons/icon-pages.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/icons/icon-settings.svg b/src/icons/icon-settings.svg new file mode 100644 index 000000000000..dfcafd55f310 --- /dev/null +++ b/src/icons/icon-settings.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-workflow.svg b/src/icons/icon-workflow.svg new file mode 100644 index 000000000000..11f3d4abe6b7 --- /dev/null +++ b/src/icons/icon-workflow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-write.svg b/src/icons/icon-write.svg new file mode 100644 index 000000000000..40703cb5a4d8 --- /dev/null +++ b/src/icons/icon-write.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/index.css b/src/index.css index b26c820160b6..40893757306b 100644 --- a/src/index.css +++ b/src/index.css @@ -1,15 +1,17 @@ @import "react-toolbox-global.css"; @import "./material-icons.css"; -@import "./components/UI/theme.css"; +@import "./base.css"; +@import "./icons/Icon.css"; @import "./backends/git-gateway/AuthenticationPage.css"; @import "./backends/github/AuthenticationPage.css"; -@import "./components/AppHeader/AppHeader.css"; +@import "./App/App.css"; +@import "./Collection/Collection.css"; +@import "./components/MediaLibrary/MediaLibrary.css"; @import "./components/ControlPanel/ControlPane.css"; @import "./components/EntryEditor/EntryEditor.css"; @import "./components/EntryListing/EntryListing.css"; -@import "./components/FindBar/FindBar.css"; @import "./components/PreviewPane/PreviewPane.css"; @import "./components/UI/Sticky/Sticky.css"; @import "./components/UI/card/Card.css"; @@ -30,348 +32,6 @@ @import "./components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarPluginForm.css"; @import "./components/Widgets/Markdown/MarkdownControl/VisualEditor/index.css"; @import "./components/Widgets/ObjectControl.css"; +@import "./components/Widgets/Relation/Relation.css"; +@import "./components/Widgets/DateTime/DateTime.css"; -@import "./containers/App.css"; -@import "./containers/CollectionPage.css"; -@import "./containers/Sidebar.css"; -@import "./components/MediaLibrary/MediaLibrary.css"; - -html { - box-sizing: border-box; - -ms-text-size-adjust: 100%; - -webkit-text-size-adjust: 100%; - margin: 0; -} - -*, *:before, *:after { - box-sizing: inherit; -} - -body { - font-family: var(--fontFamilyPrimary); - height: 100%; - background-color: #fff; - color: #7c8382; - margin: 0; -} - -h1, h2, h3, h4, h5, h6, p { - margin: 0; - font-family: var(--fontFamilyPrimary); -} - -h1 { - margin: 30px auto 25px; - padding-bottom: 15px; - font-size: 24px; - font-weight: 800; - color: #313D3E; - letter-spacing: 0; - line-height: 24px; -} - -img { - max-width: 100%; -} - -.react-autosuggest__container { - position: relative; -} - -.react-autosuggest__input { - width: 240px; - height: 30px; - padding: 10px 20px; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; - border: 1px solid #aaa; - border-radius: 4px; -} - -.react-autosuggest__input:focus { - outline: none; -} - -.react-autosuggest__container--open .react-autosuggest__input { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.react-autosuggest__suggestions-container { - display: none; -} - -.react-autosuggest__container--open .react-autosuggest__suggestions-container { - display: block; - position: absolute; - top: 51px; - width: 100%; - border: 1px solid #aaa; - background-color: #fff; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - z-index: 2; -} - -.react-autosuggest__suggestions-list { - margin: 0; - padding: 0; - list-style-type: none; -} - -.react-autosuggest__suggestion { - cursor: pointer; - padding: 10px 20px; -} - -.react-autosuggest__suggestion--focused { - background-color: #ddd; -} - -.Resizer { - background: #000; - opacity: .2; - z-index: 1; - box-sizing: border-box; - background-clip: padding-box; -} - -.Resizer:hover { - -webkit-transition: all 2s ease; - transition: all 2s ease; -} - -.Resizer.vertical { - width: 11px; - margin: 0 -5px; - border-left: 5px solid rgba(255, 255, 255, 0); - border-right: 5px solid rgba(255, 255, 255, 0); - cursor: col-resize; -} - -.Resizer.vertical:hover { - border-left: 5px solid rgba(0, 0, 0, 0.5); - border-right: 5px solid rgba(0, 0, 0, 0.5); -} -.Resizer.disabled { - cursor: not-allowed; -} -.Resizer.disabled:hover { - border-color: transparent; -} - - -.rdt { - position: relative; -} -.rdtPicker { - display: none; - position: absolute; - width: 250px; - padding: 4px; - margin-top: 1px; - z-index: 99999 !important; - background: #fff; - border: 2px solid var(--secondaryColor); - border-radius: 2px; - box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .16); -} -.rdtOpen .rdtPicker { - display: block; -} -.rdtStatic .rdtPicker { - box-shadow: none; - position: static; -} - -.rdtPicker .rdtTimeToggle { - text-align: center; -} - -.rdtPicker table { - width: 100%; - margin: 0; -} -.rdtPicker td, -.rdtPicker th { - text-align: center; - height: 28px; -} -.rdtPicker td { - cursor: pointer; -} -.rdtPicker td.rdtDay:hover, -.rdtPicker td.rdtHour:hover, -.rdtPicker td.rdtMinute:hover, -.rdtPicker td.rdtSecond:hover, -.rdtPicker .rdtTimeToggle:hover { - background: #eeeeee; - cursor: pointer; -} -.rdtPicker td.rdtOld, -.rdtPicker td.rdtNew { - color: #999999; -} -.rdtPicker td.rdtToday { - position: relative; -} -.rdtPicker td.rdtToday:before { - content: ''; - display: inline-block; - border-left: 7px solid transparent; - border-bottom: 7px solid #428bca; - border-top-color: rgba(0, 0, 0, 0.2); - position: absolute; - bottom: 4px; - right: 4px; -} -.rdtPicker td.rdtActive, -.rdtPicker td.rdtActive:hover { - background-color: #428bca; - color: #fff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); -} -.rdtPicker td.rdtActive.rdtToday:before { - border-bottom-color: #fff; -} -.rdtPicker td.rdtDisabled, -.rdtPicker td.rdtDisabled:hover { - background: none; - color: #999999; - cursor: not-allowed; -} - -.rdtPicker td span.rdtOld { - color: #999999; -} -.rdtPicker td span.rdtDisabled, -.rdtPicker td span.rdtDisabled:hover { - background: none; - color: #999999; - cursor: not-allowed; -} -.rdtPicker th { - border-bottom: 1px solid #f9f9f9; -} -.rdtPicker .dow { - width: 14.2857%; - border-bottom: none; -} -.rdtPicker th.rdtSwitch { - width: 100px; -} -.rdtPicker th.rdtNext, -.rdtPicker th.rdtPrev { - font-size: 21px; - vertical-align: top; -} - -.rdtPrev span, -.rdtNext span { - display: block; - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Chrome/Safari/Opera */ - -khtml-user-select: none; /* Konqueror */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; -} - -.rdtPicker th.rdtDisabled, -.rdtPicker th.rdtDisabled:hover { - background: none; - color: #999999; - cursor: not-allowed; -} -.rdtPicker thead tr:first-child th { - cursor: pointer; -} -.rdtPicker thead tr:first-child th:hover { - background: #eeeeee; -} - -.rdtPicker tfoot { - border-top: 1px solid #f9f9f9; -} - -.rdtPicker button { - border: none; - background: none; - cursor: pointer; -} -.rdtPicker button:hover { - background-color: #eee; -} - -.rdtPicker thead button { - width: 100%; - height: 100%; -} - -td.rdtMonth, -td.rdtYear { - height: 50px; - width: 25%; - cursor: pointer; -} -td.rdtMonth:hover, -td.rdtYear:hover { - background: #eee; -} - -.rdtCounters { - display: inline-block; -} - -.rdtCounters > div { - float: left; -} - -.rdtCounter { - height: 100px; -} - -.rdtCounter { - width: 40px; -} - -.rdtCounterSeparator { - line-height: 100px; -} - -.rdtCounter .rdtBtn { - height: 40%; - line-height: 40px; - cursor: pointer; - display: block; - - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Chrome/Safari/Opera */ - -khtml-user-select: none; /* Konqueror */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; -} -.rdtCounter .rdtBtn:hover { - background: #eee; -} -.rdtCounter .rdtCount { - height: 20%; - font-size: 1.2em; -} - -.rdtMilli { - vertical-align: middle; - padding-left: 8px; - width: 48px; -} - -.rdtMilli input { - width: 100%; - font-size: 1.2em; - margin-top: 37px; -} diff --git a/src/reducers/globalUI.js b/src/reducers/globalUI.js index ebe8a4179b77..68a4a37134ae 100644 --- a/src/reducers/globalUI.js +++ b/src/reducers/globalUI.js @@ -1,9 +1,8 @@ import { Map } from 'immutable'; -import { TOGGLE_SIDEBAR, OPEN_SIDEBAR } from '../actions/globalUI'; /* * Reducer for some global UI state that we want to share between components * */ -const globalUI = (state = Map({ isFetching: false, sidebarIsOpen: true }), action) => { +const globalUI = (state = Map({ isFetching: false }), action) => { // Generic, global loading indicator if ((action.type.indexOf('REQUEST') > -1)) { return state.set('isFetching', true); @@ -13,15 +12,7 @@ const globalUI = (state = Map({ isFetching: false, sidebarIsOpen: true }), actio ) { return state.set('isFetching', false); } - - switch (action.type) { - case TOGGLE_SIDEBAR: - return state.set('sidebarIsOpen', !state.get('sidebarIsOpen')); - case OPEN_SIDEBAR: - return state.set('sidebarIsOpen', action.payload.open); - default: - return state; - } + return state; }; export default globalUI; diff --git a/src/root.js b/src/root.js index 1fb54a998893..222fd6f40330 100644 --- a/src/root.js +++ b/src/root.js @@ -5,7 +5,7 @@ import { ConnectedRouter } from 'react-router-redux'; import history from './routing/history'; import configureStore from './redux/configureStore'; import { setStore } from './valueObjects/AssetProxy'; -import App from './containers/App'; +import App from './App/App'; const store = configureStore(); diff --git a/webpack.base.js b/webpack.base.js index 08f246c364e8..acf5fa9f9fd1 100644 --- a/webpack.base.js +++ b/webpack.base.js @@ -60,7 +60,13 @@ module.exports = { }), }, { - test: /\.(png|eot|woff|woff2|ttf|svg|gif)(\?v=\d+\.\d+\.\d+)?$/, + include: [/src\/icons/], + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + loader: 'svg-inline-loader', + }, + { + exclude: [/src\/icons/], + test: /\.(png|eot|woff|woff2|ttf|gif|svg)(\?v=\d+\.\d+\.\d+)?$/, use: { loader: "url-loader", options: { limit: 10000 } }, }, ], diff --git a/website/site/content/docs/editorial-workflow.md b/website/site/content/docs/editorial-workflow.md index 089369ef7297..aa95335fd04f 100755 --- a/website/site/content/docs/editorial-workflow.md +++ b/website/site/content/docs/editorial-workflow.md @@ -36,9 +36,3 @@ There are no other configuration options right now. There are always three possi Netlify CMS embraces the idea of Git-as-backend for storing metadata. The first time it runs with the editorial_workflow setup, it creates a new ref called `meta/_netlify_cms`, pointing to an empty, orphan tree. Actual data are stored in individual `json` files committed to this tree. - -## Implementation - -Instead of adding logic to `CollectionPage` and `EntryPage`, the Editorial Workflow is implemented as Higher Order Components, adding UI and dispatching additional actions. - -Furthermore, all editorial workflow state is managed in Redux - there's an `actions/editorialWorkflow.js` file and a `reducers/editorialWorkflow.js` file. \ No newline at end of file From 058b3e53ae6006bd8881acd075ed22041bbe7ffe Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Sun, 22 Oct 2017 23:58:25 -0400 Subject: [PATCH 002/134] update collection entries UI --- src/App/Header.css | 1 + src/Collection/Collection.css | 8 ++-- src/Collection/Collection.js | 22 ++++----- src/Collection/Entries.js | 9 +--- src/Collection/Top.css | 18 +++++++ src/Collection/Top.js | 14 ++++++ src/base.css | 26 ++++++++--- src/components/EntryListing/EntryListing.css | 49 +++++++++++++------- src/components/EntryListing/EntryListing.js | 23 +++++---- 9 files changed, 114 insertions(+), 56 deletions(-) create mode 100644 src/Collection/Top.css create mode 100644 src/Collection/Top.js diff --git a/src/App/Header.css b/src/App/Header.css index b6f4e3e61087..fc8a53d90cf2 100644 --- a/src/App/Header.css +++ b/src/App/Header.css @@ -63,6 +63,7 @@ border: 0; padding: 8px; cursor: pointer; + background-color: transparent; & img { width: 32px; diff --git a/src/Collection/Collection.css b/src/Collection/Collection.css index eace33917122..b5db5f85bed0 100644 --- a/src/Collection/Collection.css +++ b/src/Collection/Collection.css @@ -1,15 +1,13 @@ @import "./Sidebar.css"; +@import "./Top.css"; .nc-collectionPage-container { display: grid; grid-template-columns: 250px auto; - grid-template-rows: auto; + grid-template-rows: auto auto; grid-gap: 28px; grid-template-areas: + "sidebar top" "sidebar main"; margin: 0 18px; } - -.nc-collectionPage-main { - grid-area: main; -} diff --git a/src/Collection/Collection.js b/src/Collection/Collection.js index 7f7c5195ce32..5550b71f665b 100644 --- a/src/Collection/Collection.js +++ b/src/Collection/Collection.js @@ -7,6 +7,7 @@ import { showCollection } from '../actions/collections'; import { selectEntries } from '../reducers'; import { Loader } from '../components/UI'; import Sidebar from './Sidebar'; +import Top from './Top'; import Entries from './Entries'; import EntryListing from '../components/EntryListing/EntryListing'; @@ -50,17 +51,16 @@ class Collection extends React.Component { return (
-
- -
+ +
); } diff --git a/src/Collection/Entries.js b/src/Collection/Entries.js index 1e7034888a71..6829337fec89 100644 --- a/src/Collection/Entries.js +++ b/src/Collection/Entries.js @@ -1,15 +1,10 @@ import PropTypes from 'prop-types'; import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { loadEntries } from '../actions/entries'; -import { showCollection } from '../actions/collections'; -import { selectEntries } from '../reducers'; import { Loader } from '../components/UI'; -import Sidebar from './Sidebar'; import EntryListing from '../components/EntryListing/EntryListing'; -const Entries = props => { - const { entries, isFetching } = props; +const Entries = ({ collections, entries, publicFolder, page, onPaginate, isFetching }) => { const loadingMessages = [ 'Loading Entries', 'Caching Entries', @@ -17,7 +12,7 @@ const Entries = props => { ]; if (entries) { - return + return } if (isFetching) { diff --git a/src/Collection/Top.css b/src/Collection/Top.css new file mode 100644 index 000000000000..51c010b75828 --- /dev/null +++ b/src/Collection/Top.css @@ -0,0 +1,18 @@ +.nc-collectionPage-top { + @apply(--card); + grid-area: top; + display: flex; + align-items: center; + justify-content: space-between; + max-width: 682px; + padding: 16px 20px; +} + +.nc-collectionPage-topHeading { + font-size: 20px; + font-weight: 600; + margin: 12px 0 0; +} + +.nc-collectionPage-top-button { +} diff --git a/src/Collection/Top.js b/src/Collection/Top.js new file mode 100644 index 000000000000..9609dd8af048 --- /dev/null +++ b/src/Collection/Top.js @@ -0,0 +1,14 @@ +import PropTypes from 'prop-types'; +import React from 'react'; + +const Top = ({ collectionName }) => +
+

{collectionName}

+ +
; + +Top.propTypes = { + collectionName: PropTypes.string.isRequired, +}; + +export default Top; diff --git a/src/base.css b/src/base.css index c2fa01cac02c..f7a61c7cfcbb 100644 --- a/src/base.css +++ b/src/base.css @@ -9,16 +9,16 @@ --shadowColor: rgba(19, 39, 48, .24); --infoColor: #69c; --activeColor: #3a69c8; + --secondaryColor: #798291; --primaryColor: #4990e2; --successColor: #1c7; --warningColor: #fa0; --errorColor: #f52; - --textColor: #798291; + --textColor: var(--secondaryColor); --textLeadColor: #313d3e; --textMutedColor: #8c8c8c; --borderRadius: 6px; --borderRadiusLarge: 8px; - --secondaryColor: #d9d9d9; --dropShadow: 0 2px 4px 0 rgba(19, 39, 48, .12); --topmostZindex: 99999; --foregroundAltColor: #fff; @@ -35,6 +35,8 @@ --borderWidth: 2px; --border: solid var(--borderWidth) var(--secondaryColor); --textFieldBorder: var(--border); + --buttonTextColor: #fff; + --buttonBackgroundColor: var(--secondaryColor); --card: { box-shadow: var(--dropShadow); @@ -85,8 +87,11 @@ body { } h1, h2, h3, h4, h5, h6, p { - margin: 0; font-family: var(--fontFamilyPrimary); + color: var(--textLeadColor); + font-size: 15px; + font-weight: 500; + line-height: 1.5; } h1 { @@ -95,15 +100,24 @@ h1 { font-size: 24px; font-weight: 800; color: var(--textLeadColor); - letter-spacing: 0; - line-height: 24px; } a, button { + font-size: 14px; + font-weight: 500; +} + +a { color: var(--textColor); - cursor: pointer; +} + +button { + color: var(--buttonTextColor); + background-color: var(--buttonBackgroundColor); + padding: 10px 30px; border: 0; + cursor: pointer; } img { diff --git a/src/components/EntryListing/EntryListing.css b/src/components/EntryListing/EntryListing.css index 58602141f89c..4a009c8288df 100644 --- a/src/components/EntryListing/EntryListing.css +++ b/src/components/EntryListing/EntryListing.css @@ -1,35 +1,50 @@ .nc-entryListing-card { - flex: 0 335px; + @apply(--card); + flex: 0 0 335px; overflow: hidden; margin-bottom: 16px; - margin-left: 16px; - max-width: 50%; - max-height: 290px; + margin-left: 12px; + height: 240px; cursor: pointer; } -.nc-entryListing-card:hover { - background: #f8f9fa; - transform: translateY(-8px); -} - .nc-entryListing-cardImage { - margin: -16px -24px 16px -24px; - width: calc(100% + 24px + 24px); - height: 135px; background-position: center center; background-size: cover; background-repeat: no-repeat; + height: 135px; +} + +.nc-entryListing-cardBody { + padding: 16px 22px; + height: 105px; + position: relative; + + &:after { + content: ''; + position: absolute; + display: block; + z-index: 1; + bottom: 0; + left: -20%; + height: 140%; + width: 140%; + box-shadow: inset 0 -15px 24px #fff; + } +} + +.nc-entryListing-cardBody-full { + height: 100%; +} + +.nc-entryListing-cardHeading { + margin: 0 0 2px; } .nc-entryListing-cardsGrid { display: flex; flex-flow: row wrap; - margin-left: -16px; -} - -.nc-entryListing-cardList { - margin-bottom: 1.1rem; + margin-left: -12px; } .nc-entryListing-cardListLabel { diff --git a/src/components/EntryListing/EntryListing.js b/src/components/EntryListing/EntryListing.js index 9c8ed951bbd8..413b9104e3dd 100644 --- a/src/components/EntryListing/EntryListing.js +++ b/src/components/EntryListing/EntryListing.js @@ -3,6 +3,7 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Waypoint from 'react-waypoint'; import { Map } from 'immutable'; +import c from 'classnames'; import history from '../../routing/history'; import { resolvePath } from '../../lib/pathHelper'; import { selectFields, selectInferedField } from '../../reducers/collections'; @@ -52,16 +53,18 @@ export default class EntryListing extends React.Component { { image &&
} -

{title}

- {inferedFields.descriptionField ? -

{entry.getIn(['data', inferedFields.descriptionField])}

- : inferedFields.remainingFields && inferedFields.remainingFields.map(f => ( -

- {f.get('label')}:{' '} - { (entry.getIn(['data', f.get('name')]) || '').toString() } -

- )) - } +
+

{title}

+ {inferedFields.descriptionField ? +

{entry.getIn(['data', inferedFields.descriptionField])}

+ : inferedFields.remainingFields && inferedFields.remainingFields.map(f => ( +

+ {f.get('label')}:{' '} + { (entry.getIn(['data', f.get('name')]) || '').toString() } +

+ )) + } +
); } From 046fd9f1fa521c54011bdc19d7528f68b647856b Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Mon, 23 Oct 2017 18:57:57 -0400 Subject: [PATCH 003/134] improve global layout --- src/App/App.css | 12 ++++++---- src/App/Header.css | 9 ++++++-- src/App/Header.js | 42 ++++++++++++++++++----------------- src/Collection/Collection.css | 1 + src/Collection/Entries.js | 6 ++--- 5 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/App/App.css b/src/App/App.css index 0424d6d58e9c..db9270add5c2 100644 --- a/src/App/App.css +++ b/src/App/App.css @@ -2,11 +2,15 @@ .nc-app-container { display: grid; - grid-template-columns: 1440px; + grid-template-columns: auto 1440px auto; grid-template-rows: 56px auto; grid-gap: 28px; grid-template-areas: - "header" - "main"; - justify-content: center; + "header header header" + ". main ."; + justify-content: stretch; +} + +.nc-app-main { + grid-area: main; } diff --git a/src/App/Header.css b/src/App/Header.css index fc8a53d90cf2..012c4b6c8f35 100644 --- a/src/App/Header.css +++ b/src/App/Header.css @@ -1,10 +1,15 @@ .nc-appHeader-container { - background-color: var(--backgroundColorSecondary); grid-area: header; + background-color: var(--backgroundColorSecondary); + box-shadow: var(--dropShadow); +} + +.nc-appHeader-content { display: flex; justify-content: space-between; + max-width: 1600px; padding: 0 12px; - box-shadow: var(--dropShadow); + margin: 0 auto; } .nc-appHeader-button { diff --git a/src/App/Header.js b/src/App/Header.js index a160248f0c18..805d0eff337b 100644 --- a/src/App/Header.js +++ b/src/App/Header.js @@ -48,26 +48,28 @@ export default class Header extends React.Component { return (
- -
- - - olddvdscreensaver.com - - +
+ +
+ + + olddvdscreensaver.com + + +
); diff --git a/src/Collection/Collection.css b/src/Collection/Collection.css index b5db5f85bed0..89537cc4ca07 100644 --- a/src/Collection/Collection.css +++ b/src/Collection/Collection.css @@ -2,6 +2,7 @@ @import "./Top.css"; .nc-collectionPage-container { + grid-area: main; display: grid; grid-template-columns: 250px auto; grid-template-rows: auto auto; diff --git a/src/Collection/Entries.js b/src/Collection/Entries.js index 6829337fec89..02a700bd2564 100644 --- a/src/Collection/Entries.js +++ b/src/Collection/Entries.js @@ -23,11 +23,11 @@ const Entries = ({ collections, entries, publicFolder, page, onPaginate, isFetch } Entries.propTypes = { - collection: ImmutablePropTypes.map.isRequired, - collections: ImmutablePropTypes.orderedMap.isRequired, + collections: ImmutablePropTypes.map.isRequired, + entries: ImmutablePropTypes.list, publicFolder: PropTypes.string.isRequired, page: PropTypes.number, - entries: ImmutablePropTypes.list, + isFetching: PropTypes.bool, }; export default Entries; From af4bf5d5c6f850ae2a64c84b0cec977851d911fa Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Fri, 3 Nov 2017 13:42:47 -0400 Subject: [PATCH 004/134] merge search page into collection page --- src/App/App.js | 5 +- src/Collection/Collection.css | 1 + src/Collection/Collection.js | 72 ++++---------- src/Collection/Entries/Entries.css | 2 + src/Collection/{ => Entries}/Entries.js | 4 +- src/Collection/Entries/EntriesCollection.js | 62 ++++++++++++ .../Entries/EntriesSearch.js} | 61 ++++++------ .../Entries/EntryCard.css} | 6 -- src/Collection/Entries/EntryCard.js | 43 +++++++++ src/Collection/Entries/EntryListing.css | 5 + src/Collection/Entries/EntryListing.js | 69 +++++++++++++ src/Collection/Sidebar.js | 2 +- src/components/EntryListing/EntryListing.js | 96 ------------------- src/index.css | 1 - 14 files changed, 231 insertions(+), 198 deletions(-) create mode 100644 src/Collection/Entries/Entries.css rename src/Collection/{ => Entries}/Entries.js (88%) create mode 100644 src/Collection/Entries/EntriesCollection.js rename src/{containers/SearchPage.js => Collection/Entries/EntriesSearch.js} (54%) rename src/{components/EntryListing/EntryListing.css => Collection/Entries/EntryCard.css} (89%) create mode 100644 src/Collection/Entries/EntryCard.js create mode 100644 src/Collection/Entries/EntryListing.css create mode 100644 src/Collection/Entries/EntryListing.js delete mode 100644 src/components/EntryListing/EntryListing.js diff --git a/src/App/App.js b/src/App/App.js index aa29e34e2992..42282bdac101 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -19,7 +19,6 @@ import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper'; import { SIMPLE, EDITORIAL_WORKFLOW } from '../constants/publishModes'; import Collection from '../Collection/Collection'; import EntryPage from '../containers/EntryPage'; -import SearchPage from '../containers/SearchPage'; import NotFoundPage from '../containers/NotFoundPage'; TopBarProgress.config({ @@ -136,9 +135,9 @@ class App extends React.Component { - ()} /> + } /> - + } /> diff --git a/src/Collection/Collection.css b/src/Collection/Collection.css index 89537cc4ca07..5bc769bee385 100644 --- a/src/Collection/Collection.css +++ b/src/Collection/Collection.css @@ -1,5 +1,6 @@ @import "./Sidebar.css"; @import "./Top.css"; +@import "./Entries/Entries.css"; .nc-collectionPage-container { grid-area: main; diff --git a/src/Collection/Collection.js b/src/Collection/Collection.js index 5550b71f665b..1283590871a8 100644 --- a/src/Collection/Collection.js +++ b/src/Collection/Collection.js @@ -1,83 +1,45 @@ -import PropTypes from 'prop-types'; import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; -import { loadEntries } from '../actions/entries'; -import { showCollection } from '../actions/collections'; -import { selectEntries } from '../reducers'; -import { Loader } from '../components/UI'; import Sidebar from './Sidebar'; import Top from './Top'; -import Entries from './Entries'; -import EntryListing from '../components/EntryListing/EntryListing'; +import EntriesCollection from './Entries/EntriesCollection'; +import EntriesSearch from './Entries/EntriesSearch'; class Collection extends React.Component { - static propTypes = { collection: ImmutablePropTypes.map.isRequired, collections: ImmutablePropTypes.orderedMap.isRequired, - publicFolder: PropTypes.string.isRequired, - dispatch: PropTypes.func.isRequired, - page: PropTypes.number, - entries: ImmutablePropTypes.list, - isFetching: PropTypes.bool.isRequired, }; - state = { query: '' }; - - componentDidMount() { - const { collection, dispatch } = this.props; - if (collection) { - dispatch(loadEntries(collection)); - } - } - - componentWillReceiveProps(nextProps) { - const { collection, dispatch } = this.props; - if (nextProps.collection !== collection) { - dispatch(loadEntries(nextProps.collection)); - } - } + renderEntriesCollection = () => { + const { name, collection } = this.props; + return + }; - handleLoadMore = (page) => { - const { collection, dispatch } = this.props; - dispatch(loadEntries(collection, page)); + renderEntriesSearch = () => { + const { searchTerm, collections } = this.props; + return }; render() { - const { collections, collection, publicFolder, page, entries, isFetching } = this.props; - const { query } = this.state; - + const { collection, collections, isSearchResults, searchTerm } = this.props; return (
- - - + + { isSearchResults ? null : } + { isSearchResults ? this.renderEntriesSearch() : this.renderEntriesCollection() }
); } } - function mapStateToProps(state, ownProps) { - const { collections, config } = state; - const { name } = ownProps.match.params; - const publicFolder = config.get('public_folder'); + const { collections } = state; + const { isSearchResults, match } = ownProps; + const { name, searchTerm } = match.params; const collection = name ? collections.get(name) : collections.first(); - const page = state.entries.getIn(['pages', collection.get('name'), 'page']); - - const entries = selectEntries(state, collection.get('name')); - const isFetching = state.entries.getIn(['pages', collection.get('name'), 'isFetching'], false); - - return { publicFolder, collection, collections, page, entries, isFetching }; + return { collection, collections, isSearchResults, searchTerm }; } export default connect(mapStateToProps)(Collection); diff --git a/src/Collection/Entries/Entries.css b/src/Collection/Entries/Entries.css new file mode 100644 index 000000000000..8f62e0a25a43 --- /dev/null +++ b/src/Collection/Entries/Entries.css @@ -0,0 +1,2 @@ +@import "./EntryListing.css"; +@import "./EntryCard.css"; diff --git a/src/Collection/Entries.js b/src/Collection/Entries/Entries.js similarity index 88% rename from src/Collection/Entries.js rename to src/Collection/Entries/Entries.js index 02a700bd2564..a3d3267ab692 100644 --- a/src/Collection/Entries.js +++ b/src/Collection/Entries/Entries.js @@ -1,8 +1,8 @@ import PropTypes from 'prop-types'; import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { Loader } from '../components/UI'; -import EntryListing from '../components/EntryListing/EntryListing'; +import { Loader } from '../../components/UI'; +import EntryListing from './EntryListing'; const Entries = ({ collections, entries, publicFolder, page, onPaginate, isFetching }) => { const loadingMessages = [ diff --git a/src/Collection/Entries/EntriesCollection.js b/src/Collection/Entries/EntriesCollection.js new file mode 100644 index 000000000000..f311d4a623be --- /dev/null +++ b/src/Collection/Entries/EntriesCollection.js @@ -0,0 +1,62 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { connect } from 'react-redux'; +import { loadEntries } from '../../actions/entries'; +import { selectEntries } from '../../reducers'; +import Entries from './Entries'; + +class EntriesCollection extends React.Component { + static propTypes = { + collection: ImmutablePropTypes.map.isRequired, + publicFolder: PropTypes.string.isRequired, + dispatch: PropTypes.func.isRequired, + page: PropTypes.number, + entries: ImmutablePropTypes.list, + isFetching: PropTypes.bool.isRequired, + }; + + componentDidMount() { + const { collection, dispatch } = this.props; + if (collection) { + dispatch(loadEntries(collection)); + } + } + + componentWillReceiveProps(nextProps) { + const { collection, dispatch } = this.props; + if (nextProps.collection !== collection) { + dispatch(loadEntries(nextProps.collection)); + } + } + + render () { + const { dispatch, collection, entries, publicFolder, page, isFetching } = this.props; + + return ( + dispatch(loadEntries(collection, page))} + isFetching={isFetching} + collectionName={collection.get('label')} + /> + ); + } +} + +function mapStateToProps(state, ownProps) { + const { name, collection } = ownProps; + const { config } = state; + const publicFolder = config.get('public_folder'); + const page = state.entries.getIn(['pages', collection.get('name'), 'page']); + + const entries = selectEntries(state, collection.get('name')); + const isFetching = state.entries.getIn(['pages', collection.get('name'), 'isFetching'], false); + + return { publicFolder, collection, page, entries, isFetching }; +} + +export default connect(mapStateToProps)(EntriesCollection); diff --git a/src/containers/SearchPage.js b/src/Collection/Entries/EntriesSearch.js similarity index 54% rename from src/containers/SearchPage.js rename to src/Collection/Entries/EntriesSearch.js index e27d5721c38b..953fa0afbf4c 100644 --- a/src/containers/SearchPage.js +++ b/src/Collection/Entries/EntriesSearch.js @@ -1,14 +1,15 @@ -import PropTypes from 'prop-types'; import React from 'react'; +import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; -import { selectSearchedEntries } from '../reducers'; -import { searchEntries as actionSearchEntries, clearSearch as actionClearSearch } from '../actions/search'; -import { Loader } from '../components/UI'; -import EntryListing from '../components/EntryListing/EntryListing'; - -class SearchPage extends React.Component { +import { selectSearchedEntries } from '../../reducers'; +import { + searchEntries as actionSearchEntries, + clearSearch as actionClearSearch +} from '../../actions/search'; +import Entries from './Entries'; +class EntriesSearch extends React.Component { static propTypes = { isFetching: PropTypes.bool, searchEntries: PropTypes.func.isRequired, @@ -40,43 +41,35 @@ class SearchPage extends React.Component { if (!isNaN(page)) searchEntries(searchTerm, page); }; - render() { - const { collections, searchTerm, entries, isFetching, page, publicFolder } = this.props; - return (
- {(isFetching === true || !entries) ? - {['Loading Entries', 'Caching Entries', 'This might take several minutes']} - : - - Results for “{searchTerm}” - - } -
); + render () { + const { dispatch, collections, entries, publicFolder, page, isFetching } = this.props; + return ( + + ); } } - function mapStateToProps(state, ownProps) { + const { searchTerm } = ownProps; + const collections = ownProps.collections.toIndexedSeq(); const isFetching = state.entries.getIn(['search', 'isFetching']); const page = state.entries.getIn(['search', 'page']); const entries = selectSearchedEntries(state); - const collections = state.collections.toIndexedSeq(); const publicFolder = state.config.get('public_folder'); - const { searchTerm } = ownProps.match.params; return { isFetching, page, collections, entries, publicFolder, searchTerm }; } +const mapDispatchToProps = { + searchEntries: actionSearchEntries, + clearSearch: actionClearSearch, +}; -export default connect( - mapStateToProps, - { - searchEntries: actionSearchEntries, - clearSearch: actionClearSearch, - } -)(SearchPage); +export default connect(mapStateToProps, mapDispatchToProps)(EntriesSearch); diff --git a/src/components/EntryListing/EntryListing.css b/src/Collection/Entries/EntryCard.css similarity index 89% rename from src/components/EntryListing/EntryListing.css rename to src/Collection/Entries/EntryCard.css index 4a009c8288df..ced99adfefdf 100644 --- a/src/components/EntryListing/EntryListing.css +++ b/src/Collection/Entries/EntryCard.css @@ -41,12 +41,6 @@ margin: 0 0 2px; } -.nc-entryListing-cardsGrid { - display: flex; - flex-flow: row wrap; - margin-left: -12px; -} - .nc-entryListing-cardListLabel { white-space: nowrap; font-weight: bold; diff --git a/src/Collection/Entries/EntryCard.js b/src/Collection/Entries/EntryCard.js new file mode 100644 index 000000000000..504a9a708e57 --- /dev/null +++ b/src/Collection/Entries/EntryCard.js @@ -0,0 +1,43 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import c from 'classnames'; +import history from '../../routing/history'; +import { resolvePath } from '../../lib/pathHelper'; + +const EntryCard = ({ collection, entry, inferedFields, publicFolder }) => { + const path = `/collections/${ collection.get('name') }/entries/${ entry.get('slug') }`; + const label = entry.get('label'); + const title = label || entry.getIn(['data', inferedFields.titleField]); + let image = entry.getIn(['data', inferedFields.imageField]); + image = resolvePath(image, publicFolder); + if(image) { + image = encodeURI(image); + } + + return ( +
history.push(path)} + className="nc-entryListing-card" + > + { image && +
+ } +
+

{title}

+ {inferedFields.descriptionField ? +

{entry.getIn(['data', inferedFields.descriptionField])}

+ : inferedFields.remainingFields && inferedFields.remainingFields.map(f => ( +

+ {f.get('label')}:{' '} + { (entry.getIn(['data', f.get('name')]) || '').toString() } +

+ )) + } +
+
+ ); +} + +export default EntryCard; diff --git a/src/Collection/Entries/EntryListing.css b/src/Collection/Entries/EntryListing.css new file mode 100644 index 000000000000..38601165f1c5 --- /dev/null +++ b/src/Collection/Entries/EntryListing.css @@ -0,0 +1,5 @@ +.nc-entryListing-cardsGrid { + display: flex; + flex-flow: row wrap; + margin-left: -12px; +} diff --git a/src/Collection/Entries/EntryListing.js b/src/Collection/Entries/EntryListing.js new file mode 100644 index 000000000000..4d277bb83b7c --- /dev/null +++ b/src/Collection/Entries/EntryListing.js @@ -0,0 +1,69 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import Waypoint from 'react-waypoint'; +import { Map } from 'immutable'; +import { selectFields, selectInferedField } from '../../reducers/collections'; +import EntryCard from './EntryCard'; + +export default class EntryListing extends React.Component { + static propTypes = { + publicFolder: PropTypes.string.isRequired, + collections: PropTypes.oneOfType([ + ImmutablePropTypes.map, + ImmutablePropTypes.iterable, + ]).isRequired, + entries: ImmutablePropTypes.list, + onPaginate: PropTypes.func.isRequired, + page: PropTypes.number, + }; + + handleLoadMore = () => { + this.props.onPaginate(this.props.page + 1); + }; + + inferFields = collection => { + const titleField = selectInferedField(collection, 'title'); + const descriptionField = selectInferedField(collection, 'description'); + const imageField = selectInferedField(collection, 'image'); + const fields = selectFields(collection); + const inferedFields = [titleField, descriptionField, imageField]; + const remainingFields = fields && fields.filter(f => inferedFields.indexOf(f.get('name')) === -1); + return { titleField, descriptionField, imageField, remainingFields }; + }; + + renderCardsForSingleCollection = () => { + const { collections, entries, publicFolder } = this.props; + const inferedFields = this.inferFields(collections); + const entryCardProps = { collection: collections, inferedFields, publicFolder }; + return entries.map((entry, idx) => ); + }; + + renderCardsForMultipleCollections = () => { + const { collections, entries, publicFolder } = this.props; + return entries.map((entry, idx) => { + const collectionName = entry.get('collection'); + const collection = collections.find(coll => coll.get('name') === collectionName); + const inferedFields = this.inferFields(collection); + const entryCardProps = { collection, entry, inferedFields, publicFolder, key: idx }; + return ; + }); + }; + + render() { + const { collections, entries, publicFolder } = this.props; + + return ( +
+
+ { + Map.isMap(collections) + ? this.renderCardsForSingleCollection() + : this.renderCardsForMultipleCollections() + } + +
+
+ ); + } +} diff --git a/src/Collection/Sidebar.js b/src/Collection/Sidebar.js index 9feeb0cf58ea..3c8913cb4726 100644 --- a/src/Collection/Sidebar.js +++ b/src/Collection/Sidebar.js @@ -12,7 +12,7 @@ export default class Collection extends React.Component { collections: ImmutablePropTypes.orderedMap.isRequired, }; - state = { query: '' }; + state = { query: this.props.searchTerm || '' }; renderLink = collection => { const collectionName = collection.get('name'); diff --git a/src/components/EntryListing/EntryListing.js b/src/components/EntryListing/EntryListing.js deleted file mode 100644 index 413b9104e3dd..000000000000 --- a/src/components/EntryListing/EntryListing.js +++ /dev/null @@ -1,96 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import Waypoint from 'react-waypoint'; -import { Map } from 'immutable'; -import c from 'classnames'; -import history from '../../routing/history'; -import { resolvePath } from '../../lib/pathHelper'; -import { selectFields, selectInferedField } from '../../reducers/collections'; - -export default class EntryListing extends React.Component { - static propTypes = { - publicFolder: PropTypes.string.isRequired, - collections: PropTypes.oneOfType([ - ImmutablePropTypes.map, - ImmutablePropTypes.iterable, - ]).isRequired, - entries: ImmutablePropTypes.list, - onPaginate: PropTypes.func.isRequired, - page: PropTypes.number, - }; - - handleLoadMore = () => { - this.props.onPaginate(this.props.page + 1); - }; - - inferFields(collection) { //eslint-disable-line - const titleField = selectInferedField(collection, 'title'); - const descriptionField = selectInferedField(collection, 'description'); - const imageField = selectInferedField(collection, 'image'); - const fields = selectFields(collection); - const inferedFields = [titleField, descriptionField, imageField]; - const remainingFields = fields && fields.filter(f => inferedFields.indexOf(f.get('name')) === -1); - return { titleField, descriptionField, imageField, remainingFields }; - } - - renderCard(collection, entry, inferedFields, publicFolder) { - const path = `/collections/${ collection.get('name') }/entries/${ entry.get('slug') }`; - const label = entry.get('label'); - const title = label || entry.getIn(['data', inferedFields.titleField]); - let image = entry.getIn(['data', inferedFields.imageField]); - image = resolvePath(image, publicFolder); - if(image) { - image = encodeURI(image); - } - - return ( -
history.push(path)} // eslint-disable-line - className="nc-entryListing-card" - > - { image && -
- } -
-

{title}

- {inferedFields.descriptionField ? -

{entry.getIn(['data', inferedFields.descriptionField])}

- : inferedFields.remainingFields && inferedFields.remainingFields.map(f => ( -

- {f.get('label')}:{' '} - { (entry.getIn(['data', f.get('name')]) || '').toString() } -

- )) - } -
-
- ); - } - renderCards = () => { - const { collections, entries, publicFolder } = this.props; - - if (Map.isMap(collections)) { - const inferedFields = this.inferFields(collections); - return entries.map(entry => this.renderCard(collections, entry, inferedFields, publicFolder)); - } - return entries.map((entry) => { - const collection = collections - .filter(collection => collection.get('name') === entry.get('collection')).first(); - const inferedFields = this.inferFields(collection); - return this.renderCard(collection, entry, inferedFields, publicFolder); - }); - }; - - render() { - return ( -
-
- { this.renderCards() } - -
-
- ); - } -} diff --git a/src/index.css b/src/index.css index 40893757306b..024d925615db 100644 --- a/src/index.css +++ b/src/index.css @@ -11,7 +11,6 @@ @import "./components/MediaLibrary/MediaLibrary.css"; @import "./components/ControlPanel/ControlPane.css"; @import "./components/EntryEditor/EntryEditor.css"; -@import "./components/EntryListing/EntryListing.css"; @import "./components/PreviewPane/PreviewPane.css"; @import "./components/UI/Sticky/Sticky.css"; @import "./components/UI/card/Card.css"; From aa39c07bf6a6fa0f1e08ff7f1f28a9823a072b60 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Fri, 3 Nov 2017 15:17:25 -0400 Subject: [PATCH 005/134] enable new entry button --- src/Collection/Collection.js | 12 +++++++++--- src/Collection/Top.css | 3 ++- src/Collection/Top.js | 20 ++++++++++++++------ src/base.css | 15 ++++++++++----- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/Collection/Collection.js b/src/Collection/Collection.js index 1283590871a8..67d7747c0803 100644 --- a/src/Collection/Collection.js +++ b/src/Collection/Collection.js @@ -1,6 +1,7 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; +import { getNewEntryUrl } from '../lib/urlHelper'; import Sidebar from './Sidebar'; import Top from './Top'; import EntriesCollection from './Entries/EntriesCollection'; @@ -23,11 +24,16 @@ class Collection extends React.Component { }; render() { - const { collection, collections, isSearchResults, searchTerm } = this.props; + const { collection, collections, collectionName, isSearchResults, searchTerm } = this.props; + const newEntryUrl = collection.get('create') && getNewEntryUrl(collectionName, true); return (
- { isSearchResults ? null : } + { + isSearchResults + ? null + : + } { isSearchResults ? this.renderEntriesSearch() : this.renderEntriesCollection() }
); @@ -39,7 +45,7 @@ function mapStateToProps(state, ownProps) { const { isSearchResults, match } = ownProps; const { name, searchTerm } = match.params; const collection = name ? collections.get(name) : collections.first(); - return { collection, collections, isSearchResults, searchTerm }; + return { collection, collections, collectionName: name, isSearchResults, searchTerm }; } export default connect(mapStateToProps)(Collection); diff --git a/src/Collection/Top.css b/src/Collection/Top.css index 51c010b75828..8be8e3eb74bd 100644 --- a/src/Collection/Top.css +++ b/src/Collection/Top.css @@ -14,5 +14,6 @@ margin: 12px 0 0; } -.nc-collectionPage-top-button { +.nc-collectionPage-topNewButton { + @apply(--button); } diff --git a/src/Collection/Top.js b/src/Collection/Top.js index 9609dd8af048..bbab8b53cfd7 100644 --- a/src/Collection/Top.js +++ b/src/Collection/Top.js @@ -1,14 +1,22 @@ import PropTypes from 'prop-types'; import React from 'react'; -const Top = ({ collectionName }) => -
-

{collectionName}

- -
; +const Top = ({ collectionLabel, newEntryUrl }) => { + const newEntryLink = ( + {`New ${collectionLabel}`} + ); + + return ( +
+

{collectionLabel}

+ { newEntryUrl ? newEntryLink : null } +
+ ); +}; Top.propTypes = { - collectionName: PropTypes.string.isRequired, + collectionLabel: PropTypes.string.isRequired, + newEntryUrl: PropTypes.string }; export default Top; diff --git a/src/base.css b/src/base.css index f7a61c7cfcbb..7fdaed22541f 100644 --- a/src/base.css +++ b/src/base.css @@ -64,6 +64,15 @@ border-color: var(--primaryColor); } } + + --button: { + color: var(--buttonTextColor); + background-color: var(--buttonBackgroundColor); + padding: 10px 30px; + border: 0; + border-radius: var(--borderRadius); + cursor: pointer; + } } html { @@ -113,11 +122,7 @@ a { } button { - color: var(--buttonTextColor); - background-color: var(--buttonBackgroundColor); - padding: 10px 30px; - border: 0; - cursor: pointer; + @apply(--button); } img { From fcb3ccddc56e2bfdc09b2c335f896b8ab1045298 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Fri, 3 Nov 2017 15:21:53 -0400 Subject: [PATCH 006/134] search fixup --- src/App/App.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/App/App.js b/src/App/App.js index 42282bdac101..e97348d9a191 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -119,6 +119,8 @@ class App extends React.Component { return this.authenticating(); } + const defaultPath = `/collections/${collections.first().get('name')}`; + return (
@@ -133,7 +135,8 @@ class App extends React.Component { { isFetching && }
- + + } /> From 393b7b2ffb781d35e770fd169e600f3c6d4ad434 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Fri, 3 Nov 2017 22:26:46 -0400 Subject: [PATCH 007/134] wip -initial editor update --- src/App/App.css | 14 +----- src/App/Header.css | 10 ++++- src/App/Header.js | 44 ++++++++++--------- src/Collection/Collection.css | 12 ++--- src/Collection/Collection.js | 14 +++--- src/Collection/Sidebar.css | 4 +- src/Collection/Top.css | 2 +- src/base.css | 7 +-- src/components/ControlPanel/ControlPane.css | 39 ++++++++++++++-- src/components/ControlPanel/ControlPane.js | 2 +- src/components/EntryEditor/EntryEditor.css | 26 ++++++----- src/components/EntryEditor/EntryEditor.js | 8 ++-- src/components/EntryEditor/ReactSplitPane.css | 29 +----------- src/components/PreviewPane/PreviewPane.css | 1 + src/components/PreviewPane/PreviewPane.js | 1 + src/components/Widgets/FileControl.css | 9 +--- .../MarkdownControl/Toolbar/Toolbar.css | 4 ++ .../MarkdownControl/Toolbar/ToolbarButton.css | 8 +++- .../MarkdownControl/VisualEditor/index.css | 3 -- 19 files changed, 124 insertions(+), 113 deletions(-) diff --git a/src/App/App.css b/src/App/App.css index db9270add5c2..b7aaf3615434 100644 --- a/src/App/App.css +++ b/src/App/App.css @@ -1,16 +1,6 @@ @import "./Header.css"; -.nc-app-container { - display: grid; - grid-template-columns: auto 1440px auto; - grid-template-rows: 56px auto; - grid-gap: 28px; - grid-template-areas: - "header header header" - ". main ."; - justify-content: stretch; -} - .nc-app-main { - grid-area: main; + width: 1440px; + margin: 0 auto; } diff --git a/src/App/Header.css b/src/App/Header.css index 012c4b6c8f35..965ec9a82d55 100644 --- a/src/App/Header.css +++ b/src/App/Header.css @@ -1,7 +1,15 @@ .nc-appHeader-container { - grid-area: header; + margin-bottom: 84px; + z-index: 300; +} + +.nc-appHeader-main { + position: fixed; + width: 100%; + top: 0; background-color: var(--backgroundColorSecondary); box-shadow: var(--dropShadow); + z-index: 300; } .nc-appHeader-content { diff --git a/src/App/Header.js b/src/App/Header.js index 805d0eff337b..80e5e0079325 100644 --- a/src/App/Header.js +++ b/src/App/Header.js @@ -48,27 +48,29 @@ export default class Header extends React.Component { return (
-
- -
- - - olddvdscreensaver.com - - +
+
+ +
+ + + olddvdscreensaver.com + + +
diff --git a/src/Collection/Collection.css b/src/Collection/Collection.css index 5bc769bee385..3bf04d0f1e2d 100644 --- a/src/Collection/Collection.css +++ b/src/Collection/Collection.css @@ -3,13 +3,9 @@ @import "./Entries/Entries.css"; .nc-collectionPage-container { - grid-area: main; - display: grid; - grid-template-columns: 250px auto; - grid-template-rows: auto auto; - grid-gap: 28px; - grid-template-areas: - "sidebar top" - "sidebar main"; margin: 0 18px; } + +.nc-collectionPage-main { + padding-left: 280px; +} diff --git a/src/Collection/Collection.js b/src/Collection/Collection.js index 67d7747c0803..60340530e52c 100644 --- a/src/Collection/Collection.js +++ b/src/Collection/Collection.js @@ -29,12 +29,14 @@ class Collection extends React.Component { return (
- { - isSearchResults - ? null - : - } - { isSearchResults ? this.renderEntriesSearch() : this.renderEntriesCollection() } +
+ { + isSearchResults + ? null + : + } + { isSearchResults ? this.renderEntriesSearch() : this.renderEntriesCollection() } +
); } diff --git a/src/Collection/Sidebar.css b/src/Collection/Sidebar.css index ede0e5f94b93..178f0aa30fbb 100644 --- a/src/Collection/Sidebar.css +++ b/src/Collection/Sidebar.css @@ -1,8 +1,8 @@ .nc-collectionPage-sidebar { @apply(--card); + width: 250px; padding: 8px 0 12px; - grid-area: sidebar; - align-self: start; + position: fixed; } .nc-collectionPage-sidebarHeading { diff --git a/src/Collection/Top.css b/src/Collection/Top.css index 8be8e3eb74bd..440d9de029e3 100644 --- a/src/Collection/Top.css +++ b/src/Collection/Top.css @@ -1,11 +1,11 @@ .nc-collectionPage-top { @apply(--card); - grid-area: top; display: flex; align-items: center; justify-content: space-between; max-width: 682px; padding: 16px 20px; + margin-bottom: 28px; } .nc-collectionPage-topHeading { diff --git a/src/base.css b/src/base.css index 7fdaed22541f..6833771d570f 100644 --- a/src/base.css +++ b/src/base.css @@ -23,18 +23,18 @@ --topmostZindex: 99999; --foregroundAltColor: #fff; --backgroundAltColor: #fff; - --textFieldBorderColor: var(--secondaryColor); + --textFieldBorderColor: #dfdfe3; --highlightFGColor: #fff; --highlightBGColor: #3ab7a5; --highlightFGAltColor: #eee; - --controlLabelColor: #8b8b8b; + --controlLabelColor: #7a8291; --controlBGColor: #fff; --backgroundTertiaryColor: #fff; --backgroundTertiaryColorDark: color(var(--backgroundTertiaryColor) lightness(90%)); --richTextEditorMinHeight: 300px; --borderWidth: 2px; --border: solid var(--borderWidth) var(--secondaryColor); - --textFieldBorder: var(--border); + --textFieldBorder: solid var(--borderWidth) var(--textFieldBorderColor); --buttonTextColor: #fff; --buttonBackgroundColor: var(--secondaryColor); @@ -58,6 +58,7 @@ font-size: 16px; color: var(--textColor); transition: border-color .3s ease; + position: relative; &:focus, &:active { diff --git a/src/components/ControlPanel/ControlPane.css b/src/components/ControlPanel/ControlPane.css index 7d4967765c68..eb52000a6ae9 100644 --- a/src/components/ControlPanel/ControlPane.css +++ b/src/components/ControlPanel/ControlPane.css @@ -1,3 +1,11 @@ +.nc-controlPane-root { + padding: 20px 20px 0; + background-color: #fff; + height: 100%; + overflow-y: auto; + border-radius: var(--borderRadius); +} + .nc-controlPane-root p { font-size: 16px; } @@ -5,8 +13,7 @@ .nc-controlPane-control { color: var(--textColor); position: relative; - padding: 20px 0 10px 0; - margin-top: 16px; + margin-top: 14px; & input, & textarea, @@ -16,11 +23,37 @@ } .nc-controlPane-label { - display: block; + display: inline-block; color: var(--controlLabelColor); font-size: 12px; text-transform: uppercase; font-weight: 600; + background-color: #dfdfe3; + border: 0; + position: relative; + top: 6px; + border-radius: 3px 3px 0 0; + padding: 3px 8px 8px; + + /** + * Faux outside curve into top of input + */ + &:before, + &:after { + content: ''; + display: block; + position: absolute; + top: 0; + right: -4px; + height: calc(100% - 6px); + width: 4px; + background-color: inherit; + } + + &:after { + border-bottom-left-radius: 3px; + background-color: #fff; + } } .nc-controlPane-labelWithError { diff --git a/src/components/ControlPanel/ControlPane.js b/src/components/ControlPanel/ControlPane.js index 1d7525d6b2ee..ecc0066dd866 100644 --- a/src/components/ControlPanel/ControlPane.js +++ b/src/components/ControlPanel/ControlPane.js @@ -44,7 +44,6 @@ export default class ControlPane extends Component { if (entry.size === 0 || entry.get('partial') === true) return null; return (
-
    { errors && errors.map(error => @@ -54,6 +53,7 @@ export default class ControlPane extends Component { ) }
+ * { pointer-events: none; } diff --git a/src/components/EntryEditor/EntryEditor.js b/src/components/EntryEditor/EntryEditor.js index de828ee978dc..ed75e052fda3 100644 --- a/src/components/EntryEditor/EntryEditor.js +++ b/src/components/EntryEditor/EntryEditor.js @@ -118,12 +118,13 @@ class EntryEditor extends Component { const editorWithPreview = ( -
+
{editor}
@@ -146,9 +147,10 @@ class EntryEditor extends Component {
); + return collectionPreviewEnabled && this.state.previewVisible ? editorWithPreview : editorWithoutPreview; + return ( -
- { collectionPreviewEnabled && this.state.previewVisible ? editorWithPreview : editorWithoutPreview } +
`; + return ( diff --git a/src/components/Widgets/FileControl.css b/src/components/Widgets/FileControl.css index bdaa85bbe100..6ba96c0728c2 100644 --- a/src/components/Widgets/FileControl.css +++ b/src/components/Widgets/FileControl.css @@ -3,17 +3,12 @@ } .nc-fileControl-message { - padding: 20px; + padding: 8px; display: block; font-size: 12px; } .nc-fileControl-imageUpload { - background-color: #fff; - text-align: center; - color: #999; - border: var(--border); - border-style: dashed; - border-radius: var(--borderRadius); + @apply(--input); cursor: pointer; } diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css index 7c554a327306..bb75f16db420 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css @@ -1,5 +1,9 @@ .nc-toolbar-Toolbar { z-index: 1000; + background-color: var(--textFieldBorderColor); + padding: 10px 10px 16px; + margin-bottom: -6px; + border-top-right-radius: var(--borderRadius); } .nc-toolbar-Toggle { diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css index dc853c5b5a0e..bad3f2d8ff03 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css @@ -3,9 +3,13 @@ padding: 6px; border: none; background-color: transparent; + color: var(--controlLabelColor); + font-size: 16px; + cursor: pointer; - &:not(:disabled) { - cursor: pointer; + &:disabled { + cursor: auto; + opacity: 0.5; } } diff --git a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.css b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.css index 384334aba9a0..1fa4179da1d8 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.css +++ b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.css @@ -1,8 +1,5 @@ .nc-visualEditor-editorControlBar { z-index: 1; - border: 2px solid transparent; - border-top: 0; - background-color: var(--controlBGColor); } .nc-visualEditor-editorControlBarSticky { From 925abd8b30afe6d5dbf2e7680179e406bba986b9 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Mon, 6 Nov 2017 11:09:02 -0500 Subject: [PATCH 008/134] update editor scrolling and markdown toolbar position --- src/components/ControlPanel/ControlPane.css | 8 ---- src/components/EntryEditor/EntryEditor.css | 18 +++++++-- src/components/EntryEditor/EntryEditor.js | 38 +++++++++++-------- src/components/UI/Sticky/Sticky.css | 4 +- src/components/UI/Sticky/Sticky.js | 2 +- .../MarkdownControl/Toolbar/Toolbar.css | 21 ++++++++-- .../MarkdownControl/VisualEditor/index.css | 1 + 7 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/components/ControlPanel/ControlPane.css b/src/components/ControlPanel/ControlPane.css index eb52000a6ae9..d70fe58a9b45 100644 --- a/src/components/ControlPanel/ControlPane.css +++ b/src/components/ControlPanel/ControlPane.css @@ -1,11 +1,3 @@ -.nc-controlPane-root { - padding: 20px 20px 0; - background-color: #fff; - height: 100%; - overflow-y: auto; - border-radius: var(--borderRadius); -} - .nc-controlPane-root p { font-size: 16px; } diff --git a/src/components/EntryEditor/EntryEditor.css b/src/components/EntryEditor/EntryEditor.css index 476d9982d0b7..a25ecc3c4df7 100644 --- a/src/components/EntryEditor/EntryEditor.css +++ b/src/components/EntryEditor/EntryEditor.css @@ -5,6 +5,9 @@ margin: 0 auto; overflow: hidden; top: 0; + padding-top: 84px; + padding-bottom: 28px; + position: relative; } /* Quick fix for preview pane not fully displaying in Safari */ @@ -14,15 +17,22 @@ .nc-entryEditor-controlPane, .nc-entryEditor-previewPane { + background-color: #fff; height: 100%; - padding-top: 84px; - padding-bottom: 28px; + overflow-y: auto; + border-radius: var(--borderRadius); +} + +.nc-entryEditor-controlPane { + padding: 20px 20px 0; + position: relative; + overflow-x: hidden; } .nc-entryEditor-controlPaneButtons { position: absolute; - top: 8px; - right: 20px; + top: 50px; + right: 0; z-index: 299; opacity: 0.8; display: flex; diff --git a/src/components/EntryEditor/EntryEditor.js b/src/components/EntryEditor/EntryEditor.js index ed75e052fda3..a4e45a135aec 100644 --- a/src/components/EntryEditor/EntryEditor.js +++ b/src/components/EntryEditor/EntryEditor.js @@ -84,20 +84,6 @@ class EntryEditor extends Component { className={classnames('nc-entryEditor-controlPane', { 'nc-entryEditor-blocker': showEventBlocker })} registerListener={fn => this.updateStickyContext = fn} > - { collectionPreviewEnabled ? ( -
- { previewVisible && ( - - ) } - -
- ) : null } ); - return collectionPreviewEnabled && this.state.previewVisible ? editorWithPreview : editorWithoutPreview; + return ( +
+ { collectionPreviewEnabled ? ( +
+ { previewVisible && ( + + ) } + +
+ ) : null } + { + collectionPreviewEnabled && this.state.previewVisible + ? editorWithPreview + : editorWithoutPreview + } +
+ ); return (
diff --git a/src/components/UI/Sticky/Sticky.css b/src/components/UI/Sticky/Sticky.css index 16f059d63856..02eff2e0e2f2 100644 --- a/src/components/UI/Sticky/Sticky.css +++ b/src/components/UI/Sticky/Sticky.css @@ -8,11 +8,11 @@ .nc-sticky-stickyActive:not(.nc-sticky-stickyAtBottom) { position: fixed !important; - top: 54px !important; + top: 84px !important; } .nc-sticky-stickyAtBottom { position: absolute !important; top: auto !important; - bottom: 30px !important; + bottom: 70px !important; } diff --git a/src/components/UI/Sticky/Sticky.js b/src/components/UI/Sticky/Sticky.js index 345452d36e75..d0a1c85cc748 100644 --- a/src/components/UI/Sticky/Sticky.js +++ b/src/components/UI/Sticky/Sticky.js @@ -130,7 +130,7 @@ export class StickyContainer extends Component { getPosition = (contextTop) => { const rect = this.ref.getBoundingClientRect(); const shouldStick = rect.top < contextTop; - const shouldStickAtBottom = rect.bottom - 60 < contextTop; + const shouldStickAtBottom = rect.bottom - 120 < contextTop; this.subscriptions.forEach((fn) => { fn(shouldStick, shouldStickAtBottom, rect.width); }); }; diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css index bb75f16db420..5f78c5f7b78d 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css @@ -1,9 +1,24 @@ .nc-toolbar-Toolbar { - z-index: 1000; + z-index: 0; background-color: var(--textFieldBorderColor); - padding: 10px 10px 16px; - margin-bottom: -6px; + padding: 10px; border-top-right-radius: var(--borderRadius); + position: relative; + + &:after { + content: ''; + display: block; + width: 100%; + height: 6px; + background-color: inherit; + position: absolute; + bottom: -6px; + left: 0; + } +} + +.nc-visualEditor-editorControlBarSticky .nc-toolbar-Toolbar { + border-top-right-radius: 0; } .nc-toolbar-Toggle { diff --git a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.css b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.css index 1fa4179da1d8..ef4a416ca680 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.css +++ b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.css @@ -4,6 +4,7 @@ .nc-visualEditor-editorControlBarSticky { border-color: var(--textFieldBorderColor); + border-top-right-radius: 0; } .nc-visualEditor-wrapper { From 65f54a8f8c04a72224baf4b4e18d54c68a0f2f6a Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Mon, 6 Nov 2017 16:58:18 -0500 Subject: [PATCH 009/134] wip --- src/App/Header.css | 1 + src/base.css | 1 + src/components/EntryEditor/EntryEditor.css | 68 ++++++++++++++++--- src/components/EntryEditor/EntryEditor.js | 60 ++++++++-------- .../EntryEditor/EntryEditorToolbar.js | 12 ++-- 5 files changed, 97 insertions(+), 45 deletions(-) diff --git a/src/App/Header.css b/src/App/Header.css index 965ec9a82d55..3c389a266214 100644 --- a/src/App/Header.css +++ b/src/App/Header.css @@ -10,6 +10,7 @@ background-color: var(--backgroundColorSecondary); box-shadow: var(--dropShadow); z-index: 300; + height: var(--topBarHeight); } .nc-appHeader-content { diff --git a/src/base.css b/src/base.css index 6833771d570f..c483f3a0610d 100644 --- a/src/base.css +++ b/src/base.css @@ -37,6 +37,7 @@ --textFieldBorder: solid var(--borderWidth) var(--textFieldBorderColor); --buttonTextColor: #fff; --buttonBackgroundColor: var(--secondaryColor); + --topBarHeight: 56px; --card: { box-shadow: var(--dropShadow); diff --git a/src/components/EntryEditor/EntryEditor.css b/src/components/EntryEditor/EntryEditor.css index a25ecc3c4df7..ee63f8508d6d 100644 --- a/src/components/EntryEditor/EntryEditor.css +++ b/src/components/EntryEditor/EntryEditor.css @@ -1,12 +1,20 @@ @import "./ReactSplitPane.css"; -.nc-entryEditor-container { - max-width: 1440px; - margin: 0 auto; - overflow: hidden; +.nc-entryEditor-containerOuter { + width: 100%; + height: 100%; + position: absolute; top: 0; + left: 0; + overflow: hidden; padding-top: 84px; padding-bottom: 28px; +} + +.nc-entryEditor-container { + max-width: 1440px; + height: 100%; + margin: 0 auto; position: relative; } @@ -31,16 +39,16 @@ .nc-entryEditor-controlPaneButtons { position: absolute; - top: 50px; - right: 0; + top: 0; + right: -50px; z-index: 299; opacity: 0.8; - display: flex; - justify-content: flex-end; } .nc-entryEditor-toggleButton { + margin-top: 10px; margin-left: 5px; + display: block; } .nc-entryEditor-toggleButtonShow { @@ -74,3 +82,47 @@ .nc-entryEditor-ProseMirror { border: var(--textFieldBorder); } + +.nc-entryEditor-toolbar { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 300; + background-color: #fff; + height: 66px; + display: flex; + box-shadow: var(--dropShadow); +} + +.nc-entryEditor-toolbar-backSection { + height: 100%; + display: flex; + align-items: center; + padding: 0 16px; + border-right: 1px solid var(--textFieldBorderColor); + cursor: pointer; +} + +.nc-entryEditor-toolbar-backArrow { + color: var(--textLeadColor); + font-size: 21px; + font-weight: 600; + margin-right: 16px; +} + +.nc-entryEditor-toolbar-backCollection { + color: var(--textLeadColor); + font-size: 14px; +} + +.nc-entryEditor-toolbar-backStatus { + color: #005614; + font-size: 13px; + background-color: #caef6f; + border-radius: var(--borderRadius); + padding: 3px 10px; + margin-top: 6px; + text-align: center; + display: inline-block; +} diff --git a/src/components/EntryEditor/EntryEditor.js b/src/components/EntryEditor/EntryEditor.js index a4e45a135aec..70a76de9b45b 100644 --- a/src/components/EntryEditor/EntryEditor.js +++ b/src/components/EntryEditor/EntryEditor.js @@ -110,7 +110,6 @@ class EntryEditor extends Component { onDragStarted={this.handleSplitPaneDragStart} onDragFinished={this.handleSplitPaneDragFinished} onChange={this.updateStickyContext} - className="nc-entryEditor-container" > {editor}
@@ -134,40 +133,35 @@ class EntryEditor extends Component { ); return ( -
- { collectionPreviewEnabled ? ( -
- { previewVisible && ( +
+ +
+ { collectionPreviewEnabled ? ( +
- ) } - -
- ) : null } - { - collectionPreviewEnabled && this.state.previewVisible - ? editorWithPreview - : editorWithoutPreview - } -
- ); - - return ( -
-
- + { previewVisible && ( + + ) } +
+ ) : null } + { + collectionPreviewEnabled && this.state.previewVisible + ? editorWithPreview + : editorWithoutPreview + }
); diff --git a/src/components/EntryEditor/EntryEditorToolbar.js b/src/components/EntryEditor/EntryEditorToolbar.js index 92efd4859228..746656a85911 100644 --- a/src/components/EntryEditor/EntryEditorToolbar.js +++ b/src/components/EntryEditor/EntryEditorToolbar.js @@ -13,7 +13,14 @@ const EntryEditorToolbar = ( }) => { const disabled = !enableSave || isPersisting; return ( -
+
+
+
+
+
Writing in Posts collection
+
Changes saved ✓
+
+
); }; From 23e803ccd39db5a6d727479acf396223e370f727 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Tue, 7 Nov 2017 19:51:17 -0500 Subject: [PATCH 010/134] editor toolbar progress --- src/components/EntryEditor/EntryEditor.css | 46 +++++++++++++++++-- src/components/EntryEditor/EntryEditor.js | 38 ++++++++------- .../EntryEditor/EntryEditorToolbar.js | 44 +++++++++++------- src/containers/EntryPage.js | 10 +++- 4 files changed, 102 insertions(+), 36 deletions(-) diff --git a/src/components/EntryEditor/EntryEditor.css b/src/components/EntryEditor/EntryEditor.css index ee63f8508d6d..459222f23e7d 100644 --- a/src/components/EntryEditor/EntryEditor.css +++ b/src/components/EntryEditor/EntryEditor.css @@ -92,18 +92,39 @@ background-color: #fff; height: 66px; display: flex; + justify-content: space-between; box-shadow: var(--dropShadow); } -.nc-entryEditor-toolbar-backSection { +.nc-entryEditor-toolbar-mainSection, +.nc-entryEditor-toolbar-backSection, +.nc-entryEditor-toolbar-metaSection { height: 100%; display: flex; align-items: center; padding: 0 16px; - border-right: 1px solid var(--textFieldBorderColor); +} + +.nc-entryEditor-toolbar-mainSection { + flex: 10; + display: flex; + justify-content: space-between; +} + +.nc-entryEditor-toolbar-backSection, +.nc-entryEditor-toolbar-metaSection { + border: 0 solid var(--textFieldBorderColor); cursor: pointer; } +.nc-entryEditor-toolbar-backSection { + border-right-width: 1px; +} + +.nc-entryEditor-toolbar-metaSection { + border-left-width: 1px; +} + .nc-entryEditor-toolbar-backArrow { color: var(--textLeadColor); font-size: 21px; @@ -116,7 +137,8 @@ font-size: 14px; } -.nc-entryEditor-toolbar-backStatus { +.nc-entryEditor-toolbar-backStatus, +.nc-entryEditor-toolbar-backStatus-hasChanged { color: #005614; font-size: 13px; background-color: #caef6f; @@ -126,3 +148,21 @@ text-align: center; display: inline-block; } + +.nc-entryEditor-toolbar-backStatus-hasChanged { + color: #ff003b; + background-color: #fbe0d7; +} + +.nc-entryEditor-toolbar-deleteButton { + color: #ff003b; + background-color: #fbe0d7; + padding-left: 20px; + padding-right: 20px; +} + +.nc-entryEditor-toolbar-saveButton { + background-color: #17a2b8; + padding-left: 20px; + padding-right: 20px; +} diff --git a/src/components/EntryEditor/EntryEditor.js b/src/components/EntryEditor/EntryEditor.js index 70a76de9b45b..08baf8ff1e74 100644 --- a/src/components/EntryEditor/EntryEditor.js +++ b/src/components/EntryEditor/EntryEditor.js @@ -47,22 +47,24 @@ class EntryEditor extends Component { render() { const { - collection, - entry, - fields, - fieldsMetaData, - fieldsErrors, - mediaPaths, - getAsset, - onChange, - enableSave, - showDelete, - onDelete, - onValidate, - onOpenMediaLibrary, - onAddAsset, - onRemoveAsset, - onCancelEdit, + collection, + entry, + fields, + fieldsMetaData, + fieldsErrors, + mediaPaths, + getAsset, + onChange, + enableSave, + showDelete, + onDelete, + onValidate, + onOpenMediaLibrary, + onAddAsset, + onRemoveAsset, + onCancelEdit, + user, + hasChanged, } = this.props; const { previewVisible, scrollSyncEnabled, showEventBlocker } = this.state; @@ -141,6 +143,8 @@ class EntryEditor extends Component { onDelete={onDelete} showDelete={showDelete} enableSave={enableSave} + user={user} + hasChanged={hasChanged} />
{ collectionPreviewEnabled ? ( @@ -186,6 +190,8 @@ EntryEditor.propTypes = { onDelete: PropTypes.func.isRequired, onRemoveAsset: PropTypes.func.isRequired, onCancelEdit: PropTypes.func.isRequired, + user: ImmutablePropTypes.map, + hasChanged: PropTypes.bool, }; export default EntryEditor; diff --git a/src/components/EntryEditor/EntryEditorToolbar.js b/src/components/EntryEditor/EntryEditorToolbar.js index 746656a85911..3486c9272468 100644 --- a/src/components/EntryEditor/EntryEditorToolbar.js +++ b/src/components/EntryEditor/EntryEditorToolbar.js @@ -1,4 +1,5 @@ import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; import React from 'react'; import { Button } from 'react-toolbox/lib/button'; @@ -10,6 +11,8 @@ const EntryEditorToolbar = ( showDelete, onDelete, onCancelEdit, + user, + hasChanged, }) => { const disabled = !enableSave || isPersisting; return ( @@ -18,24 +21,31 @@ const EntryEditorToolbar = (
Writing in Posts collection
-
Changes saved ✓
+ { + hasChanged + ?
Unsaved Changes
+ :
Changes saved ✓
+ }
- - {' '} - { showDelete - ? () - : '' } - { showDelete ? ' ' : '' } +
+ { + showDelete + ? + : null + } + +
+
); }; @@ -47,6 +57,8 @@ EntryEditorToolbar.propTypes = { showDelete: PropTypes.bool.isRequired, onDelete: PropTypes.func.isRequired, onCancelEdit: PropTypes.func.isRequired, + user: ImmutablePropTypes.map, + hasChanged: PropTypes.bool, }; export default EntryEditorToolbar; diff --git a/src/containers/EntryPage.js b/src/containers/EntryPage.js index a0f338934ba6..fcfded056db5 100644 --- a/src/containers/EntryPage.js +++ b/src/containers/EntryPage.js @@ -148,6 +148,8 @@ class EntryPage extends React.Component { addAsset, removeAsset, closeEntry, + user, + hasChanged, } = this.props; if (entry && entry.get('error')) { @@ -177,13 +179,15 @@ class EntryPage extends React.Component { showDelete={this.props.showDelete} enableSave={entryDraft.get('hasChanged')} onCancelEdit={this.handleCloseEntry} + user={user} + hasChanged={hasChanged} /> ); } } function mapStateToProps(state, ownProps) { - const { collections, entryDraft, mediaLibrary } = state; + const { collections, entryDraft, mediaLibrary, auth } = state; const slug = ownProps.match.params.slug; const collection = collections.get(ownProps.match.params.name); const newEntry = ownProps.newRecord === true; @@ -191,6 +195,8 @@ function mapStateToProps(state, ownProps) { const entry = newEntry ? null : selectEntry(state, collection.get('name'), slug); const boundGetAsset = getAsset.bind(null, state); const mediaPaths = mediaLibrary.get('controlMedia'); + const user = auth && auth.get('user'); + const hasChanged = entryDraft.get('hasChanged'); return { collection, collections, @@ -201,6 +207,8 @@ function mapStateToProps(state, ownProps) { fields, slug, entry, + user, + hasChanged, }; } From 69a6f5aba295b5dd19a4fea8bfbdbe5453a3e197 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Tue, 7 Nov 2017 21:38:23 -0500 Subject: [PATCH 011/134] editor toolbar wip --- package.json | 1 + src/components/EntryEditor/EntryEditor.css | 59 ++++++++++++++++++- .../EntryEditor/EntryEditorToolbar.js | 18 +++++- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 0d6a9a117d7d..17775399fb8a 100644 --- a/package.json +++ b/package.json @@ -148,6 +148,7 @@ "normalize.css": "^4.2.0", "prop-types": "^15.5.10", "react": "^16.0.0", + "react-aria-menubutton": "^5.1.0", "react-autosuggest": "^9.3.2", "react-datetime": "^2.6.0", "react-dnd": "^2.5.4", diff --git a/src/components/EntryEditor/EntryEditor.css b/src/components/EntryEditor/EntryEditor.css index 459222f23e7d..61fac3e93c43 100644 --- a/src/components/EntryEditor/EntryEditor.css +++ b/src/components/EntryEditor/EntryEditor.css @@ -114,11 +114,11 @@ .nc-entryEditor-toolbar-backSection, .nc-entryEditor-toolbar-metaSection { border: 0 solid var(--textFieldBorderColor); - cursor: pointer; } .nc-entryEditor-toolbar-backSection { border-right-width: 1px; + cursor: pointer; } .nc-entryEditor-toolbar-metaSection { @@ -161,8 +161,61 @@ padding-right: 20px; } -.nc-entryEditor-toolbar-saveButton { +.nc-entryEditor-toolbar-saveDropdown { + position: relative; + font-size: 14px; +} + +.nc-entryEditor-toolbar-saveDropdownButton { + @apply --button; + display: block; background-color: #17a2b8; padding-left: 20px; - padding-right: 20px; + padding-right: 30px; + position: relative; + + &:after { + content: ''; + display: block; + position: absolute; + top: 16px; + right: 12px; + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 5px solid #fff; + border-radius: 2px; + } +} + +.nc-entryEditor-toolbar-saveDropdownList { + position: absolute; + top: 40px; + left: 0; + width: 100%; + background-color: #fff; + border-radius: var(--borderRadius); + overflow: hidden; + box-shadow: 0 4px 12px 0 rgba(68,74,87,0.15), 0 1px 3px 0 rgba(68,74,87,0.25); +} + +.nc-entryEditor-toolbar-saveDropdownItem { + @apply --button; + background-color: transparent; + border-radius: 0; + color: var(--textColor); + font-weight: normal; + border-bottom: 1px solid #eaebf1; + padding: 10px 30px 10px 14px; + + &:last-of-type { + border-bottom: 0; + } + + &:hover, + &:active, + &:focus { + background-color: #e8f5fe; + } } diff --git a/src/components/EntryEditor/EntryEditorToolbar.js b/src/components/EntryEditor/EntryEditorToolbar.js index 3486c9272468..4feb4340a73b 100644 --- a/src/components/EntryEditor/EntryEditorToolbar.js +++ b/src/components/EntryEditor/EntryEditorToolbar.js @@ -2,6 +2,12 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import React from 'react'; import { Button } from 'react-toolbox/lib/button'; +import { + Wrapper as Dropdown, + Button as DropdownButton, + Menu as DropdownList, + MenuItem as DropdownItem, +} from 'react-aria-menubutton'; const EntryEditorToolbar = ( { @@ -35,8 +41,18 @@ const EntryEditorToolbar = ( : null } + + + { isPersisting ? 'Saving...' : 'Save and Publish' } + + +
    + Save and publish + Save, publish, and create new +
+
+
From 5446b8503ba75fc49ca950e0406ab2b8d8f0fbd2 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Tue, 7 Nov 2017 23:44:38 -0500 Subject: [PATCH 012/134] finished basic editor toolbar --- src/components/EntryEditor/EntryEditor.css | 20 +++-- .../EntryEditor/EntryEditorToolbar.js | 20 +++-- src/icons/Icon.css | 15 +++- src/icons/Icon.js | 83 +++++++++++++++---- src/icons/icon-arrow.svg | 3 + 5 files changed, 109 insertions(+), 32 deletions(-) create mode 100644 src/icons/icon-arrow.svg diff --git a/src/components/EntryEditor/EntryEditor.css b/src/components/EntryEditor/EntryEditor.css index 61fac3e93c43..863d1ed4f0f3 100644 --- a/src/components/EntryEditor/EntryEditor.css +++ b/src/components/EntryEditor/EntryEditor.css @@ -164,6 +164,7 @@ .nc-entryEditor-toolbar-saveDropdown { position: relative; font-size: 14px; + user-select: none; } .nc-entryEditor-toolbar-saveDropdownButton { @@ -171,15 +172,14 @@ display: block; background-color: #17a2b8; padding-left: 20px; - padding-right: 30px; - position: relative; + padding-right: 40px; &:after { content: ''; display: block; position: absolute; top: 16px; - right: 12px; + right: 16px; width: 0; height: 0; border-left: 4px solid transparent; @@ -191,7 +191,7 @@ .nc-entryEditor-toolbar-saveDropdownList { position: absolute; - top: 40px; + top: 39px; left: 0; width: 100%; background-color: #fff; @@ -207,7 +207,10 @@ color: var(--textColor); font-weight: normal; border-bottom: 1px solid #eaebf1; - padding: 10px 30px 10px 14px; + padding: 10px 14px; + display: flex; + justify-content: space-between; + align-items: center; &:last-of-type { border-bottom: 0; @@ -219,3 +222,10 @@ background-color: #e8f5fe; } } + +.nc-entryEditor-toolbar-saveDropdownItemIcon { + flex: 1 0 32px; + text-align: right; + position: relative; + top: 2px; +} diff --git a/src/components/EntryEditor/EntryEditorToolbar.js b/src/components/EntryEditor/EntryEditorToolbar.js index 4feb4340a73b..d1cc236f9766 100644 --- a/src/components/EntryEditor/EntryEditorToolbar.js +++ b/src/components/EntryEditor/EntryEditorToolbar.js @@ -1,13 +1,13 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import React from 'react'; -import { Button } from 'react-toolbox/lib/button'; import { Wrapper as Dropdown, Button as DropdownButton, Menu as DropdownList, MenuItem as DropdownItem, } from 'react-aria-menubutton'; +import Icon from '../../icons/Icon'; const EntryEditorToolbar = ( { @@ -40,16 +40,24 @@ const EntryEditorToolbar = ( ? : null } - - + handler()}> { isPersisting ? 'Saving...' : 'Save and Publish' }
    - Save and publish - Save, publish, and create new + + Save and publish + + + + + + Save, publish, and create new + + + +
diff --git a/src/icons/Icon.css b/src/icons/Icon.css index 30531a7e450c..8b76436a9de1 100644 --- a/src/icons/Icon.css +++ b/src/icons/Icon.css @@ -1,10 +1,19 @@ .nc-icon { - width: 24px; - height: 24px; display: inline-block; & path, - & circle { + & circle, + & polygon { fill: currentColor; } } + +.nc-icon-small { + width: 18px; + height: 18px; +} + +.nc-icon-medium { + width: 24px; + height: 24px; +} diff --git a/src/icons/Icon.js b/src/icons/Icon.js index 2957c9cb885d..dcceea2c596f 100644 --- a/src/icons/Icon.js +++ b/src/icons/Icon.js @@ -1,5 +1,6 @@ import React from 'react'; import iconAdd from './icon-add.svg'; +import iconArrow from './icon-arrow.svg'; import iconCircle from './icon-circle.svg'; import iconFolder from './icon-folder.svg'; import iconGrid from './icon-grid.svg'; @@ -14,29 +15,75 @@ import iconSettings from './icon-settings.svg'; import iconWorkflow from './icon-workflow.svg'; import iconWrite from './icon-write.svg'; -const svgIcons = { - 'add': iconAdd, - 'circle': iconCircle, - 'folder': iconFolder, - 'grid': iconGrid, - 'home': iconHome, - 'list': iconList, - 'media': iconMedia, - 'media-alt': iconMediaAlt, - 'page': iconPage, - 'pages': iconPages, - 'pages-alt': iconPagesAlt, - 'settings': iconSettings, - 'workflow': iconWorkflow, - 'write': iconWrite, +const icons = { + 'add': { + image: iconAdd, + }, + 'arrow': { + image: iconArrow, + direction: 'left', + }, + 'circle': { + image: iconCircle, + }, + 'folder': { + image: iconFolder, + }, + 'grid': { + image: iconGrid, + }, + 'home': { + image: iconHome, + }, + 'list': { + image: iconList, + }, + 'media': { + image: iconMedia, + }, + 'media-alt': { + image: iconMediaAlt, + }, + 'page': { + image: iconPage, + }, + 'pages': { + image: iconPages, + }, + 'pages-alt': { + image: iconPagesAlt, + }, + 'settings': { + image: iconSettings, + }, + 'workflow': { + image: iconWorkflow, + }, + 'write': { + image: iconWrite, + }, }; +const getRotation = (iconDirection, newDirection) => { + if (!iconDirection || !newDirection) { + return '0deg'; + } + const rotations = { right: 90, down: 180, left: 270, up: 360 }; + const degrees = rotations[newDirection] - rotations[iconDirection]; + return `${degrees}deg`; +} + const Icon = props => { - const { type, className = '', ...remainingProps } = props; + const { type, direction, size = 'medium', className = '', ...remainingProps } = props; + const icon = icons[type]; + const rotation = getRotation(icon.direction, direction) + const transform = `rotate(${rotation})`; + const style = { transform }; return ( ); diff --git a/src/icons/icon-arrow.svg b/src/icons/icon-arrow.svg new file mode 100644 index 000000000000..5cf58e50dee7 --- /dev/null +++ b/src/icons/icon-arrow.svg @@ -0,0 +1,3 @@ + + + From 2c898926d4b79f1a353d69bb7601dd320130579a Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Thu, 9 Nov 2017 18:14:12 -0500 Subject: [PATCH 013/134] add standalone toggle component --- package.json | 1 + src/components/UI/Toggle/Toggle.css | 31 ++++++++ src/components/UI/Toggle/Toggle.js | 21 ++++++ .../MarkdownControl/Toolbar/Toolbar.css | 8 +-- .../MarkdownControl/Toolbar/Toolbar.js | 71 ++++++++++--------- .../MarkdownControl/Toolbar/ToolbarButton.css | 6 +- .../MarkdownControl/Toolbar/ToolbarButton.js | 4 +- .../Toolbar/ToolbarComponentsMenu.js | 2 +- src/index.css | 1 + 9 files changed, 101 insertions(+), 44 deletions(-) create mode 100644 src/components/UI/Toggle/Toggle.css create mode 100644 src/components/UI/Toggle/Toggle.js diff --git a/package.json b/package.json index 17775399fb8a..de1cb979092d 100644 --- a/package.json +++ b/package.json @@ -161,6 +161,7 @@ "react-router-redux": "^5.0.0-alpha.8", "react-sortable-hoc": "^0.6.8", "react-split-pane": "^0.1.66", + "react-toggled": "^1.1.2", "react-toolbox": "^2.0.0-beta.12", "react-topbar-progress-indicator": "^2.0.0", "react-transition-group": "^2.2.1", diff --git a/src/components/UI/Toggle/Toggle.css b/src/components/UI/Toggle/Toggle.css new file mode 100644 index 000000000000..c3ec65170586 --- /dev/null +++ b/src/components/UI/Toggle/Toggle.css @@ -0,0 +1,31 @@ +.nc-toggle { + display: flex; + align-items: center; + justify-content: center; + position: relative; + width: 40px; + height: 20px; + cursor: pointer; +} + +.nc-toggle-background { + width: 34px; + height: 14px; + border-radius: 10px; + background-color: #3a69c7; +} + +.nc-toggle-switch { + position: absolute; + left: 0; + width: 20px; + height: 20px; + border-radius: 50%; + background-color: white; + transition: transform 0.2s; + box-shadow: 0 2px 6px 0 rgba(68,74,87,0.15), 0 1px 3px 0 rgba(68,74,87,0.30); +} + +.nc-toggle-active .nc-toggle-switch { + transform: translateX(20px); +} diff --git a/src/components/UI/Toggle/Toggle.js b/src/components/UI/Toggle/Toggle.js new file mode 100644 index 000000000000..4d06a01719ef --- /dev/null +++ b/src/components/UI/Toggle/Toggle.js @@ -0,0 +1,21 @@ +import React from 'react' +import Toggle from 'react-toggled'; +import c from 'classnames'; + +const Switch = () => ( + + {({on, getElementTogglerProps}) => ( + + + + + )} + +); + +export default Switch; diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css index 5f78c5f7b78d..cd6a36695eb2 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css @@ -1,9 +1,11 @@ .nc-toolbar-Toolbar { z-index: 0; background-color: var(--textFieldBorderColor); - padding: 10px; border-top-right-radius: var(--borderRadius); position: relative; + display: flex; + justify-content: space-between; + align-items: center; &:after { content: ''; @@ -22,8 +24,4 @@ } .nc-toolbar-Toggle { - float: right; - margin-bottom: 0; - padding-top: 4px; - padding-right: 10px; } diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.js b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.js index 23b7a7b2655e..74907d13437c 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.js +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.js @@ -2,11 +2,10 @@ import PropTypes from 'prop-types'; import React from 'react'; import { List } from 'immutable'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import Switch from 'react-toolbox/lib/switch'; +import Switch from '../../../../UI/Toggle/Toggle'; import ToolbarButton from './ToolbarButton'; import ToolbarComponentsMenu from './ToolbarComponentsMenu'; import ToolbarPluginForm from './ToolbarPluginForm'; -import { Icon } from '../../../../UI'; export default class Toolbar extends React.Component { static propTypes = { @@ -57,45 +56,47 @@ export default class Toolbar extends React.Component { const { activePlugin } = this.state; const buttonsConfig = [ - { label: 'Bold', icon: 'bold', state: buttons.bold }, - { label: 'Italic', icon: 'italic', state: buttons.italic }, - { label: 'Code', icon: 'code-alt', state: buttons.code }, - { label: 'Header 1', icon: 'h1', state: buttons.h1 }, - { label: 'Header 2', icon: 'h2', state: buttons.h2 }, - { label: 'Code Block', icon: 'code', state: buttons.codeBlock }, - { label: 'Quote', icon: 'quote', state: buttons.quote }, - { label: 'Bullet List', icon: 'list-bullet', state: buttons.list }, - { label: 'Numbered List', icon: 'list-numbered', state: buttons.listNumbered }, - { label: 'Link', icon: 'link', state: buttons.link }, + { label: 'Bold', icon: 'list', state: buttons.bold }, + { label: 'Italic', icon: 'list', state: buttons.italic }, + { label: 'Code', icon: 'list', state: buttons.code }, + { label: 'Header 1', icon: 'list', state: buttons.h1 }, + { label: 'Header 2', icon: 'list', state: buttons.h2 }, + { label: 'Code Block', icon: 'list', state: buttons.codeBlock }, + { label: 'Quote', icon: 'list', state: buttons.quote }, + { label: 'Bullet List', icon: 'list', state: buttons.list }, + { label: 'Numbered List', icon: 'list', state: buttons.listNumbered }, + { label: 'Link', icon: 'list', state: buttons.link }, ]; return (
- { buttonsConfig.map((btn, i) => ( - {})} - active={btn.state && btn.state.active} +
+ { buttonsConfig.map((btn, i) => ( + {})} + active={btn.state && btn.state.active} + disabled={disabled} + {...btn} + /> + ))} + - ))} - - {activePlugin && - - } - + {activePlugin && + + } +
+
); } diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css index bad3f2d8ff03..2727d0f6cdeb 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css @@ -1,6 +1,6 @@ .nc-toolbarButton-button { display: inline-block; - padding: 6px; + padding: 8px; border: none; background-color: transparent; color: var(--controlLabelColor); @@ -11,6 +11,10 @@ cursor: auto; opacity: 0.5; } + + & .nc-icon { + display: block; + } } .nc-toolbarButton-active { diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.js b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.js index 2fdac53a0cf4..6473dc26c7cf 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.js +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import classnames from 'classnames'; -import { Icon } from '../../../../UI'; +import Icon from '../../../../../icons/Icon'; const ToolbarButton = ({ label, icon, action, active, disabled }) => ( ); diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarComponentsMenu.js b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarComponentsMenu.js index 91260d3d3037..12dd79ddead0 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarComponentsMenu.js +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarComponentsMenu.js @@ -31,7 +31,7 @@ export default class ToolbarComponentsMenu extends React.Component {
diff --git a/src/index.css b/src/index.css index 024d925615db..9776d704bea7 100644 --- a/src/index.css +++ b/src/index.css @@ -19,6 +19,7 @@ @import "./components/UI/toast/Toast.css"; @import "./components/UI/Dialog/Dialog.css"; @import "./components/UI/ErrorBoundary/ErrorBoundary.css"; +@import "./components/UI/Toggle/Toggle.css"; @import "./components/UnpublishedListing/UnpublishedListing.css"; @import "./components/UnpublishedListing/UnpublishedListingCardMeta.css"; @import "./components/Widgets/BooleanControl.css"; From 4e6addaeb60a38bd01226ee510474267e23cc48b Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Fri, 10 Nov 2017 15:24:17 -0500 Subject: [PATCH 014/134] improve markdown toolbar spacing --- src/components/UI/Toggle/Toggle.js | 4 ++-- .../Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.css | 1 + .../Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/UI/Toggle/Toggle.js b/src/components/UI/Toggle/Toggle.js index 4d06a01719ef..5d72608566ac 100644 --- a/src/components/UI/Toggle/Toggle.js +++ b/src/components/UI/Toggle/Toggle.js @@ -2,8 +2,8 @@ import React from 'react' import Toggle from 'react-toggled'; import c from 'classnames'; -const Switch = () => ( - +const Switch = ({ active, onChange }) => ( + {({on, getElementTogglerProps}) => ( }
- +
); } From 09cad805f66fdd3ecec99263df918becf2f5024b Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Fri, 10 Nov 2017 15:24:44 -0500 Subject: [PATCH 015/134] add user avatar placeholder --- src/App/Header.css | 16 ++++++++++++---- src/App/Header.js | 7 ++++++- src/components/EntryEditor/EntryEditorToolbar.js | 7 ++++++- src/icons/Icon.js | 4 ++++ src/icons/icon-user.svg | 5 +++++ 5 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 src/icons/icon-user.svg diff --git a/src/App/Header.css b/src/App/Header.css index 3c389a266214..a6c819069bf6 100644 --- a/src/App/Header.css +++ b/src/App/Header.css @@ -77,10 +77,18 @@ border: 0; padding: 8px; cursor: pointer; + color: #1e2532; background-color: transparent; +} - & img { - width: 32px; - border-radius: 40px; - } +.nc-appHeader-avatar-image, +.nc-appHeader-avatar-placeholder { + color: #1e2532; + width: 32px; + border-radius: 32px; +} + +.nc-appHeader-avatar-placeholder { + height: 32px; + background-color: var(--textFieldBorderColor); } diff --git a/src/App/Header.js b/src/App/Header.js index 80e5e0079325..27006e48634b 100644 --- a/src/App/Header.js +++ b/src/App/Header.js @@ -45,6 +45,7 @@ export default class Header extends React.Component { }; */ + const avatarUrl = user.get('avatar_url'); return (
diff --git a/src/components/EntryEditor/EntryEditorToolbar.js b/src/components/EntryEditor/EntryEditorToolbar.js index d1cc236f9766..0041642c7f46 100644 --- a/src/components/EntryEditor/EntryEditorToolbar.js +++ b/src/components/EntryEditor/EntryEditorToolbar.js @@ -21,6 +21,7 @@ const EntryEditorToolbar = ( hasChanged, }) => { const disabled = !enableSave || isPersisting; + const avatarUrl = user.get('avatar_url'); return (
@@ -67,7 +68,11 @@ const EntryEditorToolbar = ( olddvdscreensaver.com
diff --git a/src/icons/Icon.js b/src/icons/Icon.js index dcceea2c596f..fe9ecc6a6d53 100644 --- a/src/icons/Icon.js +++ b/src/icons/Icon.js @@ -12,6 +12,7 @@ import iconPage from './icon-page.svg'; import iconPages from './icon-pages.svg'; import iconPagesAlt from './icon-pages-alt.svg'; import iconSettings from './icon-settings.svg'; +import iconUser from './icon-user.svg'; import iconWorkflow from './icon-workflow.svg'; import iconWrite from './icon-write.svg'; @@ -56,6 +57,9 @@ const icons = { 'settings': { image: iconSettings, }, + 'user': { + image: iconUser, + }, 'workflow': { image: iconWorkflow, }, diff --git a/src/icons/icon-user.svg b/src/icons/icon-user.svg new file mode 100644 index 000000000000..b8e9574e056b --- /dev/null +++ b/src/icons/icon-user.svg @@ -0,0 +1,5 @@ + + + + + From 62ae33dcba4d20c5ad80edaec5b5cca06892bb69 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Fri, 10 Nov 2017 16:31:24 -0500 Subject: [PATCH 016/134] finish markdown toggle styling --- src/components/UI/Toggle/Toggle.css | 2 +- src/components/UI/Toggle/Toggle.js | 4 +- .../MarkdownControl/Toolbar/Toolbar.css | 21 ++++++- .../MarkdownControl/Toolbar/Toolbar.js | 57 +++++++++++++++---- 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/components/UI/Toggle/Toggle.css b/src/components/UI/Toggle/Toggle.css index c3ec65170586..5d99de90fd27 100644 --- a/src/components/UI/Toggle/Toggle.css +++ b/src/components/UI/Toggle/Toggle.css @@ -1,5 +1,5 @@ .nc-toggle { - display: flex; + display: inline-flex; align-items: center; justify-content: center; position: relative; diff --git a/src/components/UI/Toggle/Toggle.js b/src/components/UI/Toggle/Toggle.js index 5d72608566ac..45c29701e307 100644 --- a/src/components/UI/Toggle/Toggle.js +++ b/src/components/UI/Toggle/Toggle.js @@ -2,11 +2,11 @@ import React from 'react' import Toggle from 'react-toggled'; import c from 'classnames'; -const Switch = ({ active, onChange }) => ( +const Switch = ({ active, onChange, className }) => ( {({on, getElementTogglerProps}) => (
@@ -85,18 +96,44 @@ export default class Toolbar extends React.Component { onComponentMenuItemClick={this.handlePluginFormDisplay} disabled={disabled} /> - {activePlugin && - + { + activePlugin + ? + : null }
- +
+ + {toggleOffLabel} + + + + {toggleOnLabel} + +
); } From 5f454b4eca281bead410ebf00b601f483ca0933e Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Fri, 10 Nov 2017 18:17:14 -0500 Subject: [PATCH 017/134] refactor icon setup, add new icons --- src/backends/github/AuthenticationPage.js | 8 +- src/icons/Icon.js | 77 +++---------------- src/icons/icons.js | 37 +++++++++ src/icons/images/add-with.svg | 3 + src/icons/{icon-add.svg => images/add.svg} | 0 .../{icon-arrow.svg => images/arrow.svg} | 0 src/icons/images/bold.svg | 3 + .../{icon-circle.svg => images/circle.svg} | 0 src/icons/images/code-block.svg | 3 + src/icons/images/code.svg | 3 + .../{icon-folder.svg => images/folder.svg} | 0 src/icons/images/github.svg | 3 + src/icons/{icon-grid.svg => images/grid.svg} | 0 src/icons/images/h1.svg | 3 + src/icons/images/h2.svg | 3 + src/icons/{icon-home.svg => images/home.svg} | 0 src/icons/images/image.svg | 3 + src/icons/images/index.js | 63 +++++++++++++++ src/icons/images/italic.svg | 3 + src/icons/images/link.svg | 3 + src/icons/images/list-dotted.svg | 3 + src/icons/images/list-numbered.svg | 3 + src/icons/{icon-list.svg => images/list.svg} | 0 .../media-alt.svg} | 0 .../{icon-media.svg => images/media.svg} | 0 src/icons/{icon-page.svg => images/page.svg} | 0 .../pages-alt.svg} | 0 .../{icon-pages.svg => images/pages.svg} | 0 src/icons/images/quote.svg | 3 + .../settings.svg} | 0 src/icons/{icon-user.svg => images/user.svg} | 0 .../workflow.svg} | 0 .../{icon-write.svg => images/write.svg} | 0 33 files changed, 151 insertions(+), 73 deletions(-) create mode 100644 src/icons/icons.js create mode 100644 src/icons/images/add-with.svg rename src/icons/{icon-add.svg => images/add.svg} (100%) rename src/icons/{icon-arrow.svg => images/arrow.svg} (100%) create mode 100644 src/icons/images/bold.svg rename src/icons/{icon-circle.svg => images/circle.svg} (100%) create mode 100644 src/icons/images/code-block.svg create mode 100644 src/icons/images/code.svg rename src/icons/{icon-folder.svg => images/folder.svg} (100%) create mode 100644 src/icons/images/github.svg rename src/icons/{icon-grid.svg => images/grid.svg} (100%) create mode 100644 src/icons/images/h1.svg create mode 100644 src/icons/images/h2.svg rename src/icons/{icon-home.svg => images/home.svg} (100%) create mode 100644 src/icons/images/image.svg create mode 100644 src/icons/images/index.js create mode 100644 src/icons/images/italic.svg create mode 100644 src/icons/images/link.svg create mode 100644 src/icons/images/list-dotted.svg create mode 100644 src/icons/images/list-numbered.svg rename src/icons/{icon-list.svg => images/list.svg} (100%) rename src/icons/{icon-media-alt.svg => images/media-alt.svg} (100%) rename src/icons/{icon-media.svg => images/media.svg} (100%) rename src/icons/{icon-page.svg => images/page.svg} (100%) rename src/icons/{icon-pages-alt.svg => images/pages-alt.svg} (100%) rename src/icons/{icon-pages.svg => images/pages.svg} (100%) create mode 100644 src/icons/images/quote.svg rename src/icons/{icon-settings.svg => images/settings.svg} (100%) rename src/icons/{icon-user.svg => images/user.svg} (100%) rename src/icons/{icon-workflow.svg => images/workflow.svg} (100%) rename src/icons/{icon-write.svg => images/write.svg} (100%) diff --git a/src/backends/github/AuthenticationPage.js b/src/backends/github/AuthenticationPage.js index 71ec7a06ea9c..96d1be755763 100644 --- a/src/backends/github/AuthenticationPage.js +++ b/src/backends/github/AuthenticationPage.js @@ -1,8 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; -import Button from 'react-toolbox/lib/button'; import Authenticator from '../../lib/netlify-auth'; -import { Icon } from '../../components/UI'; +import Icon from '../../icons/Icon'; import { Notifs } from 'redux-notifications'; import { Toast } from '../../components/UI/index'; @@ -39,14 +38,13 @@ export default class AuthenticationPage extends React.Component {
{loginError &&

{loginError}

} - +
); } diff --git a/src/icons/Icon.js b/src/icons/Icon.js index fe9ecc6a6d53..5580c92ff0f0 100644 --- a/src/icons/Icon.js +++ b/src/icons/Icon.js @@ -1,73 +1,14 @@ import React from 'react'; -import iconAdd from './icon-add.svg'; -import iconArrow from './icon-arrow.svg'; -import iconCircle from './icon-circle.svg'; -import iconFolder from './icon-folder.svg'; -import iconGrid from './icon-grid.svg'; -import iconHome from './icon-home.svg'; -import iconList from './icon-list.svg'; -import iconMedia from './icon-media.svg'; -import iconMediaAlt from './icon-media-alt.svg'; -import iconPage from './icon-page.svg'; -import iconPages from './icon-pages.svg'; -import iconPagesAlt from './icon-pages-alt.svg'; -import iconSettings from './icon-settings.svg'; -import iconUser from './icon-user.svg'; -import iconWorkflow from './icon-workflow.svg'; -import iconWrite from './icon-write.svg'; - -const icons = { - 'add': { - image: iconAdd, - }, - 'arrow': { - image: iconArrow, - direction: 'left', - }, - 'circle': { - image: iconCircle, - }, - 'folder': { - image: iconFolder, - }, - 'grid': { - image: iconGrid, - }, - 'home': { - image: iconHome, - }, - 'list': { - image: iconList, - }, - 'media': { - image: iconMedia, - }, - 'media-alt': { - image: iconMediaAlt, - }, - 'page': { - image: iconPage, - }, - 'pages': { - image: iconPages, - }, - 'pages-alt': { - image: iconPagesAlt, - }, - 'settings': { - image: iconSettings, - }, - 'user': { - image: iconUser, - }, - 'workflow': { - image: iconWorkflow, - }, - 'write': { - image: iconWrite, - }, -}; +import icons from './icons'; +/** + * Calculates rotation for icons that have a `direction` property configured + * in the imported icon definition object. If no direction is configured, a + * neutral rotation value is returned. + * + * Returned value is a string of shape `${degrees}deg`, for use in a CSS + * transform. + */ const getRotation = (iconDirection, newDirection) => { if (!iconDirection || !newDirection) { return '0deg'; diff --git a/src/icons/icons.js b/src/icons/icons.js new file mode 100644 index 000000000000..8988c1f4e567 --- /dev/null +++ b/src/icons/icons.js @@ -0,0 +1,37 @@ +import { mapValues } from 'lodash'; +import images from './images/index'; + +/** + * This module outputs icon objects with the following shape: + * + * { + * image: ..., + * ...props + * } + * + * `props` here are config properties defined in this file for specific icons. + * For example, an icon may face a specific direction, and the Icon component + * accepts a `direction` prop to rotate directional icons, which relies on + * defining the default direction here. + */ + +/** + * Configuration for individual icons. + */ +const config = { + 'arrow': { + direction: 'left', + }, +}; + +/** + * Map icon definition objects - imported object of images simply maps the icon + * name to the raw svg, so we move that to the `image` property of the + * definition object and set any additional configured properties for each icon. + */ +const icons = mapValues(images, (image, name) => { + const props = config[name] || {}; + return { image, ...props }; +}); + +export default icons; diff --git a/src/icons/images/add-with.svg b/src/icons/images/add-with.svg new file mode 100644 index 000000000000..d27c315eec4c --- /dev/null +++ b/src/icons/images/add-with.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-add.svg b/src/icons/images/add.svg similarity index 100% rename from src/icons/icon-add.svg rename to src/icons/images/add.svg diff --git a/src/icons/icon-arrow.svg b/src/icons/images/arrow.svg similarity index 100% rename from src/icons/icon-arrow.svg rename to src/icons/images/arrow.svg diff --git a/src/icons/images/bold.svg b/src/icons/images/bold.svg new file mode 100644 index 000000000000..71bf08e4f661 --- /dev/null +++ b/src/icons/images/bold.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-circle.svg b/src/icons/images/circle.svg similarity index 100% rename from src/icons/icon-circle.svg rename to src/icons/images/circle.svg diff --git a/src/icons/images/code-block.svg b/src/icons/images/code-block.svg new file mode 100644 index 000000000000..39c02dc4c650 --- /dev/null +++ b/src/icons/images/code-block.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/images/code.svg b/src/icons/images/code.svg new file mode 100644 index 000000000000..e7093b7a7804 --- /dev/null +++ b/src/icons/images/code.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-folder.svg b/src/icons/images/folder.svg similarity index 100% rename from src/icons/icon-folder.svg rename to src/icons/images/folder.svg diff --git a/src/icons/images/github.svg b/src/icons/images/github.svg new file mode 100644 index 000000000000..ab49685926a6 --- /dev/null +++ b/src/icons/images/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-grid.svg b/src/icons/images/grid.svg similarity index 100% rename from src/icons/icon-grid.svg rename to src/icons/images/grid.svg diff --git a/src/icons/images/h1.svg b/src/icons/images/h1.svg new file mode 100644 index 000000000000..8d8c5e29f2bb --- /dev/null +++ b/src/icons/images/h1.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/images/h2.svg b/src/icons/images/h2.svg new file mode 100644 index 000000000000..8ae81f84ebc6 --- /dev/null +++ b/src/icons/images/h2.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-home.svg b/src/icons/images/home.svg similarity index 100% rename from src/icons/icon-home.svg rename to src/icons/images/home.svg diff --git a/src/icons/images/image.svg b/src/icons/images/image.svg new file mode 100644 index 000000000000..8f23e2905b5d --- /dev/null +++ b/src/icons/images/image.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/images/index.js b/src/icons/images/index.js new file mode 100644 index 000000000000..56df8a614253 --- /dev/null +++ b/src/icons/images/index.js @@ -0,0 +1,63 @@ +import iconAdd from './add.svg'; +import iconAddWith from './add-with.svg'; +import iconArrow from './arrow.svg'; +import iconBold from './bold.svg'; +import iconCircle from './circle.svg'; +import iconCode from './code.svg'; +import iconCodeBlock from './code-block.svg'; +import iconFolder from './folder.svg'; +import iconGithub from './github.svg'; +import iconGrid from './grid.svg'; +import iconH1 from './h1.svg'; +import iconH2 from './h2.svg'; +import iconHome from './home.svg'; +import iconImage from './image.svg'; +import iconItalic from './italic.svg'; +import iconLink from './link.svg'; +import iconList from './list.svg'; +import iconListDotted from './list-dotted.svg'; +import iconListNumbered from './list-numbered.svg'; +import iconMedia from './media.svg'; +import iconMediaAlt from './media-alt.svg'; +import iconPage from './page.svg'; +import iconPages from './pages.svg'; +import iconPagesAlt from './pages-alt.svg'; +import iconQuote from './quote.svg'; +import iconSettings from './settings.svg'; +import iconUser from './user.svg'; +import iconWorkflow from './workflow.svg'; +import iconWrite from './write.svg'; + +const images = { + 'add': iconAdd, + 'add-with': iconAddWith, + 'arrow': iconArrow, + 'bold': iconBold, + 'circle': iconCircle, + 'code': iconCode, + 'code-block': iconCodeBlock, + 'folder': iconFolder, + 'github': iconGithub, + 'grid': iconGrid, + 'h1': iconH1, + 'h2': iconH2, + 'home': iconHome, + 'image': iconImage, + 'italic': iconItalic, + 'link': iconLink, + 'list': iconList, + 'list-dotted': iconListDotted, + 'list-numbered': iconListNumbered, + 'media': iconMedia, + 'media-alt': iconMediaAlt, + 'page': iconPage, + 'pages': iconPages, + 'pages-alt': iconPagesAlt, + 'quote': iconQuote, + 'settings': iconSettings, + 'user': iconUser, + 'workflow': iconWorkflow, + 'write': iconWrite, +}; + +export default images; diff --git a/src/icons/images/italic.svg b/src/icons/images/italic.svg new file mode 100644 index 000000000000..b8ed0aa770fa --- /dev/null +++ b/src/icons/images/italic.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/images/link.svg b/src/icons/images/link.svg new file mode 100644 index 000000000000..42de6a0a613f --- /dev/null +++ b/src/icons/images/link.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/images/list-dotted.svg b/src/icons/images/list-dotted.svg new file mode 100644 index 000000000000..0d1253dbf88f --- /dev/null +++ b/src/icons/images/list-dotted.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/images/list-numbered.svg b/src/icons/images/list-numbered.svg new file mode 100644 index 000000000000..4c5f3a887207 --- /dev/null +++ b/src/icons/images/list-numbered.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-list.svg b/src/icons/images/list.svg similarity index 100% rename from src/icons/icon-list.svg rename to src/icons/images/list.svg diff --git a/src/icons/icon-media-alt.svg b/src/icons/images/media-alt.svg similarity index 100% rename from src/icons/icon-media-alt.svg rename to src/icons/images/media-alt.svg diff --git a/src/icons/icon-media.svg b/src/icons/images/media.svg similarity index 100% rename from src/icons/icon-media.svg rename to src/icons/images/media.svg diff --git a/src/icons/icon-page.svg b/src/icons/images/page.svg similarity index 100% rename from src/icons/icon-page.svg rename to src/icons/images/page.svg diff --git a/src/icons/icon-pages-alt.svg b/src/icons/images/pages-alt.svg similarity index 100% rename from src/icons/icon-pages-alt.svg rename to src/icons/images/pages-alt.svg diff --git a/src/icons/icon-pages.svg b/src/icons/images/pages.svg similarity index 100% rename from src/icons/icon-pages.svg rename to src/icons/images/pages.svg diff --git a/src/icons/images/quote.svg b/src/icons/images/quote.svg new file mode 100644 index 000000000000..e61c5a78996f --- /dev/null +++ b/src/icons/images/quote.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icon-settings.svg b/src/icons/images/settings.svg similarity index 100% rename from src/icons/icon-settings.svg rename to src/icons/images/settings.svg diff --git a/src/icons/icon-user.svg b/src/icons/images/user.svg similarity index 100% rename from src/icons/icon-user.svg rename to src/icons/images/user.svg diff --git a/src/icons/icon-workflow.svg b/src/icons/images/workflow.svg similarity index 100% rename from src/icons/icon-workflow.svg rename to src/icons/images/workflow.svg diff --git a/src/icons/icon-write.svg b/src/icons/images/write.svg similarity index 100% rename from src/icons/icon-write.svg rename to src/icons/images/write.svg From 06cd5c3cb9aa9e1174fa4bcf317932e699bc3f39 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Fri, 10 Nov 2017 21:38:33 -0500 Subject: [PATCH 018/134] add new icons to markdown editor toolbar --- .../MarkdownControl/Toolbar/Toolbar.js | 20 +++++++++---------- .../MarkdownControl/Toolbar/ToolbarButton.css | 2 +- .../MarkdownControl/Toolbar/ToolbarButton.js | 2 +- src/icons/images/index.js | 4 ++-- .../{list-dotted.svg => list-bulleted.svg} | 0 5 files changed, 14 insertions(+), 14 deletions(-) rename src/icons/images/{list-dotted.svg => list-bulleted.svg} (100%) diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.js b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.js index bdc91087f52d..e60630d02ea4 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.js +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/Toolbar.js @@ -57,16 +57,16 @@ export default class Toolbar extends React.Component { const { activePlugin } = this.state; const buttonsConfig = [ - { label: 'Bold', icon: 'list', state: buttons.bold }, - { label: 'Italic', icon: 'list', state: buttons.italic }, - { label: 'Code', icon: 'list', state: buttons.code }, - { label: 'Header 1', icon: 'list', state: buttons.h1 }, - { label: 'Header 2', icon: 'list', state: buttons.h2 }, - { label: 'Code Block', icon: 'list', state: buttons.codeBlock }, - { label: 'Quote', icon: 'list', state: buttons.quote }, - { label: 'Bullet List', icon: 'list', state: buttons.list }, - { label: 'Numbered List', icon: 'list', state: buttons.listNumbered }, - { label: 'Link', icon: 'list', state: buttons.link }, + { label: 'Bold', icon: 'bold', state: buttons.bold }, + { label: 'Italic', icon: 'italic', state: buttons.italic }, + { label: 'Code', icon: 'code', state: buttons.code }, + { label: 'Header 1', icon: 'h1', state: buttons.h1 }, + { label: 'Header 2', icon: 'h2', state: buttons.h2 }, + { label: 'Code Block', icon: 'code-block', state: buttons.codeBlock }, + { label: 'Quote', icon: 'quote', state: buttons.quote }, + { label: 'Bullet List', icon: 'list-bulleted', state: buttons.list }, + { label: 'Numbered List', icon: 'list-numbered', state: buttons.listNumbered }, + { label: 'Link', icon: 'link', state: buttons.link }, ]; /** diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css index 2727d0f6cdeb..d00f54ab361a 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css @@ -1,6 +1,6 @@ .nc-toolbarButton-button { display: inline-block; - padding: 8px; + padding: 6px; border: none; background-color: transparent; color: var(--controlLabelColor); diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.js b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.js index 6473dc26c7cf..e60980683e7e 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.js +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.js @@ -10,7 +10,7 @@ const ToolbarButton = ({ label, icon, action, active, disabled }) => ( title={label} disabled={disabled} > - { icon ? : label } + { icon ? : label } ); diff --git a/src/icons/images/index.js b/src/icons/images/index.js index 56df8a614253..9bd8d877a1f6 100644 --- a/src/icons/images/index.js +++ b/src/icons/images/index.js @@ -15,7 +15,7 @@ import iconImage from './image.svg'; import iconItalic from './italic.svg'; import iconLink from './link.svg'; import iconList from './list.svg'; -import iconListDotted from './list-dotted.svg'; +import iconListBulleted from './list-bulleted.svg'; import iconListNumbered from './list-numbered.svg'; import iconMedia from './media.svg'; import iconMediaAlt from './media-alt.svg'; @@ -46,7 +46,7 @@ const images = { 'italic': iconItalic, 'link': iconLink, 'list': iconList, - 'list-dotted': iconListDotted, + 'list-bulleted': iconListBulleted, 'list-numbered': iconListNumbered, 'media': iconMedia, 'media-alt': iconMediaAlt, diff --git a/src/icons/images/list-dotted.svg b/src/icons/images/list-bulleted.svg similarity index 100% rename from src/icons/images/list-dotted.svg rename to src/icons/images/list-bulleted.svg From fcac527b89568028d1d4b989e1e30713e960b102 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Sat, 11 Nov 2017 14:54:50 -0500 Subject: [PATCH 019/134] remove extra app container --- src/containers/App.js | 248 ------------------------------------------ 1 file changed, 248 deletions(-) delete mode 100644 src/containers/App.js diff --git a/src/containers/App.js b/src/containers/App.js deleted file mode 100644 index b7414aadb4a7..000000000000 --- a/src/containers/App.js +++ /dev/null @@ -1,248 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { connect } from 'react-redux'; -import { Route, Switch, Link } from 'react-router-dom'; -import FontIcon from 'react-toolbox/lib/font_icon'; -import { Navigation } from 'react-toolbox/lib/navigation'; -import { Notifs } from 'redux-notifications'; -import TopBarProgress from 'react-topbar-progress-indicator'; -import Sidebar from './Sidebar'; -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 { - runCommand as actionRunCommand, - navigateToCollection as actionNavigateToCollection, - createNewEntryInCollection as actionCreateNewEntryInCollection, -} from '../actions/findbar'; -import { openMediaLibrary as actionOpenMediaLibrary } from '../actions/mediaLibrary'; -import AppHeader from '../components/AppHeader/AppHeader'; -import MediaLibrary from '../components/MediaLibrary/MediaLibrary'; -import { Loader, Toast } from '../components/UI/index'; -import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper'; -import { SIMPLE, EDITORIAL_WORKFLOW } from '../constants/publishModes'; -import DashboardPage from './DashboardPage'; -import CollectionPage from './CollectionPage'; -import EntryPage from './EntryPage'; -import SearchPage from './SearchPage'; -import NotFoundPage from './NotFoundPage'; - -TopBarProgress.config({ - barColors: { - "0": '#3ab7a5', - '1.0': '#3ab7a5', - }, - shadowBlur: 5, - shadowColor: '#3ab7a5', - barThickness: 2, -}); - -class App extends React.Component { - - static propTypes = { - auth: ImmutablePropTypes.map, - config: ImmutablePropTypes.map, - collections: ImmutablePropTypes.orderedMap, - createNewEntryInCollection: PropTypes.func.isRequired, - logoutUser: PropTypes.func.isRequired, - dispatch: PropTypes.func.isRequired, - toggleSidebar: PropTypes.func.isRequired, - navigateToCollection: PropTypes.func.isRequired, - user: ImmutablePropTypes.map, - runCommand: PropTypes.func.isRequired, - isFetching: PropTypes.bool.isRequired, - publishMode: PropTypes.oneOf([SIMPLE, EDITORIAL_WORKFLOW]), - siteId: PropTypes.string, - }; - - static configError(config) { - return (
-

Error loading the CMS configuration

- -
-

The config.yml file could not be loaded or failed to parse properly.

-

Error message: {config.get('error')}

-
-
); - } - - componentDidMount() { - this.props.dispatch(actionLoadConfig()); - } - - handleLogin(credentials) { - this.props.dispatch(actionLoginUser(credentials)); - } - - authenticating() { - const { auth } = this.props; - const backend = currentBackend(this.props.config); - - if (backend == null) { - return

Waiting for backend...

; - } - - return ( -
- { - React.createElement(backend.authComponent(), { - onLogin: this.handleLogin.bind(this), - error: auth && auth.get('error'), - inProgress: (auth && auth.get('isFetching')) || false, - siteId: this.props.config.getIn(["backend", "site_domain"]), - base_url: this.props.config.getIn(["backend", "base_url"], null) - }) - } -
- ); - } - - handleLinkClick(event, handler, ...args) { - event.preventDefault(); - handler(...args); - } - - render() { - const { - user, - config, - collections, - toggleSidebar, - runCommand, - navigateToCollection, - createNewEntryInCollection, - logoutUser, - isFetching, - publishMode, - openMediaLibrary, - } = this.props; - - - if (config === null) { - return null; - } - - if (config.get('error')) { - return App.configError(config); - } - - if (config.get('isFetching')) { - return Loading configuration...; - } - - if (user == null) { - return this.authenticating(); - } - - const sidebarContent = ( -
- - { - publishMode === SIMPLE ? null : -
-

Publishing

-
- Editorial Workflow -
-
- } -
-

Collections

- { - collections.valueSeq().map((collection) => { - const collectionName = collection.get('name'); - return ( - - ); - }) - } -
-
-
- ); - - return ( - -
- - -
- { isFetching && } -
- - - - ()} /> - - - - - -
-
-
-
- ); - } -} - -function mapStateToProps(state) { - const { auth, config, collections, globalUI } = state; - const user = auth && auth.get('user'); - const isFetching = globalUI.get('isFetching'); - const publishMode = config && config.get('publish_mode'); - return { auth, config, collections, user, isFetching, publishMode }; -} - -function mapDispatchToProps(dispatch) { - return { - dispatch, - toggleSidebar: () => dispatch(actionToggleSidebar()), - runCommand: (type, payload) => { - dispatch(actionRunCommand(type, payload)); - }, - navigateToCollection: (collection) => { - dispatch(actionNavigateToCollection(collection)); - }, - createNewEntryInCollection: (collectionName) => { - dispatch(actionCreateNewEntryInCollection(collectionName)); - }, - openMediaLibrary: () => { - dispatch(actionOpenMediaLibrary()); - }, - logoutUser: () => { - dispatch(actionLogoutUser()); - }, - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(App); From 91111d9291f166f4b805f094bbd6be1b7bdffdec Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Sat, 11 Nov 2017 15:54:31 -0500 Subject: [PATCH 020/134] add markdown active mark style --- src/App/Header.css | 2 +- .../Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App/Header.css b/src/App/Header.css index a6c819069bf6..566a6f9a99ba 100644 --- a/src/App/Header.css +++ b/src/App/Header.css @@ -83,12 +83,12 @@ .nc-appHeader-avatar-image, .nc-appHeader-avatar-placeholder { - color: #1e2532; width: 32px; border-radius: 32px; } .nc-appHeader-avatar-placeholder { height: 32px; + color: #1e2532; background-color: var(--textFieldBorderColor); } diff --git a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css index d00f54ab361a..a0924751eaf7 100644 --- a/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css +++ b/src/components/Widgets/Markdown/MarkdownControl/Toolbar/ToolbarButton.css @@ -18,5 +18,5 @@ } .nc-toolbarButton-active { - background-color: var(--highlightFGAltColor); + color: #1e2532; } From aa19f3ea89b5758d00aa3e5c1362b830b79ff50d Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Sat, 11 Nov 2017 17:45:56 -0500 Subject: [PATCH 021/134] relation and text widget styling --- package.json | 1 + src/base.css | 14 +++-- src/components/EntryEditor/EntryEditor.css | 58 +++++++++++-------- .../Widgets/Relation/ReactAutosuggest.css | 32 +++------- src/components/Widgets/TextControl.js | 41 +++++-------- 5 files changed, 68 insertions(+), 78 deletions(-) diff --git a/package.json b/package.json index de1cb979092d..1bd958c59bec 100644 --- a/package.json +++ b/package.json @@ -161,6 +161,7 @@ "react-router-redux": "^5.0.0-alpha.8", "react-sortable-hoc": "^0.6.8", "react-split-pane": "^0.1.66", + "react-textarea-autosize": "^5.2.0", "react-toggled": "^1.1.2", "react-toolbox": "^2.0.0-beta.12", "react-topbar-progress-indicator": "^2.0.0", diff --git a/src/base.css b/src/base.css index c483f3a0610d..779dd2342adc 100644 --- a/src/base.css +++ b/src/base.css @@ -46,7 +46,6 @@ } --input: { - font-family: 'SFMono-Regular', Consolas, "Liberation Mono", Menlo, Courier, monospace; display: block; width: 100%; padding: 12px; @@ -56,10 +55,10 @@ outline: 0; box-shadow: none; background-color: var(--controlBGColor); - font-size: 16px; - color: var(--textColor); + color: #444a57; transition: border-color .3s ease; position: relative; + font-size: 15px; &:focus, &:active { @@ -101,10 +100,13 @@ h1, h2, h3, h4, h5, h6, p { font-family: var(--fontFamilyPrimary); color: var(--textLeadColor); font-size: 15px; - font-weight: 500; line-height: 1.5; } +h1, h2, h3, h4, h5, h6 { + font-weight: 500; +} + h1 { margin: 30px auto 25px; padding-bottom: 15px; @@ -131,6 +133,10 @@ img { max-width: 100%; } +textarea { + resize: none; +} + .nc-theme-base { box-sizing: border-box; } diff --git a/src/components/EntryEditor/EntryEditor.css b/src/components/EntryEditor/EntryEditor.css index 863d1ed4f0f3..976ce0988ec3 100644 --- a/src/components/EntryEditor/EntryEditor.css +++ b/src/components/EntryEditor/EntryEditor.css @@ -189,38 +189,48 @@ } } +:root { + --dropdownList: { + background-color: #fff; + border-radius: var(--borderRadius); + overflow: hidden; + box-shadow: 0 4px 12px 0 rgba(68,74,87,0.15), 0 1px 3px 0 rgba(68,74,87,0.25); + } + + --dropdownItem: { + @apply --button; + background-color: transparent; + border-radius: 0; + color: var(--textColor); + font-weight: normal; + border-bottom: 1px solid #eaebf1; + padding: 10px 14px; + display: flex; + justify-content: space-between; + align-items: center; + + &:last-of-type { + border-bottom: 0; + } + + &:hover, + &:active, + &:focus { + background-color: #e8f5fe; + } + } +} + .nc-entryEditor-toolbar-saveDropdownList { + @apply(--dropdownList); position: absolute; top: 39px; left: 0; width: 100%; - background-color: #fff; - border-radius: var(--borderRadius); - overflow: hidden; - box-shadow: 0 4px 12px 0 rgba(68,74,87,0.15), 0 1px 3px 0 rgba(68,74,87,0.25); } .nc-entryEditor-toolbar-saveDropdownItem { - @apply --button; - background-color: transparent; - border-radius: 0; - color: var(--textColor); - font-weight: normal; - border-bottom: 1px solid #eaebf1; - padding: 10px 14px; - display: flex; - justify-content: space-between; - align-items: center; - - &:last-of-type { - border-bottom: 0; - } - - &:hover, - &:active, - &:focus { - background-color: #e8f5fe; - } + @apply(--dropdownItem); } .nc-entryEditor-toolbar-saveDropdownItemIcon { diff --git a/src/components/Widgets/Relation/ReactAutosuggest.css b/src/components/Widgets/Relation/ReactAutosuggest.css index 324758a0c26c..8e4fa8526b65 100644 --- a/src/components/Widgets/Relation/ReactAutosuggest.css +++ b/src/components/Widgets/Relation/ReactAutosuggest.css @@ -3,23 +3,7 @@ } .react-autosuggest__input { - width: 240px; - height: 30px; - padding: 10px 20px; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; - border: 1px solid #aaa; - border-radius: 4px; -} - -.react-autosuggest__input:focus { - outline: none; -} - -.react-autosuggest__container--open .react-autosuggest__input { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; + @apply(--input); } .react-autosuggest__suggestions-container { @@ -27,20 +11,18 @@ } .react-autosuggest__container--open .react-autosuggest__suggestions-container { - display: block; + @apply(--dropdownList); position: absolute; + display: block; top: 51px; width: 100%; - border: 1px solid #aaa; - background-color: #fff; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; z-index: 2; } +.react-autosuggest__suggestion { + @apply(--dropdownItem); +} + .react-autosuggest__suggestions-list { margin: 0; padding: 0; diff --git a/src/components/Widgets/TextControl.js b/src/components/Widgets/TextControl.js index ba4324baee7a..f075d0fb52f5 100644 --- a/src/components/Widgets/TextControl.js +++ b/src/components/Widgets/TextControl.js @@ -1,33 +1,24 @@ import PropTypes from 'prop-types'; import React from 'react'; +import Textarea from 'react-textarea-autosize'; -export default class StringControl extends React.Component { - componentDidMount() { - this.updateHeight(); - } - - handleChange = (e) => { - this.props.onChange(e.target.value); - this.updateHeight(); - }; - - updateHeight() { - if (this.element.scrollHeight > this.element.clientHeight) { - this.element.style.height = `${ this.element.scrollHeight }px`; - } - } - - handleRef = (ref) => { - this.element = ref; +export default class TextControl extends React.Component { + static propTypes = { + onChange: PropTypes.func.isRequired, + forID: PropTypes.string, + value: PropTypes.node, }; render() { - return