Structured main content of site with Bulma

This commit is contained in:
Robbie Antenesse 2017-04-04 14:39:35 -06:00
parent a3437771f5
commit 067489225a
13 changed files with 571 additions and 106 deletions

View File

@ -40,6 +40,7 @@
"dexie": "^1.5.1",
"inferno": "^1.6.0",
"inferno-component": "^1.6.0",
"inferno-devtools": "^1.6.0",
"marked": "^0.3.6",
"papaparse": "^4.2.0"
}

View File

@ -0,0 +1,45 @@
import Inferno from 'inferno';
import Component from 'inferno-component';
import {LeftColumn} from './structure/LeftColumn';
import {RightColumn} from './structure/RightColumn';
import {WordForm} from './management/WordForm';
import {DictionaryDetails} from './display/DictionaryDetails';
export class Lexiconga extends Component {
constructor (props) {
super(props);
}
render () {
return (
<section className='section'>
<div className='container'>
<div className='columns'>
<LeftColumn>
<WordForm
partsOfSpeech={['Noun','Adjective','Verb']}
/>
</LeftColumn>
<RightColumn>
<DictionaryDetails
description='Test Description'
details={{
custom: [
{
name: 'Test Tab'
}
]
}}
/>
</RightColumn>
</div>
</div>
</section>
);
}
}

View File

@ -0,0 +1,204 @@
import Inferno from 'inferno';
import Component from 'inferno-component';
import marked from 'marked';
import {SearchBox} from '../management/SearchBox';
const DISPLAY = {
NONE: false
, DESCRIPTION: 1
, DETAILS: 2
, STATS: 3
, SEARCH: 4
}
export class DictionaryDetails extends Component {
constructor (props) {
super(props);
this.state = {
currentDisplay: DISPLAY.NONE
}
this._descriptionHTML = marked(props.description);
}
componentWillReceiveProps (nextProps) {
const currentDescription = this.props.description
, nextDescription = nextProps.description;
if (currentDescription !== nextDescription) {
this._descriptionHTML = marked(nextProps.description);
}
}
toggleDisplay (display) {
display = (this.state.currentDisplay !== display) ? display : DISPLAY.NONE;
this.setState({
currentDisplay: display
});
}
displayInfo () {
if (this.state.currentDisplay !== DISPLAY.NONE) {
let displayJSX;
switch(this.state.currentDisplay) {
case DISPLAY.DESCRIPTION : {
// Not sure why, but the dangerouslySet div needs to be wrapped in another div or else
// the HTML content sticks around for some reason.
displayJSX = (
<div>
<div className="content"
dangerouslySetInnerHTML={{
__html: this._descriptionHTML
}} />
</div>
);
break;
}
case DISPLAY.DETAILS : {
let additionalMenu;
if (this.props.details.hasOwnProperty('custom')) {
let customTabsJSX = this.props.details.custom.map((tab) => {
return (
<li key={'customTab' + Date.now().toString()}>
<a>
{tab.name}
</a>
</li>
);
});
additionalMenu = (
<div>
<p className="menu-label">
Additional
</p>
<ul className="menu-list">
{customTabsJSX}
</ul>
</div>
);
}
const menu = (
<aside className="column is-one-quarter menu">
<p className="menu-label">
Linguistics
</p>
<ul className="menu-list">
<li><a className="is-active">Phonology</a></li>
<li><a>Grammar</a></li>
</ul>
{additionalMenu}
</aside>
);
let content = (
<div className='column'>
<p>
Details Content!
</p>
</div>
);
displayJSX = (
<div className='columns'>
{menu}
{content}
</div>
);
break;
}
case DISPLAY.STATS : {
displayJSX = (
<div className="content">
<p>Stats!</p>
</div>
);
break;
}
case DISPLAY.SEARCH : {
displayJSX = <SearchBox />;
break;
}
}
return (
<div className='box'>
{displayJSX}
</div>
)
}
}
render () {
return (
<div className='box'>
<div className='level'>
<div className='level-left'>
<div className='level-item'>
<h2 className='title is-2'>
Dictionary Name
</h2>
</div>
</div>
<div className='level-right'>
<div className='level-item'>
<div className='field is-grouped'>
<div className='control'>
<a className='button' onClick={this.toggleDisplay.bind(this, DISPLAY.SEARCH)}>
Search
</a>
</div>
<div className='control'>
<a className='button'>
Edit Dictionary
</a>
</div>
</div>
</div>
</div>
</div>
<div className='level'>
<div className='level-left'>
<div className='level-item'>
<div className='tabs is-toggle'>
<ul>
<li className={(this.state.currentDisplay === DISPLAY.DESCRIPTION) ? 'is-active' : null}>
<a onClick={this.toggleDisplay.bind(this, DISPLAY.DESCRIPTION)}>
<span>Description</span>
</a>
</li>
<li className={(this.state.currentDisplay === DISPLAY.DETAILS) ? 'is-active' : null}>
<a onClick={this.toggleDisplay.bind(this, DISPLAY.DETAILS)}>
<span>Details</span>
</a>
</li>
<li className={(this.state.currentDisplay === DISPLAY.STATS) ? 'is-active' : null}>
<a onClick={this.toggleDisplay.bind(this, DISPLAY.STATS)}>
<span>Stats</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
{this.displayInfo()}
</div>
);
}
}

View File

@ -0,0 +1,81 @@
import Inferno from 'inferno';
import Component from 'inferno-component';
export class SearchBox extends Component {
constructor (props) {
super(props);
}
showFilterOptions () {
if (this.props.hasOwnProperty('partsOfSpeech')
&& this.props.partsOfSpeech.length > 0) {
let filterOptionsJSX = this.props.partsOfSpeech.map((partOfSpeech) => {
return (
<label key={'filterPartOfSpeech' + Date.now()}
className='checkbox'>
<input type='checkbox' />
{partOfSpeech}
</label>
);
});
return (
<div className='field'>
<label>Filter</label>
<p className='control'>
{filterOptionsJSX}
</p>
</div>
);
}
}
render () {
return (
<div className='message'>
<div class="message-header">
<span>
Search
</span>
<button class="delete"></button>
</div>
<div class="message-body">
<div className='field'>
<div className='control'>
<input className='input' type='text' placeholder='Search Term' />
</div>
</div>
<div className='field is-horizontal'>
<div className='field-label'>
<label className='label'>
Search In
</label>
</div>
<div className='field-body'>
<div className='field is-narrow'>
<div className='control'>
<label className='radio'>
<input type='radio' name='member' value='Word' checked />
Word
</label>
<label className='radio'>
<input type='radio' name='member' />
Definition
</label>
<label className='radio'>
<input type='radio' name='member' />
Details
</label>
</div>
</div>
</div>
</div>
</div>
{this.showFilterOptions()}
</div>
);
}
}

View File

@ -0,0 +1,58 @@
import Inferno from 'inferno';
import Component from 'inferno-component';
export class WordForm extends Component {
constructor (props) {
super(props);
}
render () {
return (
<div className='box'>
<div className='field'>
<label className='label'>Word</label>
<p className='control'>
<input className='input' type='text' placeholder='Required' />
</p>
</div>
<div className='field'>
<label className='label'>Pronunciation</label>
<p className='control'>
<input className='input' type='text' placeholder='[prəˌnʌnsiˈeɪʃən]' />
</p>
</div>
<div className='field'>
<label className='label'>Part of Speech</label>
<p className='control'>
<span className='select'>
<select>
<option></option>
{this.props.partsOfSpeech.map((partOfSpeech) => {
return (
<option value={partOfSpeech}>{partOfSpeech}</option>
);
})}
</select>
</span>
</p>
</div>
<div className='field'>
<label className='label'>Definition</label>
<p className='control'>
<input className='input' type='text' placeholder='Text input' />
</p>
</div>
<div className='field'>
<label className='label'>Details</label>
<p className='control'>
<textarea className='textarea' placeholder='Textarea' />
</p>
</div>
</div>
);
}
}

View File

@ -0,0 +1,50 @@
import Inferno from 'inferno';
import Component from 'inferno-component';
export class Footer extends Component {
constructor (props) {
super(props);
}
render () {
return (
<footer className='footer'>
<div className='container'>
<div className='level'>
<div className='level-left'>
<div className='content'>
<p>
Lexiconga is only guaranteed to work with the most
up-to-date <a href='https://whatbrowser.org/' target='_blank'>HTML5 browsers</a>.
</p>
</div>
</div>
<div className='level-right'>
<span className='level-item'>
<a className='button'>
Issues
</a>
</span>
<span className='level-item'>
<a className='button'>
Updates
</a>
</span>
<span className='level-item'>
<a className='button'>
Terms
</a>
</span>
<span className='level-item'>
<a className='button'>
Privacy
</a>
</span>
</div>
</div>
</div>
</footer>
);
}
}

View File

@ -0,0 +1,39 @@
import Inferno from 'inferno';
import Component from 'inferno-component';
export class Header extends Component {
constructor (props) {
super(props);
}
render () {
return (
<nav className='nav'>
<div className='nav-left'>
<a href='/' className='nav-item'>
<img src='images/logo.svg' alt='Lexiconga' />
</a>
</div>
<span class="nav-toggle">
<span></span>
<span></span>
<span></span>
</span>
<div className='nav-right nav-menu'>
<span className='nav-item'>
<a className='button'>
Login
</a>
</span>
<span className='nav-item'>
<a className='button'>
About
</a>
</span>
</div>
</nav>
);
}
}

View File

@ -0,0 +1,16 @@
import Inferno from 'inferno';
import Component from 'inferno-component';
export class LeftColumn extends Component {
constructor (props) {
super(props);
}
render () {
return (
<div className='column is-one-third'>
{this.props.children}
</div>
);
}
}

View File

@ -0,0 +1,16 @@
import Inferno from 'inferno';
import Component from 'inferno-component';
export class RightColumn extends Component {
constructor (props) {
super(props);
}
render () {
return (
<div className='column is-two-thirds'>
{this.props.children}
</div>
);
}
}

View File

@ -3,6 +3,14 @@ import './sass/main.scss';
import Inferno from 'inferno';
import Component from 'inferno-component';
if (process.env.NODE_ENV !== 'production') {
require('inferno-devtools');
}
import {Header} from './components/structure/Header';
import {Lexiconga} from './components/Lexiconga';
import {Footer} from './components/structure/Footer';
class App extends Component {
constructor (props) {
super(props);
@ -11,95 +19,11 @@ class App extends Component {
render () {
return (
<div>
<nav className='nav'>
<div className='nav-left'>
<a href='/' className='nav-item'>
<img src='images/logo.svg' alt='Lexiconga' />
</a>
</div>
<div className='nav-right'>
<a href='/' className='nav-item'>
Login
</a>
<a href='/' className='nav-item'>
About
</a>
</div>
</nav>
<Header />
<section className='section'>
<div className='container'>
<div className='columns'>
<div className='column is-one-quarter'>
<div className='box'>
<div className='field'>
<label className='label'>Word</label>
<p className='control'>
<input className='input' type='text' placeholder='Text input' />
</p>
</div>
<div className='field'>
<label className='label'>Pronunciation</label>
<p className='control'>
<input className='input' type='text' placeholder='Text input' />
</p>
</div>
<div className='field'>
<label className='label'>Part of Speech</label>
<p className='control'>
<span className='select'>
<select>
<option>Select dropdown</option>
<option>With options</option>
</select>
</span>
</p>
</div>
<div className='field'>
<label className='label'>Definition / Equivalent Word(s)</label>
<p className='control'>
<input className='input' type='text' placeholder='Text input' />
</p>
</div>
<div className='field'>
<label className='label'>Explanation / Long Definition</label>
<p className='control'>
<textarea className='textarea' placeholder='Textarea' />
</p>
</div>
</div>
</div>
<div className='column is-half'>
<div className='card'>
<header className='card-header'>
<p className='card-header-title'>
Dictionary Name
</p>
<a className='card-header-icon button'>
Edit Dictionary
</a>
</header>
<div className='card-content'>
<div className='content'>
Hello
</div>
</div>
<footer className='card-footer'>
<a className='card-footer-item'>Save</a>
<a className='card-footer-item'>Edit</a>
<a className='card-footer-item'>Delete</a>
</footer>
</div>
</div>
<div className='column is-one-quarter'>
</div>
</div>
</div>
</section>
<Lexiconga />
<Footer />
</div>
);
}

View File

@ -0,0 +1,2 @@
$radius: 0;
$radius-large: 0;

View File

@ -1,10 +1,13 @@
// Set BUILDMODE to 'production' to reduce overhead.
const BUILDMODE = 'development';
const webpack = require('webpack');
const path = require('path');
const BUILD_DIR = path.resolve(__dirname, 'public');
const APP_DIR = path.resolve(__dirname, 'src');
module.exports = {
const webpackConfig = {
entry: APP_DIR + '/index.jsx'
, output: {
path: BUILD_DIR
@ -13,8 +16,8 @@ module.exports = {
, module: {
rules: [
{
test: /\.scss$/
, exclude: /node_modules/
test: (/\.scss$/)
, exclude: (/node_modules/)
, use: [
'style-loader'
, 'css-loader'
@ -29,14 +32,18 @@ module.exports = {
]
}
, {
test: /\.jsx?$/
, exclude: /node_modules/
test: (/\.jsx?$/)
, exclude: (/node_modules/)
, use: [
{
loader: 'babel-loader'
, options: {
presets: ['es2016']
, plugins: ['inferno']
presets: [
'es2016'
]
, plugins: [
'inferno'
]
}
}
]
@ -44,18 +51,31 @@ module.exports = {
]
}
, 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
extensions: [
'.js'
, '.jsx'
]
}
, plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify(BUILDMODE)
}
})
]*/
]
};
if (BUILDMODE === 'production') {
webpackConfig.plugins.push(
new webpack.optimize.UglifyJsPlugin({
mangle: true
, compress: {
warnings: false
}
})
);
webpackConfig.devtool = 'hidden-source-map';
}
module.exports = webpackConfig;

View File

@ -1389,6 +1389,15 @@ inferno-component@^1.6.0:
inferno-shared "^1.6.0"
inferno-vnode-flags "^1.6.0"
inferno-devtools@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/inferno-devtools/-/inferno-devtools-1.6.0.tgz#869953c1d5a85fa899f9ee8405e5d93d470629d0"
dependencies:
inferno "^1.6.0"
inferno-component "^1.6.0"
inferno-shared "^1.6.0"
inferno-vnode-flags "^1.6.0"
inferno-shared@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/inferno-shared/-/inferno-shared-1.6.0.tgz#8b85ec17c7140a2fe1320e556df05accc4af7145"