diff --git a/JoyceEditor_Mock.pdf b/JoyceEditor_Mock.pdf new file mode 100644 index 0000000..eec5bff Binary files /dev/null and b/JoyceEditor_Mock.pdf differ diff --git a/TODO.txt b/TODO.txt index 3fa6c64..5ebf463 100644 --- a/TODO.txt +++ b/TODO.txt @@ -9,13 +9,14 @@ Short List: - Some Testing +- Font Awesome / Better Glyphicons - Env Configs - Collapsable Button Lists - Draft.JS - DRY up Sass Modules - Async API Middleware - CombineReducers in App.js -- Import Actions into Reducers +- Import Action consts into Reducers - PropTypes - Loading Indicators diff --git a/package.json b/package.json index f27d458..9795b0d 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "clean-webpack-plugin": "^0.1.17", "css-loader": "^0.28.7", "extract-text-webpack-plugin": "^3.0.2", + "file-loader": "^1.1.5", "manifest-revision-webpack-plugin": "^0.4.1", "node-sass": "^4.6.0", "postcss-loader": "^2.0.8", diff --git a/src/actions/actions.js b/src/actions/actions.js deleted file mode 100644 index c0b2896..0000000 --- a/src/actions/actions.js +++ /dev/null @@ -1,22 +0,0 @@ -const setCurrentChapter = id => - ({ - type: 'GET_TEXT_DATA', - id: id - }) - -const toggleHighlight = () => - ({ - type: 'TOGGLE_HIGHLIGHT' - }) - -const updateEditorState = editorState => - ({ - type: 'UPDATE_EDITOR_STATE', - payload: editorState - }) - -export { - setCurrentChapter, - toggleHighlight, - updateEditorState -} \ No newline at end of file diff --git a/src/actions/index.js b/src/actions/index.js new file mode 100644 index 0000000..cb0fce6 --- /dev/null +++ b/src/actions/index.js @@ -0,0 +1,114 @@ +import axios from 'axios' +import EditorState from 'draft-js' + +let apiRoute = '/api/chapters/' + +const SELECT_CHAPTER = 'SELECT_CHAPTER' +const GET_CHAPTER_DATA = 'GET_CHAPTER_DATA' +const GET_CHAPTER_DATA_RECEIVED = 'GET_CHAPTER_DATA_RECEIVED' +const GET_CHAPTER_DATA_ERROR = 'GET_CHAPTER_DATA_ERROR' +const GET_TEXT_DATA = 'GET_TEXT_DATA' +const GET_TEXT_DATA_RECEIVED = 'GET_TEXT_DATA_RECEIVED' +const GET_TEXT_DATA_ERROR = 'GET_TEXT_DATA_ERROR' +const UPDATE_EDITOR_STATE = 'UPDATE_EDITOR_STATE' +const TOGGLE_HIGHLIGHT = 'TOGGLE_HIGHLIGHT' +const UPDATE_CHAPTER_TITLE = 'UPDATE_CHAPTER_TITLE' + +export const setCurrentChapter = id => + ({ + type: GET_TEXT_DATA, + id: id + }) + +export const toggleHighlight = () => + ({ + type: TOGGLE_HIGHLIGHT + }) + +export const updateChapterTitleInput = chapterTitleInput => { + return ({ + type: UPDATE_CHAPTER_TITLE, + data: chapterTitleInput.target.value + }) +} + +export const updateEditorState = editorState => + ({ + type: UPDATE_EDITOR_STATE, + data: editorState + }) + +export const chapterDataReceived = data => + ({ + type: GET_CHAPTER_DATA_RECEIVED, + data + }) + +export const chapterDataError = error => + ({ + type: GET_CHAPTER_DATA_ERROR, + error + }) + +export const textDataReceived = data => + ({ + type: GET_TEXT_DATA_RECEIVED, + data + }) + +export const textDataError = error => + ({ + type: GET_TEXT_DATA_ERROR, + error + }) + +export const setChapterEditor = chapter => + ({ + type: SET_EDITED_CHAPTER, + chapter + }) + +export const logger = store => next => action => { + console.group(action.type) + console.info('dispatching', action) + let result = next(action) + console.log('next state', store.getState()) + console.log('chapter count', store.getState().chapters.length) + console.groupEnd(action.type) + return result +} + +export const joyceAPIService = store => next => action => { + next(action) + switch(action.type) { + case GET_CHAPTER_DATA: + axios.get(apiRoute).then(res => { + const data = res.data + return next(chapterDataReceived(data)) + }).catch(error => { + return next(chapterDataError(error)) + }) + break + case GET_TEXT_DATA: + axios.get(apiRoute + action.id).then(res=> { + const data = res.data + return next(textDataReceived(data)) + }).catch(error => { + return next(textDataError(error)) + }) + break + case CREATE_CHAPTER: + const nextNumber = store.getState().chapters.length + 1 + const chapter = { + number: nextNumber, + title: '', + text: EditorState.createEmpty(), + } + store.dispatch(setChapterEditor(chapter)) + break + defaut: + break + } +} + +export const foo = 'bar' \ No newline at end of file diff --git a/src/assets/glyphicons-102-italic.png b/src/assets/glyphicons-102-italic.png new file mode 100644 index 0000000..376363c Binary files /dev/null and b/src/assets/glyphicons-102-italic.png differ diff --git a/src/assets/glyphicons-103-bold.png b/src/assets/glyphicons-103-bold.png new file mode 100644 index 0000000..ec61fd9 Binary files /dev/null and b/src/assets/glyphicons-103-bold.png differ diff --git a/src/assets/glyphicons-104-text-underline.png b/src/assets/glyphicons-104-text-underline.png new file mode 100644 index 0000000..be459a0 Binary files /dev/null and b/src/assets/glyphicons-104-text-underline.png differ diff --git a/src/assets/glyphicons-111-align-left.png b/src/assets/glyphicons-111-align-left.png new file mode 100644 index 0000000..2365b1d Binary files /dev/null and b/src/assets/glyphicons-111-align-left.png differ diff --git a/src/assets/glyphicons-112-align-center.png b/src/assets/glyphicons-112-align-center.png new file mode 100644 index 0000000..9a17fe2 Binary files /dev/null and b/src/assets/glyphicons-112-align-center.png differ diff --git a/src/assets/glyphicons-113-align-right.png b/src/assets/glyphicons-113-align-right.png new file mode 100644 index 0000000..f4dc27d Binary files /dev/null and b/src/assets/glyphicons-113-align-right.png differ diff --git a/src/assets/glyphicons-151-edit.png b/src/assets/glyphicons-151-edit.png new file mode 100644 index 0000000..bc03d5a Binary files /dev/null and b/src/assets/glyphicons-151-edit.png differ diff --git a/src/assets/glyphicons-191-plus-sign.png b/src/assets/glyphicons-191-plus-sign.png new file mode 100644 index 0000000..a114c46 Binary files /dev/null and b/src/assets/glyphicons-191-plus-sign.png differ diff --git a/src/assets/glyphicons-192-minus-sign.png b/src/assets/glyphicons-192-minus-sign.png new file mode 100644 index 0000000..593bd2a Binary files /dev/null and b/src/assets/glyphicons-192-minus-sign.png differ diff --git a/src/assets/glyphicons-193-remove-sign.png b/src/assets/glyphicons-193-remove-sign.png new file mode 100644 index 0000000..37730e1 Binary files /dev/null and b/src/assets/glyphicons-193-remove-sign.png differ diff --git a/src/assets/glyphicons-194-ok-sign.png b/src/assets/glyphicons-194-ok-sign.png new file mode 100644 index 0000000..bcd36a9 Binary files /dev/null and b/src/assets/glyphicons-194-ok-sign.png differ diff --git a/src/assets/glyphicons-224-chevron-right.png b/src/assets/glyphicons-224-chevron-right.png new file mode 100644 index 0000000..c2f161d Binary files /dev/null and b/src/assets/glyphicons-224-chevron-right.png differ diff --git a/src/assets/glyphicons-225-chevron-left.png b/src/assets/glyphicons-225-chevron-left.png new file mode 100644 index 0000000..2fffbd3 Binary files /dev/null and b/src/assets/glyphicons-225-chevron-left.png differ diff --git a/src/assets/glyphicons-352-book-open.png b/src/assets/glyphicons-352-book-open.png new file mode 100644 index 0000000..5108d41 Binary files /dev/null and b/src/assets/glyphicons-352-book-open.png differ diff --git a/src/assets/index.js b/src/assets/index.js new file mode 100644 index 0000000..a9aa556 --- /dev/null +++ b/src/assets/index.js @@ -0,0 +1,30 @@ +const glyphiconItalic = require('./glyphicons-102-italic.png') +const glyphiconBold = require('./glyphicons-103-bold.png') +const glyphiconUnderline = require('./glyphicons-104-text-underline.png') + +const glyphiconAlignLeft = require('./glyphicons-111-align-left.png') +const glyphiconAlignCenter = require('./glyphicons-112-align-center.png') +const glyphiconAlignRight = require('./glyphicons-113-align-right.png') + +const glyphiconChevronLeft = require('./glyphicons-225-chevron-left.png') +const glyphiconChevronRight = require('./glyphicons-224-chevron-right.png') + +const glyphiconPlusSign = require('./glyphicons-191-plus-sign.png') +const glyphiconMinusSign = require('./glyphicons-192-minus-sign.png') +const glyphiconRemoveSign = require('./glyphicons-193-remove-sign.png') +const glyphiconOKSign = require('./glyphicons-194-ok-sign.png') + +export { + glyphiconItalic, + glyphiconBold, + glyphiconUnderline, + glyphiconAlignLeft, + glyphiconAlignCenter, + glyphiconAlignRight, + glyphiconChevronLeft, + glyphiconChevronRight, + glyphiconPlusSign, + glyphiconMinusSign, + glyphiconRemoveSign, + glyphiconOKSign +} diff --git a/src/components/editorButtonGroup.js b/src/components/editorButtonGroup.js deleted file mode 100644 index ec8d95a..0000000 --- a/src/components/editorButtonGroup.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' - -const EditorButtonGroup = () => -
-
- - -
-
- -export default EditorButtonGroup \ No newline at end of file diff --git a/src/components/editorSidebar.js b/src/components/editorSidebar.js new file mode 100644 index 0000000..b623675 --- /dev/null +++ b/src/components/editorSidebar.js @@ -0,0 +1,14 @@ +import React from 'react' +import ChapterListContainer from '../containers/chapterListContainer' +import NewChapterButtonContainer from '../containers/newChapterButtonContainer' +import SidebarSpacer from './sidebarSpacer' + +const EditorSidebar = () => + + +export default EditorSidebar \ No newline at end of file diff --git a/src/components/newChapterButton.js b/src/components/newChapterButton.js new file mode 100644 index 0000000..c3c4d60 --- /dev/null +++ b/src/components/newChapterButton.js @@ -0,0 +1,13 @@ +import React from 'react' +import { glyphiconPlusSign } from '../assets' + +const NewChapterButton = () => +
+
+ +
+
+ +export default NewChapterButton \ No newline at end of file diff --git a/src/containers/chapterListContainer.js b/src/containers/chapterListContainer.js index 16e411e..8d851c3 100644 --- a/src/containers/chapterListContainer.js +++ b/src/containers/chapterListContainer.js @@ -1,7 +1,7 @@ import React from 'react' import { connect } from 'react-redux' -import { setCurrentChapter } from '../actions/actions' +import { setCurrentChapter } from '../actions' import ChapterList from '../components/chapterList' diff --git a/src/containers/editorContainer.js b/src/containers/editorContainer.js index 2761490..a7d120c 100644 --- a/src/containers/editorContainer.js +++ b/src/containers/editorContainer.js @@ -2,7 +2,7 @@ import React from 'react' import TextEditorContainer from '../containers/textEditorContainer' import Navbar from '../components/navbar' -import Sidebar from '../components/sidebar' +import EditorSidebar from '../components/editorSidebar' import Content from '../components/content' const EditorContainer = () => @@ -10,7 +10,7 @@ const EditorContainer = () =>
- + diff --git a/src/containers/highlightButtonContainer.js b/src/containers/highlightButtonContainer.js index 573e55a..b7b6010 100644 --- a/src/containers/highlightButtonContainer.js +++ b/src/containers/highlightButtonContainer.js @@ -1,7 +1,7 @@ import React from 'react' import { connect } from 'react-redux' -import { toggleHighlight } from '../actions/actions' +import { toggleHighlight } from '../actions' import HighlightButton from '../components/highlightButton' diff --git a/src/containers/newChapterButtonContainer.js b/src/containers/newChapterButtonContainer.js new file mode 100644 index 0000000..f9c80ec --- /dev/null +++ b/src/containers/newChapterButtonContainer.js @@ -0,0 +1,18 @@ +import React from 'react' +import { connect } from 'react-redux' + +import { createNewChapter } from '../actions' + +import NewChapterButton from '../components/newChapterButton' + +const mapDispatchToProps = dispatch => { + return { + onNewChapterClick: () => { + dispatch(createNewChapter()) + } + } +} + +const NewChapterButtonContainer = connect(mapDispatchToProps)(NewChapterButton) + +export default NewChapterButtonContainer \ No newline at end of file diff --git a/src/containers/pageContainer.js b/src/containers/pageContainer.js index fa0160a..5a0385a 100644 --- a/src/containers/pageContainer.js +++ b/src/containers/pageContainer.js @@ -1,6 +1,6 @@ import React from 'react' import { connect } from 'react-redux' -import { setCurrentChapter } from '../actions/actions' +import { setCurrentChapter } from '../actions' class Page extends React.Component { render() { diff --git a/src/containers/readerContainer.js b/src/containers/readerContainer.js index 048e947..043204f 100644 --- a/src/containers/readerContainer.js +++ b/src/containers/readerContainer.js @@ -1,7 +1,7 @@ import React from 'react' import { connect } from 'react-redux' -import { setCurrentChapter } from '../actions/actions' +import { setCurrentChapter } from '../actions' import PageContainer from '../containers/pageContainer' import Content from '../components/content' diff --git a/src/containers/readerSidebarContainer b/src/containers/readerSidebarContainer new file mode 100644 index 0000000..e69de29 diff --git a/src/containers/textEditorContainer.js b/src/containers/textEditorContainer.js index a78d2cc..8d329cf 100644 --- a/src/containers/textEditorContainer.js +++ b/src/containers/textEditorContainer.js @@ -1,21 +1,57 @@ import React from 'react' import { connect } from 'react-redux' -import { updateEditorState } from '../actions/actions' +import { updateEditorState, updateChapterTitleInput } from '../actions' import { Editor, convertToRaw } from 'draft-js'; -import EditorButtonGroup from '../components/editorButtonGroup' +import { glyphiconBold, glyphiconItalic, glyphiconUnderline, glyphiconAlignLeft, glyphiconAlignCenter, glyphiconAlignRight } from '../assets' -const TextEditor = ({editorState, onSaveEditorState}) => -
- - - {JSON.stringify(editorState.getSelection())} +const TextEditor = ({editorState, onSaveEditorState, chapterTitleInput, onChapterTitleChange}) => +
+
+
+
+

Chapter 1:

+
+
+ +
+ +
+
+
+ + + +
+
+ + + +
+
+
+ +
+ +
+ +
+
+
+ +
+
+ +
+
+
const mapStateToProps = state => { return { - editorState: state.editorState + editorState: state.editorState, + chapterTitleInput: state.chapterTitleInput } } @@ -23,6 +59,9 @@ const mapDispatchToProps = dispatch => { return { onSaveEditorState: editorState => { dispatch(updateEditorState(editorState)) + }, + onChapterTitleChange: chapterTitleInput => { + dispatch(updateChapterTitleInput(chapterTitleInput)) } } } diff --git a/src/editor.js b/src/editor.js index d0735dc..41e9611 100644 --- a/src/editor.js +++ b/src/editor.js @@ -4,22 +4,26 @@ import { createStore, combineReducers, applyMiddleware } from 'redux' import { Provider } from 'react-redux' import 'bootstrap' -import { chapters, highlightActive, currentChapter, editorState } from './reducers/editor' -import joyceAPIService from './middleware' +import { chapters, highlightActive, currentChapter, chapterTitleInput, editorState } from './reducers/editor' +import { joyceAPIService, logger } from './actions' import EditorContainer from './containers/editorContainer' const reduceEditor = combineReducers({ chapters, currentChapter, + chapterTitleInput, highlightActive, editorState }) -let store = createStore(reduceEditor, applyMiddleware(joyceAPIService)) +let store = createStore(reduceEditor, applyMiddleware(joyceAPIService, logger)) ReactDOM.render( , document.getElementById('wrapper') -) \ No newline at end of file +) + +store.dispatch({type: 'GET_CHAPTER_DATA'}) +store.dispatch({type: 'GET_TEXT_DATA', id: 1}) \ No newline at end of file diff --git a/src/reader.js b/src/reader.js index ba0aa75..4aec6c4 100644 --- a/src/reader.js +++ b/src/reader.js @@ -5,7 +5,7 @@ import { Provider } from 'react-redux' import 'bootstrap' import { chapters, currentChapter, highlightActive } from './reducers/reader' -import joyceAPIService from './middleware' +import { joyceAPIService } from './actions' import { ReaderContainer } from './containers/readerContainer' const reduceReader = combineReducers({ diff --git a/src/reducers/editor.js b/src/reducers/editor.js index 7717e0a..cd8ac74 100644 --- a/src/reducers/editor.js +++ b/src/reducers/editor.js @@ -1,6 +1,6 @@ import { EditorState } from 'draft-js' import objectAssign from 'object-assign' // Object.assign() polyfill for older browsers -import actions from '../actions/actions' +import actions from '../actions' const chapters = (state=[], action) => { switch(action.type) { @@ -32,8 +32,16 @@ const currentChapter = (state={}, action) => { const editorState = (state=(EditorState.createEmpty()), action) => { switch(action.type) { case 'UPDATE_EDITOR_STATE': - console.log(action.payload.getSelection()) - return action.payload + return action.data + default: + return state + } +} + +const chapterTitleInput = (state='', action) => { + switch(action.type) { + case 'UPDATE_CHAPTER_TITLE': + return action.data default: return state } @@ -43,5 +51,6 @@ export { chapters, highlightActive, currentChapter, + chapterTitleInput, editorState } \ No newline at end of file diff --git a/src/stylesheets/_custom.scss b/src/stylesheets/_custom.scss index 340f6a4..ce18c85 100644 --- a/src/stylesheets/_custom.scss +++ b/src/stylesheets/_custom.scss @@ -1,4 +1,7 @@ $theme-colors: ( + "info": #537577, + "danger": #72030a, + "success": #118734, "dark": #07383a, "primary": #824500 ); \ No newline at end of file diff --git a/src/stylesheets/editor.scss b/src/stylesheets/editor.scss index 90ff14b..87843d6 100644 --- a/src/stylesheets/editor.scss +++ b/src/stylesheets/editor.scss @@ -108,13 +108,50 @@ nav a:hover { border-radius: 5px; } -#text_editor { +#editor_topbar { + margin: 0.8% 0; +} + +#editor_topbar input { + width: 100%; +} + +#editor_topbar button img { + width: 100%; + height: 100%; +} + +#editor_content { height: 100%; background-color: rgba(256,256,256,.8); box-shadow: 2px 5px 10px 1px rgba(0, 0, 0, 0.3); - padding: 3% 8%; + margin: 0.8% 0; border: 1px solid $border_color; border-radius: 5px; + padding: 3% 8%; +} + +#editor_content div { + height: 100%; + overflow: scroll; +} + +#editor_topbar button img { + width: 60%; +} + +#editor_bottombar { + margin: 0.8% 0; +} + +#editor_delete { + float: left; + width: 100%; +} + +#editor_save { + float: right; + width: 100%; } #page { diff --git a/webpack.config.js b/webpack.config.js index 27d41c8..855c594 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,10 +1,10 @@ -const HtmlWebpackPlugin = require('html-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') -const ManifestRevisionPlugin = require('manifest-revision-webpack-plugin'); -const webpack = require('webpack'); -const path = require('path'); +const ManifestRevisionPlugin = require('manifest-revision-webpack-plugin') +const webpack = require('webpack') +const path = require('path') -const rootAssetPath = './src/'; +const rootAssetPath = './src/' let pathsToClean = [ 'static/' @@ -25,7 +25,7 @@ module.exports = { }, output: { publicPath: "/static/js/", - filename: '[name].[chunkhash].js', + filename: '[name].[hash].js', path: path.resolve(__dirname, 'static/js/') }, watch: true, @@ -44,6 +44,12 @@ module.exports = { } } }, + { + test: /\.(png)$/, + use: { + loader: 'file-loader' + } + }, { test: /\.(scss)$/, use: [{ @@ -75,6 +81,6 @@ module.exports = { 'window.jQuery': 'jquery', Popper: ['popper.js', 'default'] }), - new CleanWebpackPlugin(pathsToClean) - ], + new CleanWebpackPlugin(pathsToClean), + ], }; \ No newline at end of file