mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-03 08:22:42 +00:00
Merge branch 'master' into SwappableThemes-ReorganizeFolderStructure
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
|
||||
/* eslint-disable max-lines */
|
||||
require('./metadataEditor.less');
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
@@ -6,6 +6,7 @@ const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
const request = require('superagent');
|
||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||
const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
|
||||
|
||||
const Themes = require('themes/themes.json');
|
||||
|
||||
@@ -21,7 +22,7 @@ const MetadataEditor = createClass({
|
||||
editId : null,
|
||||
title : '',
|
||||
description : '',
|
||||
tags : '',
|
||||
tags : [],
|
||||
published : false,
|
||||
authors : [],
|
||||
systems : [],
|
||||
@@ -50,9 +51,10 @@ const MetadataEditor = createClass({
|
||||
},
|
||||
|
||||
handleFieldChange : function(name, e){
|
||||
this.props.onChange(_.merge({}, this.props.metadata, {
|
||||
this.props.onChange({
|
||||
...this.props.metadata,
|
||||
[name] : e.target.value
|
||||
}));
|
||||
});
|
||||
},
|
||||
handleSystem : function(system, e){
|
||||
if(e.target.checked){
|
||||
@@ -71,9 +73,10 @@ const MetadataEditor = createClass({
|
||||
this.props.onChange(this.props.metadata);
|
||||
},
|
||||
handlePublish : function(val){
|
||||
this.props.onChange(_.merge({}, this.props.metadata, {
|
||||
this.props.onChange({
|
||||
...this.props.metadata,
|
||||
published : val
|
||||
}));
|
||||
});
|
||||
},
|
||||
|
||||
handleTheme : function(theme){
|
||||
@@ -211,8 +214,8 @@ const MetadataEditor = createClass({
|
||||
V3
|
||||
</label>
|
||||
|
||||
<a href='/v3_preview' target='_blank' rel='noopener noreferrer'>
|
||||
Click here for a quick intro to V3!
|
||||
<a href='/legacy' target='_blank' rel='noopener noreferrer'>
|
||||
Click here to see the demo page for the old Legacy renderer!
|
||||
</a>
|
||||
</div>
|
||||
</div>;
|
||||
@@ -243,13 +246,11 @@ const MetadataEditor = createClass({
|
||||
</button>
|
||||
{this.renderThumbnail()}
|
||||
</div>
|
||||
{/*}
|
||||
<div className='field tags'>
|
||||
<label>tags</label>
|
||||
<textarea value={this.props.metadata.tags}
|
||||
onChange={(e)=>this.handleFieldChange('tags', e)} />
|
||||
</div>
|
||||
*/}
|
||||
|
||||
<StringArrayEditor label='tags' valuePatterns={[/^(?:(?:group|meta|system|type):)?[A-Za-z0-9][A-Za-z0-9 \/.\-]{0,40}$/]}
|
||||
placeholder='add tag' unique={true}
|
||||
values={this.props.metadata.tags}
|
||||
onChange={(e)=>this.handleFieldChange('tags', e)}/>
|
||||
|
||||
{this.renderAuthors()}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@import 'naturalcrit/styles/colors.less';
|
||||
|
||||
.metadataEditor{
|
||||
position : absolute;
|
||||
@@ -159,4 +160,85 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.field .list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
#groupedIcon {
|
||||
#backgroundColors;
|
||||
display: inline-block;
|
||||
height: ~"calc(100% + 0.6em)";
|
||||
position: relative;
|
||||
top: -0.3em;
|
||||
right: -0.3em;
|
||||
cursor: pointer;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
|
||||
i {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-right: 1px solid black;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0.5em 0.5em 0;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: #dddddd;
|
||||
border-radius: .5em;
|
||||
font-size: .9em;
|
||||
margin: 2px;
|
||||
padding: .3em;
|
||||
|
||||
.icon {
|
||||
#groupedIcon
|
||||
}
|
||||
}
|
||||
|
||||
.input-group {
|
||||
height: ~"calc(.9em + 4px + .6em)";
|
||||
|
||||
input {
|
||||
border-radius: .5em 0 0 .5em;
|
||||
}
|
||||
|
||||
input:last-child {
|
||||
border-radius: .5em;
|
||||
}
|
||||
|
||||
.value {
|
||||
width: 7.5vw;
|
||||
min-width: 75px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.invalid:focus {
|
||||
background-color: pink;
|
||||
}
|
||||
|
||||
.icon {
|
||||
#groupedIcon;
|
||||
height: 97%;
|
||||
font-size: .8em;
|
||||
right: 1px;
|
||||
top: -.54em;
|
||||
|
||||
i {
|
||||
font-size: 1.125em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
142
client/homebrew/editor/stringArrayEditor/stringArrayEditor.jsx
Normal file
142
client/homebrew/editor/stringArrayEditor/stringArrayEditor.jsx
Normal file
@@ -0,0 +1,142 @@
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
const _ = require('lodash');
|
||||
|
||||
const StringArrayEditor = createClass({
|
||||
displayName : 'StringArrayEditor',
|
||||
getDefaultProps : function() {
|
||||
return {
|
||||
label : '',
|
||||
values : [],
|
||||
valuePatterns : null,
|
||||
placeholder : '',
|
||||
unique : false,
|
||||
cannotEdit : [],
|
||||
onChange : ()=>{}
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState : function() {
|
||||
return {
|
||||
valueContext : !!this.props.values ? this.props.values.map((value)=>({
|
||||
value,
|
||||
editing : false
|
||||
})) : [],
|
||||
temporaryValue : '',
|
||||
updateValue : ''
|
||||
};
|
||||
},
|
||||
|
||||
componentDidUpdate : function(prevProps) {
|
||||
if(!_.eq(this.props.values, prevProps.values)) {
|
||||
this.setState({
|
||||
valueContext : this.props.values ? this.props.values.map((newValue)=>({
|
||||
value : newValue,
|
||||
editing : this.state.valueContext.find(({ value })=>value === newValue)?.editing || false
|
||||
})) : []
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
handleChange : function(value) {
|
||||
this.props.onChange({
|
||||
target : {
|
||||
value
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addValue : function(value){
|
||||
this.handleChange(_.uniq([...this.props.values, value]));
|
||||
this.setState({
|
||||
temporaryValue : ''
|
||||
});
|
||||
},
|
||||
|
||||
removeValue : function(index){
|
||||
this.handleChange(this.props.values.filter((_, i)=>i !== index));
|
||||
},
|
||||
|
||||
updateValue : function(value, index){
|
||||
const valueContext = this.state.valueContext;
|
||||
valueContext[index].value = value;
|
||||
valueContext[index].editing = false;
|
||||
this.handleChange(valueContext.map((context)=>context.value));
|
||||
this.setState({ valueContext, updateValue: '' });
|
||||
},
|
||||
|
||||
editValue : function(index){
|
||||
if(!!this.props.cannotEdit && this.props.cannotEdit.includes(this.props.values[index])) {
|
||||
return;
|
||||
}
|
||||
const valueContext = this.state.valueContext.map((context, i)=>{
|
||||
context.editing = index === i;
|
||||
return context;
|
||||
});
|
||||
this.setState({ valueContext, updateValue: this.props.values[index] });
|
||||
},
|
||||
|
||||
valueIsValid : function(value, index) {
|
||||
const values = _.clone(this.props.values);
|
||||
if(index !== undefined) {
|
||||
values.splice(index, 1);
|
||||
}
|
||||
const matchesPatterns = !this.props.valuePatterns || this.props.valuePatterns.some((pattern)=>!!(value || '').match(pattern));
|
||||
const uniqueIfSet = !this.props.unique || !values.includes(value);
|
||||
return matchesPatterns && uniqueIfSet;
|
||||
},
|
||||
|
||||
handleValueInputKeyDown : function(event, index) {
|
||||
if(event.key === 'Enter') {
|
||||
if(this.valueIsValid(event.target.value, index)) {
|
||||
if(index !== undefined) {
|
||||
this.updateValue(event.target.value, index);
|
||||
} else {
|
||||
this.addValue(event.target.value);
|
||||
}
|
||||
}
|
||||
} else if(event.key === 'Escape') {
|
||||
this.closeEditInput(index);
|
||||
}
|
||||
},
|
||||
|
||||
closeEditInput : function(index) {
|
||||
const valueContext = this.state.valueContext;
|
||||
valueContext[index].editing = false;
|
||||
this.setState({ valueContext, updateValue: '' });
|
||||
},
|
||||
|
||||
render : function() {
|
||||
const valueElements = Object.values(this.state.valueContext).map((context, i)=>context.editing
|
||||
? <React.Fragment key={i}>
|
||||
<div className='input-group'>
|
||||
<input type='text' className={`value ${this.valueIsValid(this.state.updateValue, i) ? '' : 'invalid'}`} autoFocus placeholder={this.props.placeholder}
|
||||
value={this.state.updateValue}
|
||||
onKeyDown={(e)=>this.handleValueInputKeyDown(e, i)}
|
||||
onChange={(e)=>this.setState({ updateValue: e.target.value })}/>
|
||||
{<div className='icon steel' onClick={(e)=>{ e.stopPropagation(); this.closeEditInput(i); }}><i className='fa fa-undo fa-fw'/></div>}
|
||||
{this.valueIsValid(this.state.updateValue, i) ? <div className='icon steel' onClick={(e)=>{ e.stopPropagation(); this.updateValue(this.state.updateValue, i); }}><i className='fa fa-check fa-fw'/></div> : null}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
: <div className='badge' key={i} onClick={()=>this.editValue(i)}>{context.value}
|
||||
{!!this.props.cannotEdit && this.props.cannotEdit.includes(context.value) ? null : <div className='icon steel' onClick={(e)=>{ e.stopPropagation(); this.removeValue(i); }}><i className='fa fa-times fa-fw'/></div>}
|
||||
</div>
|
||||
);
|
||||
|
||||
return <div className='field values'>
|
||||
<label>{this.props.label}</label>
|
||||
<div className='list'>
|
||||
{valueElements}
|
||||
<div className='input-group'>
|
||||
<input type='text' className={`value ${this.valueIsValid(this.state.temporaryValue) ? '' : 'invalid'}`} placeholder={this.props.placeholder}
|
||||
value={this.state.temporaryValue}
|
||||
onKeyDown={(e)=>this.handleValueInputKeyDown(e)}
|
||||
onChange={(e)=>this.setState({ temporaryValue: e.target.value })}/>
|
||||
{this.valueIsValid(this.state.temporaryValue) ? <div className='icon steel' onClick={(e)=>{ e.stopPropagation(); this.addValue(this.state.temporaryValue); }}><i className='fa fa-check fa-fw'/></div> : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = StringArrayEditor;
|
||||
Reference in New Issue
Block a user