0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-17 14:32:41 +00:00

Added in metadata editor

This commit is contained in:
Scott Tolksdorf
2016-11-23 14:47:28 -05:00
parent ccdb6a3cbd
commit e61bf22788
19 changed files with 483 additions and 191 deletions

View File

@@ -1,25 +1,31 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
var CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
var Snippets = require('./snippets/snippets.js');
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
const SnippetBar = require('./snippetbar/snippetbar.jsx');
const MetadataEditor = require('./MetadataEditor/MetadataEditor.jsx');
var splice = function(str, index, inject){
const splice = function(str, index, inject){
return str.slice(0, index) + inject + str.slice(index);
};
var execute = function(val){
if(_.isFunction(val)) return val();
return val;
}
const SNIPPETBAR_HEIGHT = 25;
var Editor = React.createClass({
const Editor = React.createClass({
getDefaultProps: function() {
return {
value : "",
onChange : function(){}
value : '',
onChange : ()=>{},
metadata : {},
onMetadataChange : ()=>{}
};
},
getInitialState: function() {
return {
showMetadataEditor: false
};
},
cursorPosition : {
@@ -27,7 +33,6 @@ var Editor = React.createClass({
ch : 0
},
componentDidMount: function() {
this.updateEditorSize();
window.addEventListener("resize", this.updateEditorSize);
@@ -37,8 +42,8 @@ var Editor = React.createClass({
},
updateEditorSize : function() {
var paneHeight = this.refs.main.parentNode.clientHeight;
paneHeight -= this.refs.snippetBar.clientHeight + 1;
let paneHeight = this.refs.main.parentNode.clientHeight;
paneHeight -= SNIPPETBAR_HEIGHT + 1;
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
},
@@ -48,38 +53,37 @@ var Editor = React.createClass({
handleCursorActivty : function(curpos){
this.cursorPosition = curpos;
},
handleSnippetClick : function(injectText){
var lines = this.props.value.split('\n');
handleInject : function(injectText){
const lines = this.props.value.split('\n');
lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText);
this.handleTextChange(lines.join('\n'));
this.refs.codeEditor.setCursorPosition(this.cursorPosition.line, this.cursorPosition.ch + injectText.length);
},
handgleToggle : function(){
this.setState({
showMetadataEditor : !this.state.showMetadataEditor
})
},
//Called when there are changes to the editor's dimensions
update : function(){
this.refs.codeEditor.updateSize();
},
renderSnippetGroups : function(){
return _.map(Snippets, (snippetGroup)=>{
return <SnippetGroup
groupName={snippetGroup.groupName}
icon={snippetGroup.icon}
snippets={snippetGroup.snippets}
key={snippetGroup.groupName}
onSnippetClick={this.handleSnippetClick}
/>
})
renderMetadataEditor : function(){
if(!this.state.showMetadataEditor) return;
return <MetadataEditor
metadata={this.props.metadata}
onChange={this.props.onMetadataChange}
/>
},
render : function(){
return(
<div className='editor' ref='main'>
<div className='snippetBar' ref='snippetBar'>
{this.renderSnippetGroups()}
</div>
<SnippetBar onInject={this.handleInject} onToggle={this.handgleToggle} showmeta={this.state.showMetadataEditor} />
{this.renderMetadataEditor()}
<CodeEditor
ref='codeEditor'
wrap={true}
@@ -99,40 +103,3 @@ module.exports = Editor;
var SnippetGroup = React.createClass({
getDefaultProps: function() {
return {
groupName : '',
icon : 'fa-rocket',
snippets : [],
onSnippetClick : function(){},
};
},
handleSnippetClick : function(snippet){
this.props.onSnippetClick(execute(snippet.gen));
},
renderSnippets : function(){
return _.map(this.props.snippets, (snippet)=>{
return <div className='snippet' key={snippet.name} onClick={this.handleSnippetClick.bind(this, snippet)}>
<i className={'fa fa-fw ' + snippet.icon} />
{snippet.name}
</div>
})
},
render : function(){
return <div className='snippetGroup'>
<div className='text'>
<i className={'fa fa-fw ' + this.props.icon} />
<span className='groupName'>{this.props.groupName}</span>
</div>
<div className='dropdown'>
{this.renderSnippets()}
</div>
</div>
},
});

View File

@@ -2,76 +2,10 @@
.editor{
position : relative;
width : 100%;
.snippetBar{
@height : 25px;
position : relative;
height : @height;
background-color : #ddd;
.snippetGroup{
/*
.animate(background-color);
margin : 0px 8px;
padding : 3px;
font-size : 13px;
border-radius : 5px;
&:hover, &.selected{
background-color : #999;
}
*/
display : inline-block;
height : @height;
padding : 0px 5px;
cursor : pointer;
font-size : 0.6em;
font-weight : 800;
line-height : @height;
text-transform : uppercase;
border-right : 1px solid black;
i{
vertical-align : middle;
margin-right : 3px;
font-size : 1.2em;
}
&:hover, &.selected{
background-color : #999;
}
.text{
line-height : @height;
.groupName{
margin-left : 6px;
font-size : 10px;
}
}
&:hover{
.dropdown{
visibility : visible;
}
}
.dropdown{
position : absolute;
visibility : hidden;
top : 100%;
z-index : 1000;
padding : 0px;
margin-left: -5px;
background-color : #ddd;
.snippet{
.animate(background-color);
padding : 10px;
cursor : pointer;
font-size : 10px;
i{
margin-right: 8px;
font-size : 13px;
}
&:hover{
background-color : #999;
}
}
}
}
}
.codeEditor{
height : 100%;
}
}

View File

@@ -0,0 +1,106 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
const SYSTEMS = ['5e', '4e', '3.5e', 'Pathfinder']
const MetadataEditor = React.createClass({
getDefaultProps: function() {
return {
metadata: {
title : '',
description : '',
tags : '',
published : false,
authors : [],
systems : []
},
onChange : ()=>{}
};
},
handleFieldChange : function(name, e){
this.props.onChange(_.merge({}, this.props.metadata, {
[name] : e.target.value
}))
},
handleSystem : function(system, e){
if(e.target.checked){
this.props.metadata.systems.push(system);
}else{
this.props.metadata.systems = _.without(this.props.metadata.systems, system);
}
this.props.onChange(this.props.metadata);
},
handlePublish : function(val){
this.props.onChange(_.merge({}, this.props.metadata, {
published : val
}));
},
renderSystems : function(){
return _.map(SYSTEMS, (val)=>{
return <label key={val}>
<input
type='checkbox'
checked={_.includes(this.props.metadata.systems, val)}
onChange={this.handleSystem.bind(null, val)} />
{val}
</label>
});
},
renderPublish : function(){
if(this.props.metadata.published){
return <button className='unpublish' onClick={this.handlePublish.bind(null, false)}>
<i className='fa fa-ban' /> unpublish
</button>
}else{
return <button className='publish' onClick={this.handlePublish.bind(null, true)}>
<i className='fa fa-globe' /> publish
</button>
}
},
render : function(){
return <div className='metadataEditor'>
<div className='field title'>
<label>title</label>
<input type='text' className='value'
value={this.props.metadata.title}
onChange={this.handleFieldChange.bind(null, 'title')} />
</div>
<div className='field description'>
<label>description</label>
<textarea value={this.props.metadata.description} className='value'
onChange={this.handleFieldChange.bind(null, 'description')} />
</div>
{/*}
<div className='field tags'>
<label>tags</label>
<textarea value={this.props.metadata.tags}
onChange={this.handleFieldChange.bind(null, 'tags')} />
</div>
*/}
<div className='field systems'>
<label>systems</label>
<div className='value'>
{this.renderSystems()}
</div>
</div>
<div className='field publish'>
<label>publish</label>
<div className='value'>
{this.renderPublish()}
<small>Published homebrews will be publicly searchable (eventually...)</small>
</div>
</div>
</div>
}
});
module.exports = MetadataEditor;

View File

@@ -0,0 +1,63 @@
.metadataEditor{
position : absolute;
z-index : 10000;
box-sizing : border-box;
width : 100%;
padding : 25px;
background-color : #999;
.field{
display : flex;
width : 100%;
margin-bottom : 10px;
&>label{
display : inline-block;
vertical-align : top;
width : 80px;
font-size : 0.7em;
font-weight : 800;
line-height : 1.8em;
text-transform : uppercase;
flex-grow : 0;
}
&>.value{
flex-grow : 1;
}
}
.description.field textarea.value{
resize : none;
height : 5em;
font-family : 'Open Sans', sans-serif;
font-size : 0.8em;
}
.systems.field .value{
label{
vertical-align : middle;
margin-right : 15px;
cursor : pointer;
font-size : 0.7em;
font-weight : 800;
user-select : none;
}
input{
vertical-align : middle;
cursor : pointer;
}
}
.publish.field .value{
position : relative;
button.publish{
.button(@blueLight);
}
button.unpublish{
.button(@silver);
}
small{
position : absolute;
bottom : -15px;
left : 0px;
font-size : 0.6em;
font-style : italic;
}
}
}

View File

@@ -0,0 +1,91 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
const Snippets = require('./snippets/snippets.js');
const execute = function(val){
if(_.isFunction(val)) return val();
return val;
}
const Snippetbar = React.createClass({
getDefaultProps: function() {
return {
onInject : ()=>{},
onToggle : ()=>{},
showmeta : false
};
},
handleSnippetClick : function(injectedText){
this.props.onInject(injectedText)
},
renderSnippetGroups : function(){
return _.map(Snippets, (snippetGroup)=>{
return <SnippetGroup
groupName={snippetGroup.groupName}
icon={snippetGroup.icon}
snippets={snippetGroup.snippets}
key={snippetGroup.groupName}
onSnippetClick={this.handleSnippetClick}
/>
})
},
render : function(){
return <div className='snippetBar'>
{this.renderSnippetGroups()}
<div className={cx('toggleMeta', {selected: this.props.showmeta})}
onClick={this.props.onToggle}>
<i className='fa fa-database' />
</div>
</div>
}
});
module.exports = Snippetbar;
const SnippetGroup = React.createClass({
getDefaultProps: function() {
return {
groupName : '',
icon : 'fa-rocket',
snippets : [],
onSnippetClick : function(){},
};
},
handleSnippetClick : function(snippet){
this.props.onSnippetClick(execute(snippet.gen));
},
renderSnippets : function(){
return _.map(this.props.snippets, (snippet)=>{
return <div className='snippet' key={snippet.name} onClick={this.handleSnippetClick.bind(this, snippet)}>
<i className={'fa fa-fw ' + snippet.icon} />
{snippet.name}
</div>
})
},
render : function(){
return <div className='snippetGroup'>
<div className='text'>
<i className={'fa fa-fw ' + this.props.icon} />
<span className='groupName'>{this.props.groupName}</span>
</div>
<div className='dropdown'>
{this.renderSnippets()}
</div>
</div>
},
});

View File

@@ -0,0 +1,72 @@
.snippetBar{
@height : 25px;
position : relative;
height : @height;
background-color : #ddd;
.toggleMeta{
position : absolute;
top : 0px;
right : 0px;
height : @height;
width : @height;
cursor : pointer;
line-height : @height;
text-align : center;
&:hover, &.selected{
background-color : #999;
}
}
.snippetGroup{
display : inline-block;
height : @height;
padding : 0px 5px;
cursor : pointer;
font-size : 0.6em;
font-weight : 800;
line-height : @height;
text-transform : uppercase;
border-right : 1px solid black;
i{
vertical-align : middle;
margin-right : 3px;
font-size : 1.2em;
}
&:hover, &.selected{
background-color : #999;
}
.text{
line-height : @height;
.groupName{
font-size : 10px;
}
}
&:hover{
.dropdown{
visibility : visible;
}
}
.dropdown{
position : absolute;
top : 100%;
visibility : hidden;
z-index : 1000;
margin-left : -5px;
padding : 0px;
background-color : #ddd;
.snippet{
.animate(background-color);
padding : 10px;
cursor : pointer;
font-size : 10px;
i{
margin-right : 8px;
font-size : 13px;
}
&:hover{
background-color : #999;
}
}
}
}
}