Lots of different changes, mostly to convert to InfernoJS and to work on updating to Bulma structure.

This commit is contained in:
Robbie Antenesse 2017-01-14 12:55:42 -07:00
parent d4f4dd62c8
commit 91d4d5b5da
20 changed files with 289 additions and 131 deletions

View File

@ -1,3 +1,4 @@
{
"presets": [ "es2015", "react" ]
"presets": [ "es2015" ],
"plugins": [ "inferno" ]
}

View File

@ -23,6 +23,8 @@
"dependencies": {
"babel-polyfill": "^6.13.0",
"bulma": "^0.2.3",
"inferno": "^1.0.3",
"inferno-component": "^1.0.3",
"json-query": "^2.2.0",
"marked": "^0.3.6",
"papaparse": "^4.1.2",
@ -33,6 +35,7 @@
"devDependencies": {
"babel-core": "^6.14.0",
"babel-loader": "^6.2.5",
"babel-plugin-inferno": "^1.4.0",
"babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.11.1",
"css-loader": "^0.25.0",

View File

@ -1,7 +1,10 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
// Creates a clickable <span> tag with an onclick action.
export class Button extends React.Component {
// export class Button extends React.Component {
export class Button extends Component {
constructor(props) {
super(props);
}

View File

@ -1,4 +1,5 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import {Input} from './Input';
export class Checkbox extends Input {
@ -25,7 +26,7 @@ export class Checkbox extends Input {
{this.props.name}
{this.showHelperLink()}
</span>
<input type="checkbox" onChange={this.handleOnChange} checked={(this.state.value) ? 'checked' : null} disabled={(this.state.isDisabled) ? 'disabled' : null} />
<input type="checkbox" onInput={this.handleOnChange} checked={(this.state.value) ? 'checked' : null} disabled={(this.state.isDisabled) ? 'disabled' : null} />
</label>
);
}

View File

@ -1,9 +1,12 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
import {Word} from './Word';
// A component for showing just the list of words provided to it as a prop.
export class Dictionary extends React.Component {
// export class Dictionary extends React.Component {
export class Dictionary extends Component {
constructor(props) {
super(props);
}
@ -33,7 +36,7 @@ export class Dictionary extends React.Component {
render() {
return (
<div id="theDictionary">
<div className='container' id="theDictionary">
{this.showWords()}
</div>
);

View File

@ -1,4 +1,5 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import {Input} from './Input';
import {htmlEntities} from '../js/helpers';
@ -39,11 +40,15 @@ export class Dropdown extends Input {
render() {
return (
<label>
<span>
{this.props.name}
{this.showHelperLink()}
</span>
<label className='control'>
<div className='level'>
<span className='label level-item'>
{this.props.name}
</span>
<span className='level-item'>
{this.showHelperLink()}
</span>
</div>
<select value={this.state.value} onChange={this.handleOnChange} disabled={(this.state.isDisabled) ? 'disabled' : null}>
<option value=" "></option>
{this.parseOptions(this.props.optionsList)}

View File

@ -1,4 +1,6 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
import {Input} from './Input';
import {TextArea} from './TextArea';
@ -7,7 +9,8 @@ import {Button} from './Button';
import {FixedPage} from './FixedPage';
// A component that allows you to edit the dictionary's details and settings.
export class EditDictionaryForm extends React.Component {
// export class EditDictionaryForm extends React.Component {
export class EditDictionaryForm extends Component {
constructor(props) {
super(props);

View File

@ -1,4 +1,6 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
import {Input} from './Input';
import {TextArea} from './TextArea';
@ -6,7 +8,8 @@ import {TextArea} from './TextArea';
import {WordForm} from './WordForm';
// A component that allows you to edit a word
export class EditWordForm extends React.Component {
// export class EditWordForm extends React.Component {
export class EditWordForm extends Component {
constructor(props) {
super(props);
}

View File

@ -1,9 +1,12 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
import {Button} from './Button';
// Creates a page that floats above other elements when a connected button is clicked.
export class FixedPage extends React.Component {
// export class FixedPage extends React.Component {
export class FixedPage extends Component {
constructor(props) {
super(props);

View File

@ -1,10 +1,13 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
import marked from 'marked';
import {FixedPage} from './FixedPage';
// A component for the site footer
export class Footer extends React.Component {
// export class Footer extends React.Component {
export class Footer extends Component {
constructor(props) {
super(props);

View File

@ -1,11 +1,14 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
import marked from 'marked';
import {Button} from './Button';
import {FixedPage} from './FixedPage';
// A component for the site header
export class Header extends React.Component {
// export class Header extends React.Component {
export class Header extends Component {
constructor(props) {
super(props);
@ -49,10 +52,12 @@ export class Header extends React.Component {
buttons = [
<Button key='accountbutton1'
id='accountSettings'
classes='level-item'
action={() => this.lockUserOut()}
label='Account Settings' />,
<Button key='accountbutton2'
id='logoutLink'
classes='level-item'
action={() => this.logUserOut()}
label='Log Out' />
];
@ -60,6 +65,7 @@ export class Header extends React.Component {
buttons = [
<Button key='accountbutton3'
id='logoutLink'
classes='level-item'
action={() => this.unlockUser()}
label='Can&apos;t Log In' />
];
@ -67,34 +73,41 @@ export class Header extends React.Component {
buttons = [
<Button key='accountbutton4'
id='loginLink'
classes='level-item'
action={() => this.logUserIn()}
label='Log In/Create Account' />
];
}
return <div className='button-group'>{buttons}</div>;
return buttons;
}
render() {
return (
<header className='header'>
<div className='hero'>
<nav className='level' id="headerPadder">
<div id="headerPadder">
<div className='level-left'>
<a className='level-item' href="/" id="siteLogo">
Lexiconga Dictionary Builder
</a>
</div>
<a href="/" id="siteLogo">Lexiconga Dictionary Builder</a>
<div className='level-right'>
<div className='button-group'>
<FixedPage id='aboutButton' buttonText='About Lexiconga'>
<div dangerouslySetInnerHTML={{__html: marked(this.aboutText)}} />
</FixedPage>
{this.showAccountButtons()}
</div>
<div className='level-item'>
<FixedPage id='aboutButton' buttonText='About Lexiconga'>
<div dangerouslySetInnerHTML={{__html: marked(this.aboutText)}} />
</FixedPage>
</div>
{this.showAccountButtons()}
</div>
</nav>
</div>
</header>
);
}

View File

@ -1,4 +1,6 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
import marked from 'marked';
import {WordForm} from './WordForm';
@ -8,7 +10,8 @@ const saveIcon = <i>&#128190;</i>;
const editIcon = <i>&#128393;</i>;
// A component to show dictionary information in a tabbed interface.
export class InfoDisplay extends React.Component {
// export class InfoDisplay extends React.Component {
export class InfoDisplay extends Component {
constructor(props) {
super(props);

View File

@ -1,8 +1,19 @@
import React from 'react';
// import React from 'react';
import Inferno, {linkEvent} from 'inferno';
import Component from 'inferno-component';
import {Button} from './Button';
export class Input extends React.Component {
function handleOnChange(instance, event) {
console.log('changing');
instance.setState({
isValid: !(instance.props.doValidate && event.currentTarget.value === ''),
value: event.currentTarget.value
});
}
// export class Input extends React.Component {
export class Input extends Component {
constructor(props) {
super(props);
@ -12,27 +23,29 @@ export class Input extends React.Component {
// doValidate: props.doValidate || true
// };
this.generatedId = 'input' + props.idManager.nextStr;
this.state = {
value: props.value || '',
isDisabled: props.isDisabled || false
};
// Bind listeners
this.handleOnChange = this.handleOnChange.bind(this);
// this.handleOnChange = this.handleOnChange.bind(this);
this.handleOnKeyDown = this.handleOnKeyDown.bind(this);
}
// Whenever the input changes we update the value state of this component
handleOnChange(event) {
this.setState({
isValid: !(this.props.doValidate && event.currentTarget.value === ''),
handleOnChange(instance, event) {
instance.setState({
isValid: !(instance.props.doValidate && event.currentTarget.value === ''),
value: event.currentTarget.value
});
}
handleOnKeyDown(event) {
if (this.props.onKeyDown) {
this.props.onKeyDown(event);
handleOnKeyDown(instance, event) {
if (instance.props.onKeyDown) {
instance.props.onKeyDown(event);
}
}
@ -46,9 +59,9 @@ export class Input extends React.Component {
);
} else {
return (
<Button classes='inline-button'
action={this.props.helperLink.action}
label={this.props.helperLink.label || '?'} />
<Button classes='inline-button'
action={this.props.helperLink.action}
label={this.props.helperLink.label || '?'} />
);
}
}
@ -66,18 +79,38 @@ export class Input extends React.Component {
render() {
return (
<label>
<span>
{this.props.name}
{this.showHelperLink()}
</span>
<input type="text" onChange={this.handleOnChange} onKeyDown={this.handleOnKeyDown} disabled={(this.state.isDisabled) ? 'disabled' : null} value={this.state.value} />
</label>
<div>
<div className='level is-marginless'>
<div className='level-item'>
<label className='label' for={this.generatedId}>
{this.props.name}
</label>
</div>
<div className='level-item'>
{this.showHelperLink()}
</div>
</div>
<p className='control'>
<input
className='input'
id={this.generatedId}
type="text"
onInput={linkEvent(this, this.handleOnChange)}
onKeyDown={linkEvent(this, this.handleOnKeyDown)}
disabled={(this.state.isDisabled) ? 'disabled' : null} />
</p>
</div>
);
}
}
Input.defaultProps = {
name: '',
doValidate: true
doValidate: false
};

View File

@ -1,4 +1,5 @@
import React from 'react';
// import React from 'react';
import Inferno, {linkEvent} from 'inferno';
import {Input} from './Input';
import {getInputSelection, setSelectionRange} from '../js/helpers';
@ -8,26 +9,50 @@ import {FixedPage} from './FixedPage';
export class TextArea extends Input {
constructor(props) {
super(props);
this.mainTextarea = null;
this.maximizedTextarea = null;
}
handleMaximizedTextboxClose (instance, event) {
instance.mainTextarea.value = event.currentTarget.value;
}
handleMaximizedTextboxOpen (instance, event) {
instance.maximizedTextarea.value = event.currentTarget.value;
}
// Use a FixedPage for TextArea's fullscreen mode.
render() {
return (
<label>
<span>
{this.props.name}
<div className='control'>
<div className='level'>
<div className='level-item'>
<label className='label' for={this.generatedId}>
{this.props.name}
</label>
</div>
<FixedPage id={this.props.id + '_textbox'} contentClass='no-scroll' buttonClasses='maximize-button' buttonText='Maximize'>
<label><span>{this.props.name}</span></label>
<div className='level-item'>
<FixedPage id={this.generatedId + '_textbox'} contentClass='no-scroll' buttonClasses='maximize-button' buttonText='Maximize'>
<label><span>{this.props.name}</span></label>
<textarea id={this.props.id} className='fullscreen-textbox' onChange={this.handleOnChange} onKeyDown={this.handleOnKeyDown} value={this.state.value} />
</FixedPage>
<textarea id={this.generatedId} className='fullscreen-textbox'
onChange={linkEvent(this, this.handleMaximizedTextboxClose)}
onKeyDown={linkEvent(this, this.handleOnKeyDown)}
ref={(textarea) => {this.maximizedTextarea = textarea}} />
</FixedPage>
</div>
</span>
</div>
<textarea id={this.props.id} onChange={this.handleOnChange} onKeyDown={this.handleOnKeyDown} disabled={(this.state.isDisabled) ? 'disabled' : null} value={this.state.value} />
<textarea className='textarea' id={this.generatedId}
onInput={linkEvent(this, this.handleOnChange)}
onChange={linkEvent(this, this.handleMaximizedTextboxOpen)}
onKeyDown={linkEvent(this, this.handleOnChange)} disabled={(this.state.isDisabled) ? 'disabled' : null}
ref={(textarea) => {this.mainTextarea = textarea}} />
</label>
</div>
);
}
}

View File

@ -1,4 +1,6 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
import marked from 'marked';
import {WordForm} from './WordForm';
@ -7,7 +9,8 @@ import {Button} from './Button';
const saveIcon = <i>&#128190;</i>;
const editIcon = <i>&#128393;</i>;
export class Word extends React.Component {
// export class Word extends React.Component {
export class Word extends Component {
constructor(props) {
super(props);
@ -34,7 +37,7 @@ export class Word extends React.Component {
showName() {
return (
<div className='title is-4 name'>
<div className='title is-4 name is-inline'>
{this.props.name}
</div>
);
@ -43,7 +46,7 @@ export class Word extends React.Component {
showPronunciation() {
if (this.props.pronunciation !== '') {
return (
<div className='pronunciation'>
<div className='pronunciation is-inline'>
{this.props.pronunciation}
</div>
);
@ -53,7 +56,7 @@ export class Word extends React.Component {
showPartOfSpeech() {
if (this.props.partOfSpeech !== '') {
return (
<div className='part-of-speech'>
<div className='part-of-speech is-inline'>
{this.props.partOfSpeech}
</div>
);
@ -89,18 +92,23 @@ export class Word extends React.Component {
);
} else {
return (
<div>
{this.showName()}
<div className='content'>
<div className='hero'>
{this.showName()}
{this.showPronunciation()}
{this.showPronunciation()}
{this.showPartOfSpeech()}
{this.showPartOfSpeech()}
<br />
</div>
{this.showSimpleDefinition()}
<div className='section'>
{this.showLongDefinition()}
{this.showSimpleDefinition()}
{this.showLongDefinition()}
</div>
</div>
);
}
@ -148,7 +156,7 @@ export class Word extends React.Component {
render() {
return (
<div id={'entry' + this.props.wordId} className='word'>
<div id={'entry' + this.props.wordId} className='box word'>
{this.showWordOrEdit()}

View File

@ -1,4 +1,6 @@
import React from 'react';
// import React from 'react';
import Inferno from 'inferno';
import Component from 'inferno-component';
import {keyCodeFor} from '../js/helpers'
@ -7,7 +9,8 @@ import {Dropdown} from './Dropdown';
import {TextArea} from './TextArea';
import {Button} from './Button';
export class WordForm extends React.Component {
// export class WordForm extends React.Component {
export class WordForm extends Component {
constructor(props) {
super(props);
@ -91,14 +94,15 @@ export class WordForm extends React.Component {
let partOfSpeechDefaultValue = (this.props.wordValues) ? this.props.wordValues.partOfSpeech : ' ';
let simpleDefinitionDefaultValue = (this.props.wordValues) ? this.props.wordValues.simpleDefinition : '';
let longDefinitionDefaultValue = (this.props.wordValues) ? this.props.wordValues.longDefinition : '';
return (
<form>
<Input name='Word'
<div className='form'>
<Input name='Word' idManager={this.props.idManager}
value={nameDefaultValue}
onKeyDown={(event) => this.submitWordOnCtrlEnter(event)}
ref={(inputComponent) => this.wordField = inputComponent} />
<Input name='Pronunciation'
<Input name='Pronunciation' idManager={this.props.idManager}
helperLink={{
url: "http://r12a.github.io/pickers/ipa/",
label: "IPA Characters",
@ -108,17 +112,17 @@ export class WordForm extends React.Component {
onKeyDown={(event) => this.submitWordOnCtrlEnter(event)}
ref={(inputComponent) => this.pronunciationField = inputComponent} />
<Dropdown name='Part of Speech'
<Dropdown name='Part of Speech' idManager={this.props.idManager}
optionsList={this.props.partsOfSpeech}
value={partOfSpeechDefaultValue}
ref={(inputComponent) => this.partOfSpeechField = inputComponent} />
<Input name={<div style={{display: 'inline'}}>Definition/<wbr /><b className="wbr"></b>Equivalent Word(s)</div>}
<Input name={<div style={{display: 'inline'}}>Definition/<wbr /><b className="wbr"></b>Equivalent Word(s)</div>} idManager={this.props.idManager}
value={simpleDefinitionDefaultValue}
onKeyDown={(event) => this.submitWordOnCtrlEnter(event)}
ref={(inputComponent) => this.simpleDefinitionField = inputComponent} />
<TextArea id='newWordForm'
<TextArea id='newWordForm' idManager={this.props.idManager}
name={<div style={{display: 'inline'}}>Explanation/<wbr /><b className="wbr"></b>Long Definition</div>}
value={longDefinitionDefaultValue}
onKeyDown={(event) => this.submitWordOnCtrlEnter(event)}
@ -129,7 +133,7 @@ export class WordForm extends React.Component {
<Button classes={(this.props.updateWord) ? 'edit-button' : 'add-button'} action={() => this.handleSubmit()} label={this.props.submitLabel} />
<div id="updateConflict">{this.state.updateConflictMessage}</div>
</form>
</div>
);
}
}

View File

@ -3,8 +3,10 @@ import './index.html';
import './sass/main.scss';
// Import React for the React.Component class and ReactDOM for rendering.
import React from 'react';
import ReactDOM from 'react-dom';
// import React from 'react';
// import ReactDOM from 'react-dom';
import Inferno from 'inferno';
import Component from 'inferno-component';
// Import the necessary components.
import {Header} from './components/Header';
@ -17,6 +19,7 @@ import {Dictionary} from './components/Dictionary';
// Import the helper functions needed for this file.
import {dynamicSort} from './js/helpers';
import {IDManager} from './js/IDManager';
// Declare the values of the default empty dictionary.
const defaultDictionaryName = 'New'
@ -27,13 +30,16 @@ const defaultDictionaryName = 'New'
;
// Create the Lexiconga component just for rendering the whole site.
class Lexiconga extends React.Component {
// class Lexiconga extends React.Component {
class Lexiconga extends Component {
constructor(props) {
super(props);
// This could probably be a global constant instead.
this.showConsoleMessages = this.props.showConsoleMessages || false;
this.idManager = new IDManager();
// Put the dictionary details, settings, and words into the state so modifications will affect display.
this.state = {
scroll: {
@ -246,47 +252,69 @@ class Lexiconga extends React.Component {
return (
<div>
<Header />
<section className='section columns'>
<div className='column is-one-third'>
<div className='floating-form'>
<WordForm
partsOfSpeech={this.state.settings.partsOfSpeech}
addWord={(wordObject) => this.addWord(wordObject)}
submitLabel='Add Word' />
<section className='section'>
<div className='columns'>
<div className='column is-one-quarter'>
<div className='box'>
<WordForm
idManager={this.idManager}
partsOfSpeech={this.state.settings.partsOfSpeech}
addWord={(wordObject) => this.addWord(wordObject)}
submitLabel='Add Word' />
</div>
</div>
</div>
<div className='column is-half'>
<Button
action={() => this.saveLocalDictionary()}
label='Save Dictionary' />
<div className='column is-half'>
<Button
action={() => this.loadLocalDictionary()}
label='Load Dictionary' />
<div className='hero'>
<div className='container is-fluid'>
<EditDictionaryForm
details={this.state.details}
settings={this.state.settings}
saveChanges={(changesObject) => this.saveChanges(changesObject)} />
<Button
action={() => this.saveLocalDictionary()}
label='Save Dictionary' />
<h1 className="title is-1 dictionary-name">
{this.state.details.name} {this.state.details.listTypeName}
</h1>
<Button
action={() => this.loadLocalDictionary()}
label='Load Dictionary' />
<InfoDisplay
details={this.state.details}
numberOfWords={this.state.words.length}
isComplete={this.state.settings.isComplete} />
<EditDictionaryForm
details={this.state.details}
settings={this.state.settings}
saveChanges={(changesObject) => this.saveChanges(changesObject)} />
<Dictionary
details={this.state.details}
words={this.state.words}
settings={this.state.settings}
updateWord={(wordId, wordObject) => this.updateWord(wordId, wordObject)} />
</div>
<h1 className="title is-1 dictionary-name">
{this.state.details.name} {this.state.details.listTypeName}
</h1>
<div className='column ad-column'>
<InfoDisplay
details={this.state.details}
numberOfWords={this.state.words.length}
isComplete={this.state.settings.isComplete} />
</div>
</div>
<div className='section'>
<Dictionary
details={this.state.details}
words={this.state.words}
settings={this.state.settings}
updateWord={(wordId, wordObject) => this.updateWord(wordId, wordObject)} />
</div>
</div>
<div className='column is-one-quarter ad-column'>
<div className='box'>
Advertisements or something.
</div>
</div>
</div>
</section>
@ -298,4 +326,5 @@ class Lexiconga extends React.Component {
}
// Put the app on the screen.
ReactDOM.render(<Lexiconga showConsoleMessages={true} />, document.getElementById('site'));
// ReactDOM.render(<Lexiconga showConsoleMessages={true} />, document.getElementById('site'));
Inferno.render(<Lexiconga showConsoleMessages={true} />, document.getElementById('site'));

13
src/js/IDManager.js Normal file
View File

@ -0,0 +1,13 @@
export class IDManager {
constructor () {
this.nextId = 0;
}
get next () {
return this.nextId++;
}
get nextStr () {
return this.next.toString();
}
}

View File

@ -1,6 +1,6 @@
@import 'variables';
@import '../../node_modules/bulma/bulma';
@import 'styles';
@import 'lexiconga';
@import 'mobile';
// @import 'styles';
// @import 'lexiconga';
// @import 'mobile';
@import '../../node_modules/react-select/dist/react-select';

View File

@ -30,7 +30,9 @@ const APP_DIR = path.resolve(__dirname, 'src');
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
// presets: ['react', 'es2015']
presets: ['es2015'],
plugins: ['inferno']
}
}
]