Parcourir la source

Added basic functionality for projects and registermaps.

Tomi Cvetic il y a 8 ans
Parent
commit
58e9146b7a

+ 10 - 3
package.json

@@ -5,22 +5,29 @@
   "dependencies": {
     "lodash": "4.17.4",
     "react": "15.4.2",
+    "react-addons-css-transition-group": "15.4.2",
     "react-dom": "15.4.2",
     "react-redux": "5.0.3",
     "react-router": "3.0.2",
     "react-router-redux": "4.0.8",
     "redux": "3.6.0",
     "redux-saga": "0.14.3",
-    "reselect": "2.5.4"
+    "reselect": "2.5.4",
+    "stylus-normalize": "3.0.3"
   },
   "devDependencies": {
-    "flow-bin": "0.41.0",
-    "react-scripts": "0.9.4"
+    "autoprefixer-stylus": "0.13.0",
+    "concurrently": "3.4.0",
+    "stylus": "0.54.5",
+    "react-scripts": "0.9.5"
   },
   "scripts": {
     "start": "react-scripts start",
+    "watch": "concurrently --names 'WEBP,STYL' --prefix name -c 'bgBlue.bold,bgMagenta.bold' 'npm run styles:watch' 'npm run start'",
     "build": "react-scripts build",
     "test": "react-scripts test --env=jsdom",
+    "styles": "stylus -u autoprefixer-stylus ./src/css/style.styl -o ./src/css/style.css",
+    "styles:watch": "stylus -u autoprefixer-stylus -w ./src/css/style.styl -o ./src/css/style.css",
     "eject": "react-scripts eject"
   }
 }

+ 0 - 22
src/App.js

@@ -1,22 +0,0 @@
-import React, { Component } from 'react'
-import logo from './logo.svg'
-import './App.css'
-
-class App extends Component {
-  render () {
-    return (
-      <div className='App'>
-        <div className='App-header'>
-          <img src={logo} className='App-logo' alt='logo' />
-          <h2>Welcome to React</h2>
-        </div>
-        <p className='App-intro'>
-          To get going, edit <code>src/App.js</code> and save to reload.
-        </p>
-        {this.props.children}
-      </div>
-    )
-  }
-}
-
-export default App

+ 32 - 0
src/Main.js

@@ -0,0 +1,32 @@
+import React from 'react'
+import { Link } from 'react-router'
+
+import './css/style.css'
+
+class Main extends React.Component {
+  render () {
+    const children = React.Children.map(this.props.children, child =>
+      React.cloneElement(child, this.props)
+    )
+    console.log('Found these child elements:', children)
+    return (
+      <div className='app-wrapper'>
+        <div className='app-header'>
+          <h2>AutoMate</h2>
+          <nav className='app-nav'>
+            <ul>
+              <li><Link to='/project'>Projects</Link></li>
+              <li><Link to='/registermap'>Registermaps</Link></li>
+            </ul>
+          </nav>
+        </div>
+        {children}
+        <div className='app-footer'>
+          <h2>AutoMate footer</h2>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default Main

+ 0 - 0
src/bitfield/components/CreateBitfield.js


+ 0 - 0
src/bitfield/components/EditBitfield.js


+ 0 - 0
src/bitfield/components/ShowBitfield.js


+ 0 - 61
src/bitfield/initialData.js

@@ -1,61 +0,0 @@
-export const bitfield = {
-  name: 'RF I2C Table',
-  description: 'RF I2C Table for the Jungfrau RF receiver.',
-  nrOfRegisters: 3,
-  bitsPerRegister: 8,
-  modes: [
-    'GPS only',
-    'Multi-GNSS'
-  ]
-  registers: {
-    0: 'iic_lna_ctrl_reg',
-    1: 'iic_mxr_ctrl_a_reg',
-    2: 'iic_mxr_ctrl_b_reg',
-    3: 'iic_mxr_seltf_reg'
-  }
-}
-
-export const settings = [
-{
-  name: 'lna_trim',
-  description: 'LNA trimming',
-  nrOfBits: 3,
-  bitMap: [3, 2, 1],
-  comment: '',
-  values: {
-    func: (value) => Math.pow(2, value)
-  }
-  resetValue: 0,
-  defaultValues: [
-    0,
-    0
-  ]
-}, {
-  name: 'lna_en',
-  description: 'LNA bias enable',
-  nrOfBits: 1,
-  bitMap: [4],
-  comment: '',
-  values: {
-    func: (value) => Math.pow(2, value)
-  }
-  resetValue: 0,
-  defaultValues: [
-    1,
-    1
-  ]
-}, {
-  name: 'lna_trim',
-  description: 'LNA trimming',
-  nrOfBits: 3,
-  bitMap: [3, 2, 1],
-  comment: '',
-  values: {
-    func: (value) => `${}uA`
-  }
-  resetValue: 0,
-  defaultValues: [
-    0,
-    0
-  ]
-}

+ 0 - 0
src/bitfield/state.js


+ 60 - 0
src/css/foundation.styl

@@ -0,0 +1,60 @@
+/** CSS Foundation */
+html
+  -ms-text-size-adjust 100%
+  -webkit-text-size-adjust 100%
+  
+body
+  margin 0
+  font 16px/1 sans-serif
+  -moz-osx-font-smoothing: grayscale
+  -webkit-font-smoothing: antialiased
+  
+h1, h2, h3, h4, p, blockquote, figure, ol, ul
+  margin 0
+  padding 0
+  
+main li
+  display block
+  
+h1, h2, h3, h4
+  font-size inherit
+  
+strong
+  font-weight bold
+
+button
+  color inherit
+  transition 0.3s
+
+a
+  transition 0.3s
+  cursor pointer
+
+::-moz-focus-inner
+  padding 0
+  border 0
+  
+:focus
+  outline 0
+  
+img
+  max-width 100%
+  height auto
+  border 0
+  
+input, label, select, button, textarea, fieldset
+  margin 0
+  border 0
+  padding 0
+  display inline-block
+  vertical-align middle
+  white-space normal
+  background none
+  line-height 1
+  
+button
+  overflow visible
+  -webkit-font-smoothing inherit
+  letter-spacing inherit
+  cursor pointer
+  

+ 187 - 0
src/css/style.css

@@ -0,0 +1,187 @@
+html {
+  -ms-text-size-adjust: 100%;
+  -webkit-text-size-adjust: 100%;
+}
+body {
+  margin: 0;
+  font: 16px/1 sans-serif;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-font-smoothing: antialiased;
+}
+h1,
+h2,
+h3,
+h4,
+p,
+blockquote,
+figure,
+ol,
+ul {
+  margin: 0;
+  padding: 0;
+}
+main li {
+  display: block;
+}
+h1,
+h2,
+h3,
+h4 {
+  font-size: inherit;
+}
+strong {
+  font-weight: bold;
+}
+button {
+  color: inherit;
+  -webkit-transition: 0.3s;
+  transition: 0.3s;
+}
+a {
+  -webkit-transition: 0.3s;
+  transition: 0.3s;
+  cursor: pointer;
+}
+::-moz-focus-inner {
+  padding: 0;
+  border: 0;
+}
+:focus {
+  outline: 0;
+}
+img {
+  max-width: 100%;
+  height: auto;
+  border: 0;
+}
+input,
+label,
+select,
+button,
+textarea,
+fieldset {
+  margin: 0;
+  border: 0;
+  padding: 0;
+  display: inline-block;
+  vertical-align: middle;
+  white-space: normal;
+  background: none;
+  line-height: 1;
+}
+button {
+  overflow: visible;
+  -webkit-font-smoothing: inherit;
+  letter-spacing: inherit;
+  cursor: pointer;
+}
+/** Main layout (Flexbox-Design) */
+.app-wrapper {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  min-height: 100vh;
+  -webkit-box-orient: vertical;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: column;
+          flex-direction: column;
+  -ms-flex-line-pack: center;
+      align-content: center;
+}
+.app-wrapper .app-header {
+  background: #000;
+  color: #fff;
+  -webkit-box-flex: 0;
+      -ms-flex: 0 0 60px;
+          flex: 0 0 60px;
+}
+.app-wrapper .app-header .app-nav ul {
+  padding: 0;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  list-style: none;
+}
+.app-wrapper .app-header .app-nav ul li {
+  -webkit-box-flex: 1;
+      -ms-flex: 1 1 100px;
+          flex: 1 1 100px;
+}
+.app-wrapper .app-main {
+  -webkit-box-flex: 1;
+      -ms-flex: 1 1 400px;
+          flex: 1 1 400px;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+}
+.app-wrapper .app-main .app-content {
+  -webkit-box-flex: 4;
+      -ms-flex: 4 1 560px;
+          flex: 4 1 560px;
+}
+.app-wrapper .app-main .content-flex {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+}
+.app-wrapper .app-main .content-flex .content-aside-left {
+  -webkit-box-flex: 1;
+      -ms-flex: 1 0 200px;
+          flex: 1 0 200px;
+}
+.app-wrapper .app-main .content-flex .content-main {
+  -webkit-box-flex: 4;
+      -ms-flex: 4 1 400px;
+          flex: 4 1 400px;
+}
+.app-wrapper .app-main .content-flex .content-aside-right {
+  -webkit-box-flex: 1;
+      -ms-flex: 1 0 200px;
+          flex: 1 0 200px;
+}
+.app-wrapper .app-main .app-aside-left {
+  -webkit-box-flex: 1;
+      -ms-flex: 1 0 200px;
+          flex: 1 0 200px;
+  -webkit-box-ordinal-group: 0;
+      -ms-flex-order: -1;
+          order: -1;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-orient: vertical;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: column;
+          flex-direction: column;
+}
+.app-wrapper .app-main .app-aside-left div,
+.app-wrapper .app-main .app-aside-left ul {
+  -webkit-box-flex: 1;
+      -ms-flex: 1 1;
+          flex: 1 1;
+}
+.app-wrapper .app-main .app-aside-right {
+  -webkit-box-flex: 1;
+      -ms-flex: 1 0 200px;
+          flex: 1 0 200px;
+  -webkit-box-ordinal-group: 1000;
+      -ms-flex-order: 999;
+          order: 999;
+  -webkit-box-orient: vertical;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: column;
+          flex-direction: column;
+}
+.app-wrapper .app-footer {
+  background: #000;
+  color: #fff;
+  -webkit-box-flex: 0;
+      -ms-flex: 0 0 40px;
+          flex: 0 0 40px;
+}
+/** Scrollable lists */
+.scrollable {
+  max-height: 100%;
+  overflow-y: scroll;
+}

+ 62 - 0
src/css/style.styl

@@ -0,0 +1,62 @@
+@import './foundation.styl'
+
+/** Main layout (Flexbox-Design) */
+.app-wrapper
+  display flex
+  min-height 100vh
+  flex-direction column
+  align-content center
+  
+  .app-header
+    background black
+    color white
+    flex 0 0 60px
+    
+    .app-nav
+      ul
+        padding 0
+        display flex
+        list-style none
+        li
+          flex 1 1 100px
+    
+  .app-main
+    flex 1 1 400px
+    display flex
+    
+    .app-content
+      flex 4 1 560px
+      
+    .content-flex
+      display flex
+      
+      .content-aside-left
+        flex 1 0 200px
+      .content-main
+        flex 4 1 400px
+      .content-aside-right
+        flex 1 0 200px
+      
+    .app-aside-left
+      flex 1 0 200px
+      order -1    
+      display flex
+      flex-direction column 
+      div, ul
+        flex 1 1  
+      
+    .app-aside-right
+      flex 1 0 200px
+      order 999
+      flex-direction column
+    
+  .app-footer
+    background black
+    color white
+    flex 0 0 40px
+    
+/** Scrollable lists */
+.scrollable
+  max-height 100%
+  overflow-y scroll
+  

+ 43 - 11
src/index.js

@@ -1,44 +1,76 @@
 import React from 'react'
 import ReactDOM from 'react-dom'
-import { browserHistory, Router, Route, IndexRedirect } from 'react-router'
-import { createStore, combineReducers, compose } from 'redux'
-import { Provider } from 'react-redux'
+import { browserHistory, Router, Route, IndexRoute } from 'react-router'
+import { createStore, combineReducers, bindActionCreators, compose } from 'redux'
+import { Provider, connect } from 'react-redux'
 import { syncHistoryWithStore, routerReducer } from 'react-router-redux'
 
-import App from './App'
+import Main from './Main'
 import project from './project'
+import registermap from './registermap'
 import './index.css'
 
-console.log(project)
-
 /**
  * Redux Section
  **/
 const rootReducer = combineReducers({
-  projects: project.reducer,
+  project: project.reducer,
+  registermap: registermap.reducer,
   routing: routerReducer
 })
+console.log('Root reducer:', rootReducer)
 
 const defaultState = {
-  projects: project.state
+  project: project.state,
+  registermap: registermap.state
 }
+console.log('Default state:', defaultState)
 
 const enhancers = compose(
     window.devToolsExtension ? window.devToolsExtension() : f => f
 )
+console.log('Enhancers:', enhancers)
 
 const store = createStore(rootReducer, defaultState, enhancers)
 const history = syncHistoryWithStore(browserHistory, store)
 
+const actionCreators = { project: project.actions, registermap: registermap.actions }
+
+function mapStateToProps (state) {
+  const propState = {}
+  Object.keys(state).forEach(key => {
+    propState[key] = state[key]
+  })
+  console.log('Mapping state to props:', state, propState)
+  return propState
+}
+
+function mapDispatchToProps (dispatch) {
+  const boundActionCreators = {}
+  Object.keys(actionCreators).forEach(key => {
+    boundActionCreators[`${key}Actions`] = bindActionCreators(actionCreators[key], dispatch)
+  })
+  console.log('Mapping dispatch to props:', dispatch, boundActionCreators)
+  return boundActionCreators
+}
+
+const App = connect(mapStateToProps, mapDispatchToProps)(Main)
+
 /**
  * React-Router Section
  **/
 const router = (
   <Provider store={store}>
     <Router history={history}>
-      <Route path='/' component={App}>
-        <IndexRedirect to='project' />
-        <Route path='project' component={project.components.EditProject} />
+      <Route component={App}>
+        <Route path='/'>
+          <IndexRoute component={project.components.Project} />
+          <Route path='/project/:projectId' component={project.components.Project} />
+          <Route path='/project' component={project.components.Project} />
+          <Route path='/registermap/:registermapId' component={registermap.components.Registermap} />
+          <Route path='/registermap' component={registermap.components.Registermap} />
+        </Route>
+        <Route path='/login' />
       </Route>
     </Router>
   </Provider>

+ 0 - 14
src/project/components/AddProjectForm.js

@@ -1,14 +0,0 @@
-import React from 'react'
-
-class AddProjectForm extends React.Component {
-  render () {
-    return (
-      <form>
-        <input type='text' ref='{input => this.projectName}' placeholder='Project Name' />
-        <text type='text' ref='{input => this.projectDesc}' placeholder='Project Description' />
-      </form>
-    )
-  }
-}
-
-export default AddProjectForm

+ 0 - 14
src/project/components/CreateProject.js

@@ -1,14 +0,0 @@
-import React from 'react'
-
-class CreateProjectForm extends React.Component {
-  render () {
-    return (
-      <form>
-        <input type='text' ref='{input => this.projectName}' placeholder='Project Name' />
-        <text type='text' ref='{input => this.projectDesc}' placeholder='Project Description' />
-      </form>
-    )
-  }
-}
-
-export default CreateProjectForm

+ 5 - 5
src/project/components/EditProject.js

@@ -3,11 +3,11 @@ import React from 'react'
 class EditProjectForm extends React.Component {
   render () {
     return (
-      <form>
-        <input type='text' ref='{input => this.projectName}' placeholder='Project ID' />
-        <input type='text' ref='{input => this.projectName}' placeholder='Project Name' />
-        <textarea ref='{input => this.projectDesc}' placeholder='Project Description' />
-        <input type='text' ref='{input => this.projectName}' placeholder='Project Manager' />
+      <form className='app-form'>
+        <input type='text' ref={input => { this.projectName = input }} placeholder='Project ID' />
+        <input type='text' ref={input => { this.projectName = input }} placeholder='Project Name' />
+        <input type='text' ref={input => { this.projectName = input }} placeholder='Project Description' />
+        <input type='text' ref={input => { this.projectName = input }} placeholder='Project Manager' />
         <button type='submit'>Save</button>
       </form>
     )

+ 33 - 0
src/project/components/Project.js

@@ -0,0 +1,33 @@
+import React from 'react'
+import EditProject from './EditProject'
+import ProjectList from './ProjectList'
+import { projectList } from '../initialData'
+
+class Project extends React.Component {
+  render () {
+    // Retrieve the projectId from the URL
+    const { projectId } = this.props.params
+    let project
+    if (typeof projectId !== 'undefined') {
+      // It the projectId is defined, check whether there's such a project
+      project = projectList.find(project => { return (project.id === projectId) })
+    } else if (projectList.length > 0) {
+      this.props.router.transitionTo(`/project/${projectList[0].id}`)
+    }
+
+    return (
+      <div className='app-main'>
+        <div className='app-content'>
+          <EditProject project={project} actions={this.props.projectActions} />
+        </div>
+        <div className='app-aside-left'>
+          <p>New Project Button</p>
+          <p>Project List</p>
+          <ProjectList projectList={projectList} />
+        </div>
+      </div>
+    )
+  }
+}
+
+export default Project

+ 23 - 0
src/project/components/ProjectList.js

@@ -0,0 +1,23 @@
+import React from 'react'
+import { Link } from 'react-router'
+
+class ProjectList extends React.Component {
+  render () {
+    const { projectList } = this.props
+    if (typeof projectList !== 'undefined' && projectList.length > 0) {
+      return (
+        <ul className='project-list scrollable'>
+          {projectList.map((project, key) =>
+            <li key={key}><Link to={`/project/${project.id}`}>{project.name}</Link></li>
+          )}
+        </ul>
+      )
+    } else {
+      return (
+        <p>No projects found.</p>
+      )
+    }
+  }
+}
+
+export default ProjectList

+ 3 - 3
src/project/components/index.js

@@ -1,6 +1,6 @@
-import AddProjectForm from './AddProjectForm'
+import Project from './Project'
+import ProjectList from './ProjectList'
 import EditProject from './EditProject'
-import CreateProject from './CreateProject'
 import ShowProject from './ShowProject'
 
-export default { AddProjectForm, EditProject, CreateProject, ShowProject }
+export default { Project, ProjectList, EditProject, ShowProject }

+ 2 - 2
src/project/index.js

@@ -7,7 +7,7 @@
  **/
 
 import * as constants from './constants'
-import { actions, reducer } from './state'
+import { actions, reducer, state } from './state'
 import components from './components'
 // import { createSelector } from 'reselect'
 // import { NAME } from './constants'
@@ -33,4 +33,4 @@ export const getCounts = createSelector(
     })
 ) */
 
-export default { actions, constants, components, filters, selectors, reducer }
+export default { actions, constants, components, filters, selectors, reducer, state }

+ 35 - 0
src/project/initialData.js

@@ -0,0 +1,35 @@
+export const projectList = [{
+  id: 'JU_CSD',
+  name: 'Jungfrau',
+  description: '9th generation GNSS receiver',
+  manager: 'Thomas Brauner',
+  versions: [
+    { name: 'MPW033C', date: '11.12.2015' },
+    { name: 'Production TO', date: '10.03.2017' }
+  ],
+  documents: [
+    { name: 'Specification', file: 'ju_spec.docx' },
+    { name: 'Pin List', file: 'ju_pin_list.xlsx' },
+    { name: 'Characterization PCB', file: 'ju_char_bord.brd' }
+  ],
+  pictures: [
+    { name: 'Jungfrau Logo', file: 'jungfrau.png' }
+  ]
+}, {
+  id: 'TI_CSD',
+  name: 'Titlis',
+  description: '7th generation GNSS receiver',
+  manager: 'Luca Plutino',
+  versions: [
+    { name: 'MPWA', date: '11.12.2013' },
+    { name: 'Production TO', date: '10.03.2015' }
+  ],
+  documents: [
+    { name: 'Specification', file: 'ti_spec.docx' },
+    { name: 'Pin List', file: 'ti_pin_list.xlsx' },
+    { name: 'Characterization PCB', file: 'ti_char_bord.brd' }
+  ],
+  pictures: [
+    { name: 'Titlis Logo', file: 'titlis.png' }
+  ]
+}]

+ 5 - 5
src/project/state.js

@@ -6,7 +6,7 @@
  * - actions
  **/
 
-import NAME from './constants'
+import { NAME } from './constants'
 // import { call, put, takeEvery } from 'redux-saga/effects'
 
 /** actionTypes define what actions are handeled by the reducer. */
@@ -17,14 +17,14 @@ export const actionTypes = {
 }
 
 /** actions is an object with references to all action creators */
-function create (project) {
+function createProject (project) {
   return {
     type: actionTypes.CREATE_REQ,
     project
   }
 }
 
-function update (projectId, project) {
+function updateProject (projectId, project) {
   return {
     type: actionTypes.UPDATE_REQ,
     projectId,
@@ -32,13 +32,13 @@ function update (projectId, project) {
   }
 }
 
-function remove (projectId) {
+function removeProject (projectId) {
   return {
     type: actionTypes.REMOVE_REQ,
     projectId
   }
 }
-export const actions = { create, update, remove }
+export const actions = { createProject, updateProject, removeProject }
 
 /** state definition */
 export const state = []

+ 11 - 0
src/registermap/components/EditRegister.js

@@ -0,0 +1,11 @@
+import React from 'react'
+
+class EditRegisters extends React.Component {
+  render () {
+    return (
+      <p>bit</p>
+    )
+  }
+}
+
+export default EditRegisters

+ 47 - 0
src/registermap/components/EditRegistermap.js

@@ -0,0 +1,47 @@
+import React from 'react'
+
+class EditRegistermap extends React.Component {
+  constructor () {
+    super()
+    this.update = this.update.bind(this)
+  }
+
+  update () {
+    const { registermap, actions } = this.props
+    actions.updateRegistermap(registermap.id, {
+      id: registermap.id,
+      name: this.regmapName.value,
+      description: this.regmapDescription.value,
+      nrOfRegisters: this.regmapNrOfRegisters.value,
+      bitsPerRegister: this.regmapBitsPerRegister.value,
+      modes: this.regmapModes.value
+    })
+  }
+
+  render () {
+    const { registermap } = this.props
+    console.log('Edit registermap', registermap)
+
+    return (
+      <form className='app-form'>
+        <fieldset className='create-registermap-form-basic'>
+          <button onClick={this.toggleExtended}>+</button>
+          <input type='text' ref={input => { this.regmapName = input }} value={registermap.name} onChange={this.update} placeholder='Registermap Name' />
+          <input type='text' ref={input => { this.regmapDescription = input }} value={registermap.description} onChange={this.update} placeholder='Description' />
+        </fieldset>
+        <fieldset className='create-registermap-form-extended'>
+          <input type='text' ref={input => { this.regmapNrOfRegisters = input }} value={registermap.nrOfRegisters} onChange={this.update} placeholder='Number of registers' />
+          <input type='text' ref={input => { this.regmapBitsPerRegister = input }} value={registermap.bitsPerRegister} onChange={this.update} placeholder='Bits per register' />
+          <input type='text' ref={input => { this.regmapModes = input }} list='modes' value={registermap.modes} onChange={this.update} placeholder='modes' />
+          <datalist id='modes'>
+            <option value='GPS only'>GPS only</option>
+            <option value='Multi GNSS'>Multi GNSS</option>
+          </datalist>
+          <button type='submit'>Save</button>
+        </fieldset>
+      </form>
+    )
+  }
+}
+
+export default EditRegistermap

+ 70 - 0
src/registermap/components/EditSetting.js

@@ -0,0 +1,70 @@
+import React from 'react'
+
+class EditSetting extends React.Component {
+  constructor () {
+    super()
+    this.saveSetting = this.saveSetting.bind(this)
+    this.updateSetting = this.updateSetting.bind(this)
+  }
+
+  saveSetting (event) {
+    event.preventDefault()
+    this.props.actions.updateSetting(12, {
+      name: this.name.value,
+      description: this.description.value,
+      nrOfBits: this.nrOfBits.value,
+      comment: this.comment.value,
+      format: this.format.value,
+      formatOverride: this.formatOverride.value
+    })
+    console.log('Saving ')
+  }
+
+  updateSetting (event) {
+    event.preventDefault()
+    this.props.actions.updateSetting(this.id.value, {
+      name: this.name.value,
+      description: this.description.value,
+      nrOfBits: this.nrOfBits.value,
+      comment: this.comment.value,
+      format: this.format.value,
+      formatOverride: this.formatOverride.value
+    })
+    console.log('Updating')
+  }
+
+  checkSetting (event) {
+
+  }
+
+  render () {
+    const { setting } = this.props
+    if (typeof setting.id === 'undefined') {
+      setting.id = null
+      setting.name = ''
+      setting.description = ''
+      setting.nrOfBits = 1
+      setting.range = {
+        lower: 0,
+        upper: 1
+      }
+      setting.comment = ''
+      setting.format = ''
+      setting.formatOverride = {}
+    }
+    return (
+      <form className='app-form' onSubmit={this.saveSetting}>
+        <input type='text' ref={input => { this.name = input }} value={setting.name} placeholder='Setting name' onChange={this.updateSetting} onBlur={this.checkSetting} />
+        <input type='text' ref={input => { this.description = input }} value={setting.description} placeholder='Description' onChange={this.updateSetting} onBlur={this.checkSetting} />
+        <input type='text' ref={input => { this.id = input }} value={setting.id} placeholder='Description' onChange={this.updateSetting} onBlur={this.checkSetting} />
+        <input type='text' ref={input => { this.nrOfBits = input }} value={setting.nrOfBits} placeholder='Number of bits' onChange={this.updateSetting} onBlur={this.checkSetting} />
+        <input type='text' ref={input => { this.comment = input }} value={setting.comment} placeholder='Comment' onChange={this.updateSetting} onBlur={this.checkSetting} />
+        <input type='text' ref={input => { this.format = input }} value={setting.format} placeholder='Format' onChange={this.updateSetting} onBlur={this.checkSetting} />
+        <input type='text' ref={input => { this.formatOverride = input }} value={setting.formatOverride} placeholder='Format Override' onChange={this.updateSetting} onBlur={this.checkSetting} />
+        <button type='submit'>Save</button>
+      </form>
+    )
+  }
+}
+
+export default EditSetting

+ 38 - 0
src/registermap/components/Registermap.js

@@ -0,0 +1,38 @@
+import React from 'react'
+import ShowRegistermap from './ShowRegistermap'
+import RegistermapList from './RegistermapList'
+
+class Registermap extends React.Component {
+  render () {
+    // Retrieve the registermapId from the URL
+    const { registermapId } = this.props.params
+    const { registermapActions } = this.props
+    const { registermap, settings } = this.props.registermap
+
+    let registerMap
+    if (typeof registermapId !== 'undefined' && registermap && registermap.length > 0) {
+      // It the registermapId is defined, check whether there's such a registermap
+      registerMap = registermap.find(registermap => { return (registermap.id === registermapId) })
+    } else if (registermap && registermap.length > 0) {
+      registerMap = registermap[0]
+      this.props.router.transitionTo(`/registermap/${registerMap}`)
+    }
+    console.log('Registermap', registerMap, registermap, settings)
+
+    return (
+      <div className='app-main'>
+        <div className='app-content'>
+          <button onClick={registermapActions.loadSamples}>Load sample data</button>
+          {(typeof registerMap !== 'undefined') ? <ShowRegistermap registermap={registerMap} settingList={settings} actions={registermapActions} /> : <p>Please select a registermap.</p>}
+        </div>
+        <div className='app-aside-left'>
+          <p>New Registermap Button</p>
+          <p>Registermap List</p>
+          {<RegistermapList registermapList={registermap} />}
+        </div>
+      </div>
+    )
+  }
+}
+
+export default Registermap

+ 23 - 0
src/registermap/components/RegistermapList.js

@@ -0,0 +1,23 @@
+import React from 'react'
+import { Link } from 'react-router'
+
+class RegistermapList extends React.Component {
+  render () {
+    const { registermapList } = this.props
+    if (typeof registermapList !== 'undefined' && registermapList.length > 0) {
+      return (
+        <ul className='registermap-list scrollable'>
+          {registermapList.map((registermap, key) =>
+            <li key={key}><Link to={`/registermap/${registermap.id}`}>{registermap.name}</Link></li>
+          )}
+        </ul>
+      )
+    } else {
+      return (
+        <p>No registermaps found.</p>
+      )
+    }
+  }
+}
+
+export default RegistermapList

+ 83 - 0
src/registermap/components/ShowRegistermap.js

@@ -0,0 +1,83 @@
+import React from 'react'
+import EditRegistermap from './EditRegistermap'
+import EditSetting from './EditSetting'
+
+class ShowRegistermap extends React.Component {
+  constructor () {
+    super()
+    this.sayMyName = this.sayMyName.bind(this)
+    this.table = this.table.bind(this)
+
+    this.bitmap = {}
+  }
+
+  sayMyName (event) {
+    event.preventDefault()
+    console.log('Hi, my name is', event)
+  }
+
+  table (registermap, settings) {
+    // Generate a table with rows for registers and columns for bits
+    let trow = []
+    let idx
+
+    const thead = []
+    const tbody = []
+
+    for (let idxCol = 0; idxCol < registermap.bitsPerRegister; idxCol += 1) {
+      trow.push(<th key={idxCol}>{idxCol}</th>)
+    }
+    trow.push(<th key={registermap.bitsPerRegister}>Register Name</th>)
+    thead.push(<tr key={-1}>{trow}</tr>)
+
+    for (let idxRow = 0; idxRow < registermap.nrOfRegisters; idxRow += 1) {
+      trow = []
+      for (let idxCol = 0; idxCol < registermap.bitsPerRegister; idxCol += 1) {
+        idx = idxRow * registermap.bitsPerRegister + idxCol
+        this.bitmap[idx] = ''
+        trow.push(<td key={idx} onClick={this.sayMyName}>{this.bitmap[idx]}</td>)
+      }
+      trow.push(<td key={`inp${idxRow}`}><input ref={input => { this.bitmap[idxRow] = input }} type='text' placeholder={`register${idxRow}`} onChange={this.updateRegister} /></td>)
+      tbody.push(<tr key={idxRow}>{trow}</tr>)
+    }
+    return (
+      <table>
+        <thead>{thead}</thead>
+        <tbody>{tbody}</tbody>
+      </table>
+    )
+  }
+
+  render () {
+    const { registermap, settingList, actions } = this.props
+    console.log('Show registermap', registermap, settingList, actions)
+    if (typeof settingList === 'undefined') {
+      return (
+        <p>Please select a registermap</p>
+      )
+    }
+
+    return (
+      <div className='content-flex'>
+        <div className='content-main'>
+          <h2>Registermap {registermap.name}</h2>
+          <EditRegistermap registermap={registermap} actions={actions} />
+          <h2>Registers</h2>
+          {this.table(registermap, settingList)}
+          <div>Warning: Not all settings fit in the registers!</div>
+        </div>
+        <div className='content-aside-right'>
+          <h2>Settings</h2>
+          {settingList.map((value, idx) => {
+            return (
+              <EditSetting key={idx} setting={value} actions={actions} />
+            )
+          }
+          )}
+        </div>
+      </div>
+    )
+  }
+}
+
+export default ShowRegistermap

+ 7 - 0
src/registermap/components/index.js

@@ -0,0 +1,7 @@
+import EditRegistermap from './EditRegistermap'
+import EditSetting from './EditSetting'
+import EditRegister from './EditRegister'
+import Registermap from './Registermap'
+import ShowRegistermap from './ShowRegistermap'
+
+export default { Registermap, EditRegistermap, EditSetting, EditRegister, ShowRegistermap }

+ 8 - 0
src/registermap/constants.js

@@ -0,0 +1,8 @@
+/*
+ * constants.js
+ *
+ * Collection of all constants used in the component.
+ * Also define 'name' which is used to identify the component.
+ **/
+
+export const NAME = 'registermap'

+ 0 - 0
src/bitfield/helpers.js → src/registermap/helpers.js


+ 17 - 23
src/bitfield/index.js → src/registermap/index.js

@@ -7,14 +7,14 @@
  **/
 
 import * as constants from './constants'
-import { actions, reducer } from './state'
-import * as components from './components'
+import { actions, reducer, state } from './state'
+import components from './components'
 // import { createSelector } from 'reselect'
 // import { NAME } from './constants'
 // import _ from 'lodash'
 
 const filters = {
-  all: projects => projects.filter(p => projects.active)
+  all: registermap => registermap.filter(p => registermap.active)
 }
 
 const selectors = {
@@ -22,6 +22,18 @@ const selectors = {
   getActive: () => []
 }
 
+function registermapFactory () {
+  return {
+
+  }
+}
+
+function settingFactory () {
+  return {
+
+  }
+}
+
 /* export const getAll = state => state[NAME]
 export const getActive = _.compose(filterActive, getAll)
 export const getCounts = createSelector(
@@ -33,23 +45,5 @@ export const getCounts = createSelector(
     })
 ) */
 
-const bitfield = {
-  name: '',
-  description: '',
-  nrOfRegisters: 0,
-  bitsPerRegister: 0,
-  modes: []
-}
-
-const setting = {
-  name: '',
-  description: '',
-  nrOfBits: 0,
-  bitMap: [],
-  comment: '',
-  synonyms: [],
-  resetValue: 0,
-  defaultValues: []
-}
-
-export default { actions, constants, components, filters, selectors, reducer }
+console.log('Exporting registermap:', { actions, constants, components, filters, selectors, reducer, state, registermapFactory, settingFactory })
+export default { actions, constants, components, filters, selectors, reducer, state, registermapFactory, settingFactory }

+ 90 - 0
src/registermap/initialData.js

@@ -0,0 +1,90 @@
+export const registermapList = [{
+  id: 'asfsersfashdjkhkfsdf',
+  name: 'RF I2C Table',
+  description: 'RF I2C Table for the Jungfrau RF receiver.',
+  nrOfRegisters: 2,
+  bitsPerRegister: 8,
+  modes: [
+    'GPS only',
+    'Multi-GNSS'
+  ],
+  registers: {
+    0: 'iic_lna_ctrl_reg',
+    1: 'iic_mxr_ctrl_a_reg'
+  }
+}, {
+  id: 'jljserlsfbsldfsf',
+  name: 'RF ADC Table',
+  description: 'RF ADC Table for the Jungfrau RF receiver.',
+  nrOfRegisters: 2,
+  bitsPerRegister: 8,
+  modes: [
+    'GPS only',
+    'Multi-GNSS'
+  ],
+  registers: {
+    0: 'iic_lna_ctrl_reg',
+    1: 'iic_mxr_ctrl_a_reg'
+  }
+}
+]
+
+export const settingList = [{
+  id: 'sdfsegdfgbcvbcvdfgsg',
+  registermap: 'asfsersfashdjkhkfsdf',
+  name: 'lna_trim',
+  description: 'LNA trimming',
+  nrOfBits: 3,
+  bitMap: [3, 2, 1],
+  comment: '',
+  values: {
+    func: (value) => `${value * 5}uA`,
+    override: {
+      7: '150uA'
+    }
+  },
+  range: {
+    lower: null,
+    upper: null
+  },
+  resetValue: 0,
+  defaultValues: [
+    0,
+    0
+  ]
+}, {
+  id: 'zdfskehrsdhfskdhfksdhfks',
+  registermap: 'asfsersfashdjkhkfsdf',
+  name: 'lna_en',
+  description: 'LNA bias enable',
+  nrOfBits: 1,
+  bitMap: [4],
+  comment: '',
+  values: {
+    func: (value) => Math.pow(2, value)
+  },
+  resetValue: 0,
+  defaultValues: [
+    1,
+    1
+  ]
+}, {
+  id: 'ynmnrkswefhkfhkfsefsg',
+  registermap: 'asfsersfashdjkhkfsdf',
+  name: 'mxr_trim',
+  description: 'Mixer bias current trimming',
+  nrOfBits: 5,
+  bitMap: [12, 11, 10, 9, 8],
+  comment: 'Only trim if you want to',
+  values: {
+    func: (value) => `${value * 6}uA`,
+    override: {
+      13: '5mA (boost)'
+    }
+  },
+  resetValue: 0,
+  defaultValues: [
+    0,
+    0
+  ]
+}]

+ 188 - 0
src/registermap/state.js

@@ -0,0 +1,188 @@
+/**
+ * state.js
+ *
+ * Collection of everything which has to do with state changes.
+ * - actionTypes
+ * - actions
+ **/
+
+import { NAME } from './constants'
+import { registermapList, settingList } from './initialData'
+// import { call, put, takeEvery } from 'redux-saga/effects'
+
+/** actionTypes define what actions are handeled by the reducer. */
+export const actionTypes = {
+  CREATE_SETTING: `${NAME}/CREATE_SETTING`,
+  UPDATE_SETTING: `${NAME}/UPDATE_SETTING`,
+  DELETE_SETTING: `${NAME}/DELETE_SETTING`,
+  CREATE_REGISTERMAP: `${NAME}/CREATE_REGISTERMAP`,
+  UPDATE_REGISTERMAP: `${NAME}/UPDATE_REGISTERMAP`,
+  DELETE_REGISTERMAP: `${NAME}/DELETE_REGISTERMAP`,
+  LOAD_SAMPLES: `${NAME}/LOAD_SAMPLES`
+}
+
+/** actions is an object with references to all action creators */
+function createSetting (setting) {
+  return {
+    type: actionTypes.CREATE_SETTING,
+    setting
+  }
+}
+
+function updateSetting (settingId, setting) {
+  return {
+    type: actionTypes.UPDATE_SETTING,
+    settingId,
+    setting
+  }
+}
+
+function removeSetting (settingId) {
+  return {
+    type: actionTypes.DELETE_SETTING,
+    settingId
+  }
+}
+function createRegistermap (registermap) {
+  return {
+    type: actionTypes.CREATE_REGISTERMAP,
+    registermap
+  }
+}
+
+function updateRegistermap (registermapId, registermap) {
+  return {
+    type: actionTypes.UPDATE_REGISTERMAP,
+    registermapId,
+    registermap
+  }
+}
+
+function removeRegistermap (registermapId) {
+  return {
+    type: actionTypes.DELETE_REGISTERMAP,
+    registermapId
+  }
+}
+
+function loadSamples () {
+  return {
+    type: actionTypes.LOAD_SAMPLES
+  }
+}
+export const actions = { createSetting, updateSetting, removeSetting, createRegistermap, updateRegistermap, removeRegistermap, loadSamples }
+
+/** state definition */
+export const state = {
+  registermap: [],
+  settings: []
+}
+
+/** reducer is called by the redux dispatcher and handles all component actions */
+function registermapSettings (state = {}, action) {
+  console.log(`Entering registermap setting reducer.`, state, action)
+  switch (action.type) {
+    case actionTypes.CREATE_SETTING:
+      console.log(`Creating setting.`, state, action)
+      return {
+        ...state,
+        [action.project.projectId]: action.project
+      }
+    case actionTypes.UPDATE_SETTING:
+      console.log(`Update setting.`, state, action)
+      return {
+        ...state,
+        [action.projectId]: action.project
+      }
+    case actionTypes.DELETE_SETTING:
+      console.log(`Delete setting.`, state, action)
+      return {
+        ...state,
+        [action.projectId]: null
+      }
+    default:
+      return state
+  }
+}
+
+function registermapAction (state = {}, action) {
+  console.log(`Entering registermap reducer.`, state, action)
+  switch (action.type) {
+    case actionTypes.CREATE_REGISTERMAP:
+      console.log(`Creating registermap.`, state, action)
+      return {
+        ...state,
+        [action.project.projectId]: action.project
+      }
+    case actionTypes.UPDATE_REGISTERMAP:
+      console.log(`Update registermap.`, state, action)
+      const idx = state.findIndex(elem => { return (elem.id === action.registermapId) })
+      console.log(`Update registermap.`, idx)
+      const next = [
+        ...state.slice(0, idx),
+        action.registermap,
+        ...state.slice(idx + 1)
+      ]
+      console.log(`Update registermap.`, next)
+      return next
+    case actionTypes.DELETE_REGISTERMAP:
+      console.log(`Delete registermap.`, state, action)
+      return {
+        ...state,
+        [action.projectId]: null
+      }
+    default:
+      return state
+  }
+}
+
+export function reducer (state = {}, action) {
+  console.log(`Entering registermap root reducer.`, state, action)
+  if (typeof action.settingId !== 'undefined') {
+    return {
+      ...state,
+      registermap: registermapSettings(state.registermap, action)
+    }
+  }
+  if (typeof action.registermapId !== 'undefined') {
+    return {
+      ...state,
+      registermap: registermapAction(state.registermap, action)
+    }
+  }
+  switch (action.type) {
+    case actionTypes.LOAD_SAMPLES:
+      console.log(`Loading samples.`, state, action, registermapList, settingList)
+      console.log({...state, registermap: registermapList, settings: settingList})
+      return {
+        ...state,
+        registermap: registermapList,
+        settings: settingList
+      }
+    default:
+      return state
+  }
+}
+
+/** sagas are asynchronous workers (JS generators) to handle the state.
+// Worker
+export function * incrementAsync () {
+  try {
+    const data = yield call(Api.isIncrementOk)
+    yield put({ type: 'INCREMENT_SUCCESS', data })
+  } catch (error) {
+    yield put({ type: 'INCREMENT_FAIL', error})
+  }
+}
+
+// Watcher (intercepts INCREMENT_REQUEST, dispatches INCREMENT_SUCCESS or INCREMENT_FAIL in return.)
+export function * watchIncrementAsync () {
+  yield takeEvery('INCREMENT_REQUEST', incrementAsync)
+}
+
+// export all sagas in parallel
+function * sagas () {
+  yield takeEvery('INCREMENT_REQUEST')
+  yield takeEvery('DECREMENT_REQUEST')
+} */
+export const sagas = null