mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-04 21:12:41 +00:00
Merge branch 'dependabot/npm_and_yarn/mongoose-7.0.2' of https://github.com/naturalcrit/homebrewery into dependabot/npm_and_yarn/mongoose-7.0.2
This commit is contained in:
129
client/components/combobox.jsx
Normal file
129
client/components/combobox.jsx
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
const React = require('react');
|
||||||
|
const createClass = require('create-react-class');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const cx = require('classnames');
|
||||||
|
require('./combobox.less');
|
||||||
|
|
||||||
|
const Combobox = createClass({
|
||||||
|
displayName : 'Combobox',
|
||||||
|
getDefaultProps : function() {
|
||||||
|
return {
|
||||||
|
className : '',
|
||||||
|
trigger : 'hover',
|
||||||
|
default : '',
|
||||||
|
placeholder : '',
|
||||||
|
autoSuggest : {
|
||||||
|
clearAutoSuggestOnClick : true,
|
||||||
|
suggestMethod : 'includes',
|
||||||
|
filterOn : [] // should allow as array to filter on multiple attributes, or even custom filter
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getInitialState : function() {
|
||||||
|
return {
|
||||||
|
showDropdown : false,
|
||||||
|
value : '',
|
||||||
|
options : [...this.props.options],
|
||||||
|
inputFocused : false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
componentDidMount : function() {
|
||||||
|
if(this.props.trigger == 'click')
|
||||||
|
document.addEventListener('click', this.handleClickOutside);
|
||||||
|
this.setState({
|
||||||
|
value : this.props.default
|
||||||
|
});
|
||||||
|
},
|
||||||
|
componentWillUnmount : function() {
|
||||||
|
if(this.props.trigger == 'click')
|
||||||
|
document.removeEventListener('click', this.handleClickOutside);
|
||||||
|
},
|
||||||
|
handleClickOutside : function(e){
|
||||||
|
// Close dropdown when clicked outside
|
||||||
|
if(this.refs.dropdown && !this.refs.dropdown.contains(e.target)) {
|
||||||
|
this.handleDropdown(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDropdown : function(show){
|
||||||
|
this.setState({
|
||||||
|
showDropdown : show,
|
||||||
|
inputFocused : this.props.autoSuggest.clearAutoSuggestOnClick ? show : false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleInput : function(e){
|
||||||
|
e.persist();
|
||||||
|
this.setState({
|
||||||
|
value : e.target.value,
|
||||||
|
inputFocused : false
|
||||||
|
}, ()=>{
|
||||||
|
this.props.onEntry(e);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSelect : function(e){
|
||||||
|
this.setState({
|
||||||
|
value : e.currentTarget.getAttribute('data-value')
|
||||||
|
}, ()=>{this.props.onSelect(this.state.value);});
|
||||||
|
;
|
||||||
|
},
|
||||||
|
renderTextInput : function(){
|
||||||
|
return (
|
||||||
|
<div className='dropdown-input item'
|
||||||
|
onMouseEnter={this.props.trigger == 'hover' ? ()=>{this.handleDropdown(true);} : undefined}
|
||||||
|
onClick= {this.props.trigger == 'click' ? ()=>{this.handleDropdown(true);} : undefined}>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
onChange={(e)=>this.handleInput(e)}
|
||||||
|
value={this.state.value || ''}
|
||||||
|
placeholder={this.props.placeholder}
|
||||||
|
onBlur={(e)=>{
|
||||||
|
if(!e.target.checkValidity()){
|
||||||
|
this.setState({
|
||||||
|
value : this.props.default
|
||||||
|
}, ()=>this.props.onEntry(e));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
renderDropdown : function(dropdownChildren){
|
||||||
|
if(!this.state.showDropdown) return null;
|
||||||
|
if(this.props.autoSuggest && !this.state.inputFocused){
|
||||||
|
const suggestMethod = this.props.autoSuggest.suggestMethod;
|
||||||
|
const filterOn = _.isString(this.props.autoSuggest.filterOn) ? [this.props.autoSuggest.filterOn] : this.props.autoSuggest.filterOn;
|
||||||
|
const filteredArrays = filterOn.map((attr)=>{
|
||||||
|
const children = dropdownChildren.filter((item)=>{
|
||||||
|
if(suggestMethod === 'includes'){
|
||||||
|
return item.props[attr]?.toLowerCase().includes(this.state.value.toLowerCase());
|
||||||
|
} else if(suggestMethod === 'startsWith'){
|
||||||
|
return item.props[attr]?.toLowerCase().startsWith(this.state.value.toLowerCase());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return children;
|
||||||
|
});
|
||||||
|
dropdownChildren = _.uniq(filteredArrays.flat(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='dropdown-options'>
|
||||||
|
{dropdownChildren}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
render : function () {
|
||||||
|
const dropdownChildren = this.state.options.map((child, i)=>{
|
||||||
|
const clone = React.cloneElement(child, { onClick: (e)=>this.handleSelect(e) });
|
||||||
|
return clone;
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div className={`dropdown-container ${this.props.className}`}
|
||||||
|
ref='dropdown'
|
||||||
|
onMouseLeave={this.props.trigger == 'hover' ? ()=>{this.handleDropdown(false);} : undefined}>
|
||||||
|
{this.renderTextInput()}
|
||||||
|
{this.renderDropdown(dropdownChildren)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Combobox;
|
||||||
50
client/components/combobox.less
Normal file
50
client/components/combobox.less
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
.dropdown-container {
|
||||||
|
position:relative;
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.dropdown-options {
|
||||||
|
position:absolute;
|
||||||
|
background-color: white;
|
||||||
|
z-index: 100;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid gray;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 200px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 14px;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #949494;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 3px solid #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
position:relative;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: Open Sans;
|
||||||
|
padding: 5px;
|
||||||
|
cursor: default;
|
||||||
|
margin: 0 3px;
|
||||||
|
//border-bottom: 1px solid darkgray;
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(120%);
|
||||||
|
background-color: rgb(163, 163, 163);
|
||||||
|
}
|
||||||
|
.detail {
|
||||||
|
width:100%;
|
||||||
|
text-align: left;
|
||||||
|
color: rgb(124, 124, 124);
|
||||||
|
font-style:italic;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ const BrewRenderer = createClass({
|
|||||||
style : '',
|
style : '',
|
||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
theme : '5ePHB',
|
theme : '5ePHB',
|
||||||
|
lang : '',
|
||||||
errors : []
|
errors : []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -190,7 +191,6 @@ const BrewRenderer = createClass({
|
|||||||
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
|
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
|
||||||
const themePath = this.props.theme ?? '5ePHB';
|
const themePath = this.props.theme ?? '5ePHB';
|
||||||
const baseThemePath = Themes[rendererPath][themePath].baseTheme;
|
const baseThemePath = Themes[rendererPath][themePath].baseTheme;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{!this.state.isMounted
|
{!this.state.isMounted
|
||||||
@@ -223,7 +223,7 @@ const BrewRenderer = createClass({
|
|||||||
&&
|
&&
|
||||||
<>
|
<>
|
||||||
{this.renderStyle()}
|
{this.renderStyle()}
|
||||||
<div className='pages' ref='pages'>
|
<div className='pages' ref='pages' lang={`${this.props.lang || 'en'}`}>
|
||||||
{this.renderPages()}
|
{this.renderPages()}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const _ = require('lodash');
|
|||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
const request = require('../../utils/request-middleware.js');
|
const request = require('../../utils/request-middleware.js');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
|
const Combobox = require('client/components/combobox.jsx');
|
||||||
const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
|
const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
|
||||||
|
|
||||||
const Themes = require('themes/themes.json');
|
const Themes = require('themes/themes.json');
|
||||||
@@ -35,7 +36,8 @@ const MetadataEditor = createClass({
|
|||||||
authors : [],
|
authors : [],
|
||||||
systems : [],
|
systems : [],
|
||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
theme : '5ePHB'
|
theme : '5ePHB',
|
||||||
|
lang : 'en'
|
||||||
},
|
},
|
||||||
onChange : ()=>{},
|
onChange : ()=>{},
|
||||||
reportError : ()=>{}
|
reportError : ()=>{}
|
||||||
@@ -76,6 +78,7 @@ const MetadataEditor = createClass({
|
|||||||
const errMessage = validationErr.map((err)=>{
|
const errMessage = validationErr.map((err)=>{
|
||||||
return `- ${err}`;
|
return `- ${err}`;
|
||||||
}).join('\n');
|
}).join('\n');
|
||||||
|
|
||||||
callIfExists(e.target, 'setCustomValidity', errMessage);
|
callIfExists(e.target, 'setCustomValidity', errMessage);
|
||||||
callIfExists(e.target, 'reportValidity');
|
callIfExists(e.target, 'reportValidity');
|
||||||
}
|
}
|
||||||
@@ -111,6 +114,11 @@ const MetadataEditor = createClass({
|
|||||||
this.props.onChange(this.props.metadata);
|
this.props.onChange(this.props.metadata);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleLanguage : function(languageCode){
|
||||||
|
this.props.metadata.lang = languageCode;
|
||||||
|
this.props.onChange(this.props.metadata);
|
||||||
|
},
|
||||||
|
|
||||||
handleDelete : function(){
|
handleDelete : function(){
|
||||||
if(this.props.metadata.authors && this.props.metadata.authors.length <= 1){
|
if(this.props.metadata.authors && this.props.metadata.authors.length <= 1){
|
||||||
if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return;
|
if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return;
|
||||||
@@ -224,6 +232,46 @@ const MetadataEditor = createClass({
|
|||||||
</div>;
|
</div>;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderLanguageDropdown : function(){
|
||||||
|
const langCodes = ['en', 'de', 'de-ch', 'fr', 'ja', 'es', 'it', 'sv', 'ru', 'zh-Hans', 'zh-Hant'];
|
||||||
|
const listLanguages = ()=>{
|
||||||
|
return _.map(langCodes.sort(), (code, index)=>{
|
||||||
|
const localName = new Intl.DisplayNames([code], { type: 'language' });
|
||||||
|
const englishName = new Intl.DisplayNames('en', { type: 'language' });
|
||||||
|
return <div className='item' title={`${englishName.of(code)}`} key={`${index}`} data-value={`${code}`} data-detail={`${localName.of(code)}`}>
|
||||||
|
{`${code}`}
|
||||||
|
<div className='detail'>{`${localName.of(code)}`}</div>
|
||||||
|
</div>;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedHandleFieldChange = _.debounce(this.handleFieldChange, 500);
|
||||||
|
|
||||||
|
return <div className='field language'>
|
||||||
|
<label>language</label>
|
||||||
|
<div className='value'>
|
||||||
|
<Combobox trigger='click'
|
||||||
|
className='language-dropdown'
|
||||||
|
default={this.props.metadata.lang || ''}
|
||||||
|
placeholder='en'
|
||||||
|
onSelect={(value)=>this.handleLanguage(value)}
|
||||||
|
onEntry={(e)=> { e.target.setCustomValidity(''); //Clear the validation popup while typing
|
||||||
|
debouncedHandleFieldChange('lang', e);
|
||||||
|
}}
|
||||||
|
options={listLanguages()}
|
||||||
|
autoSuggest={{
|
||||||
|
suggestMethod : 'startsWith',
|
||||||
|
clearAutoSuggestOnClick : true,
|
||||||
|
filterOn : ['data-value', 'data-detail', 'title']
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</Combobox>
|
||||||
|
<small>Sets the HTML Lang property for your brew. May affect hyphenation or spellcheck.</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
|
||||||
renderRenderOptions : function(){
|
renderRenderOptions : function(){
|
||||||
if(!global.enable_v3) return;
|
if(!global.enable_v3) return;
|
||||||
|
|
||||||
@@ -301,6 +349,8 @@ const MetadataEditor = createClass({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{this.renderLanguageDropdown()}
|
||||||
|
|
||||||
{this.renderThemeDropdown()}
|
{this.renderThemeDropdown()}
|
||||||
|
|
||||||
{this.renderRenderOptions()}
|
{this.renderRenderOptions()}
|
||||||
@@ -315,7 +365,7 @@ const MetadataEditor = createClass({
|
|||||||
validators={[(v)=>!this.props.metadata.authors?.includes(v)]}
|
validators={[(v)=>!this.props.metadata.authors?.includes(v)]}
|
||||||
placeholder='invite author' unique={true}
|
placeholder='invite author' unique={true}
|
||||||
values={this.props.metadata.invitedAuthors}
|
values={this.props.metadata.invitedAuthors}
|
||||||
notes={['Invited authors are case sensitive.', 'After adding an invited author, send them the edit link. There, they can choose to accept or decline the invitation.']}
|
notes={['Invited author usernames are case sensitive.', 'After adding an invited author, send them the edit link. There, they can choose to accept or decline the invitation.']}
|
||||||
onChange={(e)=>this.handleFieldChange('invitedAuthors', e)}/>
|
onChange={(e)=>this.handleFieldChange('invitedAuthors', e)}/>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|||||||
@@ -36,11 +36,15 @@
|
|||||||
flex: 5 0 200px;
|
flex: 5 0 200px;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.field{
|
.field{
|
||||||
display : flex;
|
display : flex;
|
||||||
flex-wrap : wrap;
|
flex-wrap : wrap;
|
||||||
width : 100%;
|
width : 100%;
|
||||||
min-width : 200px;
|
min-width : 200px;
|
||||||
|
position : relative;
|
||||||
&>label{
|
&>label{
|
||||||
width : 80px;
|
width : 80px;
|
||||||
font-size : 11px;
|
font-size : 11px;
|
||||||
@@ -57,6 +61,9 @@
|
|||||||
}
|
}
|
||||||
input[type='text'], textarea {
|
input[type='text'], textarea {
|
||||||
border : 1px solid gray;
|
border : 1px solid gray;
|
||||||
|
&:focus {
|
||||||
|
outline: 1px solid #444;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.thumbnail{
|
&.thumbnail{
|
||||||
height : 1.4em;
|
height : 1.4em;
|
||||||
@@ -88,9 +95,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.language .language-dropdown {
|
||||||
|
max-width : 150px;
|
||||||
|
z-index : 200;
|
||||||
|
}
|
||||||
small {
|
small {
|
||||||
font-size : 0.6em;
|
font-size : 0.6em;
|
||||||
font-style : italic;
|
font-style : italic;
|
||||||
|
line-height : 1.4em;
|
||||||
|
display : inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +172,7 @@
|
|||||||
.navDropdownContainer {
|
.navDropdownContainer {
|
||||||
background-color : white;
|
background-color : white;
|
||||||
position : relative;
|
position : relative;
|
||||||
z-index : 500;
|
z-index : 100;
|
||||||
&.disabled {
|
&.disabled {
|
||||||
font-style :italic;
|
font-style :italic;
|
||||||
font-style : italic;
|
font-style : italic;
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
language : [
|
lang : [
|
||||||
(value)=>{
|
(value)=>{
|
||||||
return new RegExp(/[a-z]{2,3}(-.*)?/).test(value || '') === false ? 'Invalid language code.' : null;
|
return new RegExp(/^([a-zA-Z]{2,3})(-[a-zA-Z]{4})?(-(?:[0-9]{3}|[a-zA-Z]{2}))?$/).test(value) === false && (value.length > 0) ? 'Invalid language code.' : null;
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ const Homebrew = createClass({
|
|||||||
editId : null,
|
editId : null,
|
||||||
createdAt : null,
|
createdAt : null,
|
||||||
updatedAt : null,
|
updatedAt : null,
|
||||||
|
lang : ''
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const Account = createClass({
|
|||||||
if(global.account){
|
if(global.account){
|
||||||
return <Nav.dropdown>
|
return <Nav.dropdown>
|
||||||
<Nav.item
|
<Nav.item
|
||||||
className='account'
|
className='account username'
|
||||||
color='orange'
|
color='orange'
|
||||||
icon='fas fa-user'
|
icon='fas fa-user'
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -187,4 +187,7 @@
|
|||||||
.account.navItem{
|
.account.navItem{
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
.account.username.navItem{
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -398,7 +398,14 @@ const EditPage = createClass({
|
|||||||
reportError={this.errorReported}
|
reportError={this.errorReported}
|
||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} errors={this.state.htmlErrors} />
|
<BrewRenderer
|
||||||
|
text={this.state.brew.text}
|
||||||
|
style={this.state.brew.style}
|
||||||
|
renderer={this.state.brew.renderer}
|
||||||
|
theme={this.state.brew.theme}
|
||||||
|
errors={this.state.htmlErrors}
|
||||||
|
lang={this.state.brew.lang}
|
||||||
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ const NewPage = createClass({
|
|||||||
// brew.description = metaStorage?.description || this.state.brew.description;
|
// brew.description = metaStorage?.description || this.state.brew.description;
|
||||||
brew.renderer = metaStorage?.renderer ?? brew.renderer;
|
brew.renderer = metaStorage?.renderer ?? brew.renderer;
|
||||||
brew.theme = metaStorage?.theme ?? brew.theme;
|
brew.theme = metaStorage?.theme ?? brew.theme;
|
||||||
|
brew.lang = metaStorage?.lang ?? brew.lang;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
brew : brew
|
brew : brew
|
||||||
@@ -70,7 +71,7 @@ const NewPage = createClass({
|
|||||||
localStorage.setItem(BREWKEY, brew.text);
|
localStorage.setItem(BREWKEY, brew.text);
|
||||||
if(brew.style)
|
if(brew.style)
|
||||||
localStorage.setItem(STYLEKEY, brew.style);
|
localStorage.setItem(STYLEKEY, brew.style);
|
||||||
localStorage.setItem(METAKEY, JSON.stringify({ 'renderer': brew.renderer, 'theme': brew.theme }));
|
localStorage.setItem(METAKEY, JSON.stringify({ 'renderer': brew.renderer, 'theme': brew.theme, 'lang': brew.lang }));
|
||||||
},
|
},
|
||||||
componentWillUnmount : function() {
|
componentWillUnmount : function() {
|
||||||
document.removeEventListener('keydown', this.handleControlKeys);
|
document.removeEventListener('keydown', this.handleControlKeys);
|
||||||
@@ -114,13 +115,16 @@ const NewPage = createClass({
|
|||||||
handleMetaChange : function(metadata){
|
handleMetaChange : function(metadata){
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
brew : { ...prevState.brew, ...metadata },
|
brew : { ...prevState.brew, ...metadata },
|
||||||
}));
|
}), ()=>{
|
||||||
localStorage.setItem(METAKEY, JSON.stringify({
|
localStorage.setItem(METAKEY, JSON.stringify({
|
||||||
// 'title' : this.state.brew.title,
|
// 'title' : this.state.brew.title,
|
||||||
// 'description' : this.state.brew.description,
|
// 'description' : this.state.brew.description,
|
||||||
'renderer' : this.state.brew.renderer,
|
'renderer' : this.state.brew.renderer,
|
||||||
'theme' : this.state.brew.theme
|
'theme' : this.state.brew.theme,
|
||||||
}));
|
'lang' : this.state.brew.lang
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
;
|
||||||
},
|
},
|
||||||
|
|
||||||
save : async function(){
|
save : async function(){
|
||||||
@@ -211,7 +215,7 @@ const NewPage = createClass({
|
|||||||
onMetaChange={this.handleMetaChange}
|
onMetaChange={this.handleMetaChange}
|
||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} errors={this.state.htmlErrors}/>
|
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} lang={this.state.brew.lang} errors={this.state.htmlErrors}/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
15
package.json
15
package.json
@@ -43,6 +43,9 @@
|
|||||||
"shared",
|
"shared",
|
||||||
"server"
|
"server"
|
||||||
],
|
],
|
||||||
|
"coveragePathIgnorePatterns": [
|
||||||
|
"build/*"
|
||||||
|
],
|
||||||
"coverageThreshold" : {
|
"coverageThreshold" : {
|
||||||
"global" : {
|
"global" : {
|
||||||
"statements" : 25,
|
"statements" : 25,
|
||||||
@@ -68,11 +71,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.21.0",
|
"@babel/core": "^7.21.3",
|
||||||
"@babel/plugin-transform-runtime": "^7.21.0",
|
"@babel/plugin-transform-runtime": "^7.21.0",
|
||||||
"@babel/preset-env": "^7.19.4",
|
"@babel/preset-env": "^7.19.4",
|
||||||
"@babel/preset-react": "^7.18.6",
|
"@babel/preset-react": "^7.18.6",
|
||||||
"@googleapis/drive": "^4.0.2",
|
"@googleapis/drive": "^5.0.1",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"codemirror": "^5.65.6",
|
"codemirror": "^5.65.6",
|
||||||
@@ -82,7 +85,7 @@
|
|||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-async-handler": "^1.2.0",
|
"express-async-handler": "^1.2.0",
|
||||||
"express-static-gzip": "2.1.7",
|
"express-static-gzip": "2.1.7",
|
||||||
"fs-extra": "11.1.0",
|
"fs-extra": "11.1.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jwt-simple": "^0.5.6",
|
"jwt-simple": "^0.5.6",
|
||||||
"less": "^3.13.1",
|
"less": "^3.13.1",
|
||||||
@@ -94,17 +97,17 @@
|
|||||||
"mongoose": "^7.0.2",
|
"mongoose": "^7.0.2",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "3.3.4",
|
||||||
"nconf": "^0.12.0",
|
"nconf": "^0.12.0",
|
||||||
"npm": "^8.10.0",
|
"npm": "^9.6.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-frame-component": "4.1.3",
|
"react-frame-component": "4.1.3",
|
||||||
"react-router-dom": "6.8.2",
|
"react-router-dom": "6.9.0",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^6.1.0",
|
"superagent": "^6.1.0",
|
||||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.35.0",
|
"eslint": "^8.36.0",
|
||||||
"eslint-plugin-react": "^7.32.2",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"supertest": "^6.3.3"
|
"supertest": "^6.3.3"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const splitTextStyleAndMetadata = (brew)=>{
|
|||||||
const index = brew.text.indexOf('```\n\n');
|
const index = brew.text.indexOf('```\n\n');
|
||||||
const metadataSection = brew.text.slice(12, index - 1);
|
const metadataSection = brew.text.slice(12, index - 1);
|
||||||
const metadata = yaml.load(metadataSection);
|
const metadata = yaml.load(metadataSection);
|
||||||
Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme']));
|
Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang']));
|
||||||
brew.text = brew.text.slice(index + 5);
|
brew.text = brew.text.slice(index + 5);
|
||||||
}
|
}
|
||||||
if(brew.text.startsWith('```css')) {
|
if(brew.text.startsWith('```css')) {
|
||||||
@@ -225,6 +225,7 @@ app.get('/user/:username', async (req, res, next)=>{
|
|||||||
'pageCount',
|
'pageCount',
|
||||||
'description',
|
'description',
|
||||||
'authors',
|
'authors',
|
||||||
|
'lang',
|
||||||
'published',
|
'published',
|
||||||
'views',
|
'views',
|
||||||
'shareId',
|
'shareId',
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const DEFAULT_BREW = {
|
|||||||
authors : [],
|
authors : [],
|
||||||
tags : [],
|
tags : [],
|
||||||
systems : [],
|
systems : [],
|
||||||
|
lang : 'en',
|
||||||
thumbnail : '',
|
thumbnail : '',
|
||||||
views : 0,
|
views : 0,
|
||||||
published : false,
|
published : false,
|
||||||
|
|||||||
@@ -129,7 +129,9 @@ const GoogleActions = {
|
|||||||
description : file.description,
|
description : file.description,
|
||||||
views : parseInt(file.properties.views),
|
views : parseInt(file.properties.views),
|
||||||
published : file.properties.published ? file.properties.published == 'true' : false,
|
published : file.properties.published ? file.properties.published == 'true' : false,
|
||||||
systems : []
|
systems : [],
|
||||||
|
lang : file.properties.lang,
|
||||||
|
thumbnail : file.properties.thumbnail
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return brews;
|
return brews;
|
||||||
@@ -149,7 +151,8 @@ const GoogleActions = {
|
|||||||
editId : brew.editId || nanoid(12),
|
editId : brew.editId || nanoid(12),
|
||||||
pageCount : brew.pageCount,
|
pageCount : brew.pageCount,
|
||||||
renderer : brew.renderer || 'legacy',
|
renderer : brew.renderer || 'legacy',
|
||||||
isStubbed : true
|
isStubbed : true,
|
||||||
|
lang : brew.lang || 'en'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
media : {
|
media : {
|
||||||
@@ -187,7 +190,8 @@ const GoogleActions = {
|
|||||||
pageCount : brew.pageCount,
|
pageCount : brew.pageCount,
|
||||||
renderer : brew.renderer || 'legacy',
|
renderer : brew.renderer || 'legacy',
|
||||||
isStubbed : true,
|
isStubbed : true,
|
||||||
version : 1
|
version : 1,
|
||||||
|
lang : brew.lang || 'en'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -255,6 +259,7 @@ const GoogleActions = {
|
|||||||
description : obj.data.description,
|
description : obj.data.description,
|
||||||
systems : obj.data.properties.systems ? obj.data.properties.systems.split(',') : [],
|
systems : obj.data.properties.systems ? obj.data.properties.systems.split(',') : [],
|
||||||
authors : [],
|
authors : [],
|
||||||
|
lang : obj.data.properties.lang,
|
||||||
published : obj.data.properties.published ? obj.data.properties.published == 'true' : false,
|
published : obj.data.properties.published ? obj.data.properties.published == 'true' : false,
|
||||||
trashed : obj.data.trashed,
|
trashed : obj.data.trashed,
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ describe('Tests for api', ()=>{
|
|||||||
description : 'this is a description',
|
description : 'this is a description',
|
||||||
tags : ['something', 'fun'],
|
tags : ['something', 'fun'],
|
||||||
systems : ['D&D 5e'],
|
systems : ['D&D 5e'],
|
||||||
|
lang : 'en',
|
||||||
renderer : 'v3',
|
renderer : 'v3',
|
||||||
theme : 'phb',
|
theme : 'phb',
|
||||||
published : true,
|
published : true,
|
||||||
@@ -255,6 +256,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
|||||||
pageCount : 1,
|
pageCount : 1,
|
||||||
published : false,
|
published : false,
|
||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
|
lang : 'en',
|
||||||
shareId : undefined,
|
shareId : undefined,
|
||||||
systems : [],
|
systems : [],
|
||||||
tags : [],
|
tags : [],
|
||||||
@@ -448,6 +450,7 @@ brew`);
|
|||||||
pageCount : 1,
|
pageCount : 1,
|
||||||
published : false,
|
published : false,
|
||||||
renderer : 'V3',
|
renderer : 'V3',
|
||||||
|
lang : 'en',
|
||||||
shareId : expect.any(String),
|
shareId : expect.any(String),
|
||||||
style : undefined,
|
style : undefined,
|
||||||
systems : [],
|
systems : [],
|
||||||
@@ -506,6 +509,7 @@ brew`);
|
|||||||
pageCount : undefined,
|
pageCount : undefined,
|
||||||
published : false,
|
published : false,
|
||||||
renderer : undefined,
|
renderer : undefined,
|
||||||
|
lang : 'en',
|
||||||
shareId : expect.any(String),
|
shareId : expect.any(String),
|
||||||
googleId : expect.any(String),
|
googleId : expect.any(String),
|
||||||
style : undefined,
|
style : undefined,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const HomebrewSchema = mongoose.Schema({
|
|||||||
description : { type: String, default: '' },
|
description : { type: String, default: '' },
|
||||||
tags : [String],
|
tags : [String],
|
||||||
systems : [String],
|
systems : [String],
|
||||||
|
lang : { type: String, default: 'en' },
|
||||||
renderer : { type: String, default: '' },
|
renderer : { type: String, default: '' },
|
||||||
authors : [String],
|
authors : [String],
|
||||||
invitedAuthors : [String],
|
invitedAuthors : [String],
|
||||||
|
|||||||
Reference in New Issue
Block a user