1
0
mirror of https://github.com/stolksdorf/homebrewery.git synced 2026-01-06 09:29:15 +00:00

Merge branch 'styleEditor' into noHtml

This commit is contained in:
Scott Tolksdorf
2017-01-30 10:48:34 -05:00
14 changed files with 220 additions and 92 deletions

View File

@@ -33,7 +33,7 @@ const ContinousSave = React.createClass({
window.onbeforeunload = function(){};
},
actionHandler : function(actionType){
if(actionType == 'UPDATE_BREW_TEXT' || actionType == 'UPDATE_META'){
if(actionType == 'UPDATE_BREW_CODE' || actionType == 'UPDATE_META' || actionType == 'UPDATE_BREW_STYLE'){
Actions.pendingSave();
}
},
@@ -51,6 +51,9 @@ const ContinousSave = React.createClass({
Oops!
<div className='errorContainer'>
Looks like there was a problem saving. <br />
Back up your brew in a text file, just in case.
<br /><br />
Report the issue <a target='_blank' href={'https://github.com/stolksdorf/naturalcrit/issues/new?body='+ encodeURIComponent(errMsg)}>
here
</a>.

View File

@@ -120,7 +120,7 @@
top : 29px;
left : -20px;
z-index : 1000;
width : 120px;
width : 170px;
padding : 8px;
background-color : #333;
a{

View File

@@ -10,6 +10,7 @@
"codemirror",
"codemirror/mode/gfm/gfm.js",
"codemirror/mode/javascript/javascript.js",
"codemirror/mode/css/css.js",
"moment",
"superagent",
"marked",

View File

@@ -10,6 +10,7 @@ const BrewSchema = mongoose.Schema({
editId : {type : String, default: shortid.generate, index: { unique: true }},
text : {type : String, default : ""},
style : {type : String, default : ""},
title : {type : String, default : ""},
description : {type : String, default : ""},
@@ -23,7 +24,7 @@ const BrewSchema = mongoose.Schema({
updatedAt : { type: Date, default: Date.now},
lastViewed : { type: Date, default: Date.now},
views : {type:Number, default:0},
version : {type: Number, default:1}
version : {type: Number, default:2}
}, {
versionKey: false,
toJSON : {

View File

@@ -54,10 +54,13 @@ const Actions = {
setBrew : (brew) => {
dispatch('SET_BREW', brew);
},
updateBrewText : (brewText) => {
dispatch('UPDATE_BREW_TEXT', brewText)
updateBrewCode : (brewCode) => {
dispatch('UPDATE_BREW_CODE', brewCode)
},
updateMetaData : (meta) => {
updateBrewStyle : (style) => {
dispatch('UPDATE_BREW_STYLE', style)
},
updateMetadata : (meta) => {
dispatch('UPDATE_META', meta);
},
pendingSave : () => {

View File

@@ -8,6 +8,7 @@ let State = {
brew : {
text : '',
style : '',
shareId : undefined,
editId : undefined,
createdAt : undefined,
@@ -29,9 +30,13 @@ const Store = flux.createStore({
SET_BREW : (brew) => {
State.brew = brew;
},
UPDATE_BREW_TEXT : (brewText) => {
State.brew.text = brewText;
State.errors = Markdown.validate(brewText);
UPDATE_BREW_CODE : (brewCode) => {
State.brew.text = brewCode;
State.errors = Markdown.validate(brewCode);
},
UPDATE_BREW_STYLE : (style) => {
//TODO: add in an error checker?
State.brew.style = style;
},
UPDATE_META : (meta) => {
State.brew = _.merge({}, State.brew, meta);
@@ -50,11 +55,14 @@ Store.init = (state)=>{
Store.getBrew = ()=>{
return State.brew;
};
Store.getBrewText = ()=>{
Store.getBrewCode = ()=>{
return State.brew.text;
};
Store.getBrewStyle = ()=>{
return State.brew.style;
};
Store.getMetaData = ()=>{
return _.omit(State.brew, ['text']);
return _.omit(State.brew, ['text', 'style']);
};
Store.getErrors = ()=>{
return State.errors;

View File

@@ -6,35 +6,36 @@ const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
const SnippetBar = require('./snippetbar/snippetbar.jsx');
const MetadataEditor = require('./metadataEditor/metadataEditor.jsx');
const Menubar = require('./menubar/menubar.jsx');
const splice = function(str, index, inject){
return str.slice(0, index) + inject + str.slice(index);
};
const SNIPPETBAR_HEIGHT = 25;
const MENUBAR_HEIGHT = 25;
const BrewEditor = React.createClass({
getDefaultProps: function() {
return {
value : '',
onChange : ()=>{},
brew : {
text : '',
style : '',
},
metadata : {},
onMetadataChange : ()=>{},
onCodeChange : ()=>{},
onStyleChange : ()=>{},
onMetaChange : ()=>{},
};
},
getInitialState: function() {
return {
showMetadataEditor: false
view : 'code', //'code', 'style', 'meta'
};
},
cursorPosition : {
line : 0,
ch : 0
},
componentDidMount: function() {
this.updateEditorSize();
this.highlightPageLines();
//this.highlightPageLines();
window.addEventListener("resize", this.updateEditorSize);
},
componentWillUnmount: function() {
@@ -42,17 +43,15 @@ const BrewEditor = React.createClass({
},
updateEditorSize : function() {
let paneHeight = this.refs.main.parentNode.clientHeight;
paneHeight -= SNIPPETBAR_HEIGHT + 1;
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
if(this.refs.codeEditor){
let paneHeight = this.refs.main.parentNode.clientHeight;
paneHeight -= MENUBAR_HEIGHT + 1;
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
}
},
handleTextChange : function(text){
this.props.onChange(text);
},
handleCursorActivty : function(curpos){
this.cursorPosition = curpos;
},
handleInject : function(injectText){
const lines = this.props.value.split('\n');
lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText);
@@ -60,23 +59,32 @@ const BrewEditor = React.createClass({
this.handleTextChange(lines.join('\n'));
this.refs.codeEditor.setCursorPosition(this.cursorPosition.line, this.cursorPosition.ch + injectText.length);
},
handgleToggle : function(){
handleViewChange : function(newView){
this.setState({
showMetadataEditor : !this.state.showMetadataEditor
})
view : newView
}, this.updateEditorSize);
},
brewJump : function(){
const currentPage = this.getCurrentPage();
window.location.hash = 'p' + currentPage;
},
//Called when there are changes to the editor's dimensions
/*
update : function(){
this.refs.codeEditor.updateSize();
if(this.refs.codeEditor) this.refs.codeEditor.updateSize();
},
*/
//TODO: convert this into a generic function for columns and blocks
//MOve this to a util.sj file
highlightPageLines : function(){
if(!this.refs.codeEditor) return;
const codeMirror = this.refs.codeEditor.codeMirror;
@@ -91,6 +99,7 @@ const BrewEditor = React.createClass({
return lineNumbers
},
/*
renderMetadataEditor : function(){
if(!this.state.showMetadataEditor) return;
return <MetadataEditor
@@ -98,25 +107,49 @@ const BrewEditor = React.createClass({
onChange={this.props.onMetadataChange}
/>
},
*/
renderEditor : function(){
if(this.state.view == 'meta'){
return <MetadataEditor
metadata={this.props.brew}
onChange={this.props.onMetaChange} />
}
if(this.state.view == 'style'){
return <CodeEditor key='style'
ref='codeEditor'
language='css'
value={this.props.brew.style}
onChange={this.props.onStyleChange} />
}
if(this.state.view == 'code'){
return <CodeEditor key='code'
ref='codeEditor'
language='gfm'
value={this.props.brew.text}
onChange={this.props.onCodeChange} />
}
},
render : function(){
this.highlightPageLines();
return<div className='brewEditor' ref='main'>
return <div className='brewEditor' ref='main'>
{/*
<SnippetBar
brew={this.props.value}
onInject={this.handleInject}
onToggle={this.handgleToggle}
showmeta={this.state.showMetadataEditor} />
{this.renderMetadataEditor()}
<CodeEditor
ref='codeEditor'
wrap={true}
language='gfm'
value={this.props.value}
onChange={this.handleTextChange}
onCursorActivity={this.handleCursorActivty} />
*/}
<Menubar
view={this.state.view}
onViewChange={this.handleViewChange}
/>
{this.renderEditor()}
</div>
/*

View File

@@ -5,9 +5,10 @@ const BrewEditor = require('./brewEditor.jsx')
module.exports = Store.createSmartComponent(BrewEditor, ()=>{
return {
value : Store.getBrewText(),
onChange : Actions.updateBrewText,
metadata : Store.getMetaData(),
onMetadataChange : Actions.updateMetaData,
brew : Store.getBrew(),
onCodeChange : Actions.updateBrewCode,
onStyleChange : Actions.updateBrewStyle,
onMetaChange : Actions.updateMetadata,
};
});

View File

@@ -0,0 +1,35 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
const Menubar = React.createClass({
getDefaultProps: function() {
return {
view : '',
onViewChange : ()=>{},
onSnippetInject : ()=>{},
};
},
render: function(){
return <div className='menubar'>
<div className='editors'>
<div className={cx('code', {selected : this.props.view == 'code'})}
onClick={this.props.onViewChange.bind(null, 'code')}>
<i className='fa fa-beer' />
</div>
<div className={cx('style', {selected : this.props.view == 'style'})}
onClick={this.props.onViewChange.bind(null, 'style')}>
<i className='fa fa-paint-brush' />
</div>
<div className={cx('meta', {selected : this.props.view == 'meta'})}
onClick={this.props.onViewChange.bind(null, 'meta')}>
<i className='fa fa-bars' />
</div>
</div>
</div>
}
});
module.exports = Menubar;

View File

@@ -0,0 +1,35 @@
.menubar{
@menuHeight : 25px;
position : relative;
height : @menuHeight;
background-color : #ddd;
.editors{
position : absolute;
display : flex;
top : 0px;
right : 0px;
height : @menuHeight;
width : 90px;
justify-content : space-between;
&>div{
height : @menuHeight;
width : @menuHeight;
cursor : pointer;
line-height : @menuHeight;
text-align : center;
&:hover,&.selected{
background-color : #999;
}
&.code{
.tooltipLeft('Brew Editor');
}
&.style{
.tooltipLeft('Style Editor');
}
&.meta{
.tooltipLeft('Metadata');
}
}
}
}

View File

@@ -1,11 +1,11 @@
.metadataEditor{
position : absolute;
z-index : 10000;
box-sizing : border-box;
width : 100%;
padding : 25px;
background-color : #999;
// background-color : #999;
background-color: white;
.field{
display : flex;
width : 100%;

View File

@@ -15,12 +15,13 @@ const PPR_THRESHOLD = 50;
const BrewRenderer = React.createClass({
getDefaultProps: function() {
return {
brewText : '',
value : '',
style : '',
errors : []
};
},
getInitialState: function() {
const pages = this.props.brewText.split('\\page');
const pages = this.props.value.split('\\page');
return {
viewablePageNumber: 0,
@@ -45,7 +46,7 @@ const BrewRenderer = React.createClass({
componentWillReceiveProps: function(nextProps) {
if(this.refs.pages && this.refs.pages.firstChild) this.pageHeight = this.refs.pages.firstChild.clientHeight;
const pages = nextProps.brewText.split('\\page');
const pages = nextProps.value.split('\\page');
this.setState({
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD
@@ -124,6 +125,10 @@ const BrewRenderer = React.createClass({
return this.lastRender;
},
renderStyle : function(){
},
render : function(){
return <div className='brewRenderer'
onScroll={this.handleScroll}
@@ -133,6 +138,8 @@ const BrewRenderer = React.createClass({
<ErrorBar errors={this.props.errors} />
<RenderWarnings />
<style>{this.props.style}</style>
<div className='pages' ref='pages'>
{this.renderPages()}
</div>

View File

@@ -4,7 +4,8 @@ const BrewRenderer = require('./brewRenderer.jsx');
module.exports = Store.createSmartComponent(BrewRenderer, () => {
return {
brewText : Store.getBrewText(),
value : Store.getBrewCode(),
style : Store.getBrewStyle(),
errors : Store.getErrors()
}
});

View File

@@ -1,70 +1,70 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
var CodeMirror;
let CodeMirror;
if(typeof navigator !== 'undefined'){
var CodeMirror = require('codemirror');
CodeMirror = require('codemirror');
//Language Modes
require('codemirror/mode/gfm/gfm.js'); //Github flavoured markdown
require('codemirror/mode/javascript/javascript.js');
require('codemirror/mode/css/css.js');
}
var CodeEditor = React.createClass({
const CodeEditor = React.createClass({
getDefaultProps: function() {
return {
value : '',
language : '',
value : '',
wrap : false,
onChange : function(){},
onCursorActivity : function(){},
wrap : true,
onChange : ()=>{},
};
},
componentDidMount: function() {
this.codeMirror = CodeMirror(this.refs.editor,{
value : this.props.value,
lineNumbers: true,
lineWrapping : this.props.wrap,
mode : this.props.language,
});
this.codeMirror.on('change', this.handleChange);
this.codeMirror.on('cursorActivity', this.handleCursorActivity);
this.updateSize();
},
componentWillReceiveProps: function(nextProps){
if(this.props.language !== nextProps.language){
this.buildEditor();
}
if(this.codeMirror && nextProps.value !== undefined && this.codeMirror.getValue() != nextProps.value) {
this.codeMirror.setValue(nextProps.value);
}
},
shouldComponentUpdate: function(nextProps, nextState) {
return false;
},
componentDidMount: function() {
this.buildEditor();
},
buildEditor : function(){
this.codeMirror = CodeMirror(this.refs.editor,{
value : this.props.value,
lineNumbers : true,
lineWrapping : this.props.wrap,
mode : this.props.language,
indentWithTabs : true,
tabSize : 2
});
this.codeMirror.on('change', ()=>{
this.props.onChange(this.codeMirror.getValue());
});
this.updateSize();
},
//Externally Used
setCursorPosition : function(line, char){
setTimeout(()=>{
this.codeMirror.focus();
this.codeMirror.doc.setCursor(line, char);
}, 10);
},
getCursorPosition : function(){
return this.codeMirror.getCursor();
},
updateSize : function(){
this.codeMirror.refresh();
},
handleChange : function(editor){
this.props.onChange(editor.getValue());
},
handleCursorActivity : function(){
this.props.onCursorActivity(this.codeMirror.doc.getCursor());
},
render : function(){
return <div className='codeEditor' ref='editor' />
}