Got new word form React component working and rendering mostly correctly in ES2016!
This commit is contained in:
parent
d914d8f133
commit
15f26883e6
|
@ -1,3 +1,3 @@
|
|||
php/google/
|
||||
ipa_character_picker/
|
||||
node_modules/
|
||||
public/
|
||||
|
|
25
package.json
25
package.json
|
@ -5,7 +5,10 @@
|
|||
"description": "A tool to build simple dictionaries using JSON.",
|
||||
"main": "src/app.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"start": "node start-server.js",
|
||||
"pack": "node ./node_modules/webpack/bin/webpack.js -d --progress --display-error-details",
|
||||
"watch": "node ./node_modules/webpack/bin/webpack.js -d --progress --watch",
|
||||
"build": "node ./node_modules/webpack/bin/webpack.js -p --progress"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -18,7 +21,25 @@
|
|||
},
|
||||
"homepage": "https://github.com/Alamantus/Lexiconga#readme",
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.13.0",
|
||||
"json-query": "^2.2.0",
|
||||
"marked": "^0.3.6",
|
||||
"papaparse": "^4.1.2"
|
||||
"papaparse": "^4.1.2",
|
||||
"react": "^15.3.2",
|
||||
"react-dom": "^15.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-preset-es2015": "^6.14.0",
|
||||
"babel-preset-react": "^6.11.1",
|
||||
"css-loader": "^0.25.0",
|
||||
"express": "^4.14.0",
|
||||
"file-loader": "^0.9.0",
|
||||
"html-minify-loader": "^1.1.0",
|
||||
"node-sass": "^3.10.0",
|
||||
"sass-loader": "^4.0.2",
|
||||
"style-loader": "^0.13.1",
|
||||
"webpack": "^1.13.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react';
|
||||
|
||||
import {Input} from './Input';
|
||||
import {TextArea} from './TextArea';
|
||||
|
||||
import {WordForm} from './WordForm';
|
||||
|
||||
export class EditWordForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<WordForm action='edit'>
|
||||
<Input name='Word' />
|
||||
<Input name='Pronunciation' helperLink={{url: "http://r12a.github.io/pickers/ipa/", label: 'IPA Characters', hover:"IPA Character Picker located at http://r12a.github.io/pickers/ipa/"}} />
|
||||
<Input name='Part of Speech' />
|
||||
<Input name='Definition/<wbr><b class=wbr></b>Equivalent Word(s)' />
|
||||
<TextArea name='Explanation/<wbr><b class=wbr></b>Long Definition' />
|
||||
</WordForm>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import React from 'react';
|
||||
|
||||
export class Input extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// this.defaultProps = {
|
||||
// name: props.name || 'Field',
|
||||
// helperLink: props.helperLink || {url: '#', label: '', hover: ''},
|
||||
// doValidate: props.doValidate || true
|
||||
// };
|
||||
|
||||
this.state = {
|
||||
value: props.value || ''
|
||||
};
|
||||
|
||||
// Bind listeners
|
||||
this.handleOnChange = this.handleOnChange.bind(this);
|
||||
}
|
||||
|
||||
handleOnChange(event) {
|
||||
this.setValue(event);
|
||||
}
|
||||
|
||||
// Whenever the input changes we update the value state of this component
|
||||
setValue(event) {
|
||||
this.setState({
|
||||
isValid: !(this.props.doValidate && event.currentTarget.value === ''),
|
||||
value: event.currentTarget.value
|
||||
});
|
||||
}
|
||||
|
||||
showHelperLink() {
|
||||
if (this.props.helperLink) {
|
||||
return (
|
||||
<a className='clickable inline-button' href={this.props.helperLink.url} target='_blank' title={this.props.helperLink.hover}>
|
||||
{this.props.helperLink.label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<label>
|
||||
<span>
|
||||
{this.props.name}
|
||||
{this.showHelperLink()}
|
||||
</span>
|
||||
<input type="text" onChange={this.handleOnChange} value={this.state.value} />
|
||||
</label>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Input.defaultProps = {
|
||||
name: 'Field',
|
||||
doValidate: true
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
import React from 'react';
|
||||
|
||||
import {Input} from './Input';
|
||||
import {TextArea} from './TextArea';
|
||||
|
||||
import {WordForm} from './WordForm';
|
||||
|
||||
export class NewWordForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<WordForm action='new'>
|
||||
<Input name='Word' />
|
||||
<Input name='Pronunciation'
|
||||
helperLink={{
|
||||
url: "http://r12a.github.io/pickers/ipa/",
|
||||
label: "IPA Characters",
|
||||
hover: "IPA Character Picker located at http://r12a.github.io/pickers/ipa/"
|
||||
}} />
|
||||
<Input name='Part of Speech' />
|
||||
<Input name={<div style={{display: 'inline'}}>Definition/<wbr /><b className="wbr"></b>Equivalent Word(s)</div>} />
|
||||
<TextArea id='newWordForm'
|
||||
name={<div style={{display: 'inline'}}>Explanation/<wbr /><b className="wbr"></b>Long Definition</div>} />
|
||||
</WordForm>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import {Input} from './Input';
|
||||
|
||||
import {getInputSelection, setSelectionRange} from '../js/helpers';
|
||||
|
||||
export class TextArea extends Input {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Bind listeners
|
||||
this.handleMaximizeClick = this.handleMaximizeClick.bind(this);
|
||||
}
|
||||
|
||||
handleMaximizeClick(event) {
|
||||
this.showFullScreenTextbox();
|
||||
}
|
||||
|
||||
showFullScreenTextbox() {
|
||||
var sourceTextboxElement = document.getElementById(this.props.id);
|
||||
var targetTextboxElement = document.getElementById("fullScreenTextbox");
|
||||
document.getElementById("fullScreenTextboxLabel").innerHTML = this.props.name;
|
||||
var selection = getInputSelection(sourceTextboxElement);
|
||||
|
||||
document.getElementById("expandedTextboxId").innerHTML = this.props.id;
|
||||
targetTextboxElement.value = sourceTextboxElement.value;
|
||||
document.getElementById("fullScreenTextboxScreen").style.display = "block";
|
||||
|
||||
targetTextboxElement.focus();
|
||||
setSelectionRange(targetTextboxElement, selection.start, selection.end);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<label>
|
||||
<span>
|
||||
{this.props.name}
|
||||
<span className="clickable inline-button" onClick={this.handleMaximizeClick}>Maximize</span>
|
||||
</span>
|
||||
<textarea id={this.props.id} onChange={this.handleOnChange} value={this.state.value} />
|
||||
</label>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import React from 'react';
|
||||
|
||||
import {Input} from './Input';
|
||||
import {TextArea} from './TextArea';
|
||||
import {keyCodeFor} from '../js/helpers';
|
||||
|
||||
export class WordForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
errorMessage: '',
|
||||
updateConflictMessage: ''
|
||||
};
|
||||
|
||||
this.isNewWord = (this.props.action == 'new');
|
||||
}
|
||||
|
||||
submitWordOnCtrlEnter() {
|
||||
var keyCode = (event.which ? event.which : event.keyCode);
|
||||
|
||||
//Windows and Linux Chrome accept ctrl+enter as keyCode 10.
|
||||
if (keyCode === keyCodeFor('ctrlEnter') || (keyCode == keyCodeFor('enter') && event.ctrlKey)) {
|
||||
event.preventDefault();
|
||||
|
||||
this.handleSubmit();
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
if (this.validate()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
validate() {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
confirmArea() {
|
||||
if (this.isNewWord) {
|
||||
return <button type="button" onClick={this.handleSubmit}>Add Word</button>;
|
||||
}
|
||||
return (
|
||||
<div id="editWordButtonArea">
|
||||
<button type="button" onClick={this.handleSubmit}>Edit Word</button>
|
||||
<button type="button" onClick={this.clearForm}>Cancel</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
clearForm() {
|
||||
this.props.children.forEach((field) => {
|
||||
field.state.value = '';
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<form>
|
||||
{this.props.children}
|
||||
<span id="errorMessage">{this.state.errorMessage}</span>
|
||||
{this.confirmArea}
|
||||
<div id="updateConflict">{this.state.updateConflictMessage}</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -9,11 +9,9 @@
|
|||
<meta property="og:title" content="Lexiconga Dictionary Builder" />
|
||||
<meta property="og:description" content="Build lexicons for contructed languages or anything that you can think of!" />
|
||||
<meta property="og:image" content="http://lexicon.ga/images/logo.svg" />
|
||||
|
||||
<link href="/css/styles.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<site></site>
|
||||
<div id="site"></div>
|
||||
<script src="dictionaryBuilder.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
import './index.html';
|
||||
import './sass/styles.scss';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import {NewWordForm} from './components/NewWordForm';
|
||||
|
||||
class Lexiconga extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<NewWordForm />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Lexiconga />, document.getElementById('site'));
|
|
@ -1,21 +1,21 @@
|
|||
function ready(fn) {
|
||||
if (document.readyState != 'loading'){
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
}
|
||||
// function ready(fn) {
|
||||
// if (document.readyState != 'loading'){
|
||||
// fn();
|
||||
// } else {
|
||||
// document.addEventListener('DOMContentLoaded', fn);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Set Marked.js settings
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: true,
|
||||
sanitize: true
|
||||
});
|
||||
// marked.setOptions({
|
||||
// gfm: true,
|
||||
// tables: true,
|
||||
// breaks: true,
|
||||
// sanitize: true
|
||||
// });
|
||||
|
||||
// Get Keycode based on key name
|
||||
function keyCodeFor(keyName) {
|
||||
export function keyCodeFor(keyName) {
|
||||
if (keyName == "backspace") return 8;
|
||||
else if (keyName == "tab") return 9;
|
||||
else if (keyName == "ctrlEnter") return 10;
|
||||
|
@ -119,7 +119,7 @@ function keyCodeFor(keyName) {
|
|||
else return false;
|
||||
}
|
||||
|
||||
function getInputSelection(el) {
|
||||
export function getInputSelection(el) {
|
||||
// Retrieved from http://stackoverflow.com/a/4207763
|
||||
var start = 0, end = 0, normalizedValue, range,
|
||||
textInputRange, len, endRange;
|
||||
|
@ -166,7 +166,7 @@ function getInputSelection(el) {
|
|||
};
|
||||
}
|
||||
|
||||
function setSelectionRange(input, selectionStart, selectionEnd) {
|
||||
export function setSelectionRange(input, selectionStart, selectionEnd) {
|
||||
// Retrieved from http://stackoverflow.com/a/17858641/3508346
|
||||
if (input.setSelectionRange) {
|
||||
input.focus();
|
||||
|
@ -181,7 +181,7 @@ function setSelectionRange(input, selectionStart, selectionEnd) {
|
|||
}
|
||||
}
|
||||
|
||||
function SaveScroll() {
|
||||
export function SaveScroll() {
|
||||
var doc = document.documentElement;
|
||||
var left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
|
||||
var top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
|
||||
|
@ -190,36 +190,36 @@ function SaveScroll() {
|
|||
savedScroll.y = top;
|
||||
}
|
||||
|
||||
function htmlEntities(string) {
|
||||
export function htmlEntities(string) {
|
||||
return String(string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\\/g, '\').replace(/\n/g, '<br>');
|
||||
}
|
||||
|
||||
function htmlEntitiesParse(string) {
|
||||
export function htmlEntitiesParse(string) {
|
||||
return String(string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/\/g, '\\').replace(/<br>/g, '\n');
|
||||
}
|
||||
|
||||
function htmlEntitiesParseForMarkdown(string) {
|
||||
export function htmlEntitiesParseForMarkdown(string) {
|
||||
return String(string).replace(/"/g, '"').replace(/'/g, "'").replace(/\/g, '\\').replace(/<br>/g, '\n');
|
||||
}
|
||||
|
||||
function stripHtmlEntities(string) {
|
||||
export function stripHtmlEntities(string) {
|
||||
// This is for the export name.
|
||||
return String(string).replace(/&/g, '').replace(/</g, '').replace(/>/g, '').replace(/"/g, '').replace(/'/g, "").replace(/\/g, '').replace(/<br>/g, '');
|
||||
}
|
||||
|
||||
function htmlEntitiesParseForSearchEntry(string) {
|
||||
export function htmlEntitiesParseForSearchEntry(string) {
|
||||
return String(string).replace(/"/g, '%%%%').replace(/'/g, "````");
|
||||
}
|
||||
|
||||
function htmlEntitiesParseForSearch(string) {
|
||||
export function htmlEntitiesParseForSearch(string) {
|
||||
return String(string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '%%%%').replace(/'/g, "````");
|
||||
}
|
||||
|
||||
function regexParseForSearch(string) {
|
||||
export function regexParseForSearch(string) {
|
||||
return String(string).replace(/([\[\\\^\$\.\|\?\*\+\(\)\{\}\]])/g, "\\$1");
|
||||
}
|
||||
|
||||
function dynamicSort(propertiesArray) {
|
||||
export function dynamicSort(propertiesArray) {
|
||||
/* Retrieved from http://stackoverflow.com/a/30446887/3508346
|
||||
Usage: theArray.sort(dynamicSort(['propertyAscending', '-propertyDescending']));*/
|
||||
return function (a, b) {
|
||||
|
@ -240,7 +240,7 @@ function dynamicSort(propertiesArray) {
|
|||
};
|
||||
}
|
||||
|
||||
function download(filename, text) {
|
||||
export function download(filename, text) {
|
||||
/* Retrieved from http://stackoverflow.com/a/18197341/3508346
|
||||
Usage: download('test.txt', 'Hello world!');*/
|
||||
var element = document.createElement('a');
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
@import 'styles';
|
||||
// @import 'lexiconga';
|
||||
// @import 'mobile';
|
|
@ -0,0 +1,16 @@
|
|||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
app.use(express.static('./public/'));
|
||||
|
||||
app.listen(3013, function () {
|
||||
setTerminalTitle('localhost:3013');
|
||||
console.log('Example app listening on port 3013!');
|
||||
});
|
||||
|
||||
function setTerminalTitle(title)
|
||||
{
|
||||
process.stdout.write(
|
||||
String.fromCharCode(27) + "]0;" + title + String.fromCharCode(7)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
const BUILD_DIR = path.resolve(__dirname, 'public');
|
||||
const APP_DIR = path.resolve(__dirname, 'src');
|
||||
|
||||
module.exports = {
|
||||
entry: APP_DIR + '/index.jsx',
|
||||
output: {
|
||||
path: BUILD_DIR,
|
||||
filename: 'dictionaryBuilder.js'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.html?$/,
|
||||
exclude: /node_modules/,
|
||||
loaders: [
|
||||
'file?name=[name].html',
|
||||
'html-minify'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
exclude: /node_modules/,
|
||||
loaders: ['style', 'css', 'sass']
|
||||
},
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['react', 'es2015']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['', '.js', '.jsx'],
|
||||
},
|
||||
// plugins: [
|
||||
// When you're ready to publish, check this article out.
|
||||
// http://dev.topheman.com/make-your-react-production-minified-version-with-webpack/
|
||||
// new webpack.optimize.UglifyJsPlugin({
|
||||
// compress: {
|
||||
// warnings: false
|
||||
// },
|
||||
// output: {
|
||||
// comments: false
|
||||
// }
|
||||
// })
|
||||
// ],
|
||||
sassLoader: {
|
||||
file: './src/sass/styles.scss',
|
||||
// includePaths: ['./node_modules/bootstrap-sass/assets/'],
|
||||
outFile: './public/styles.css',
|
||||
outputStyle: 'compressed'
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue