AFRAME.registerComponent('datgui', {
  name: {
    type: 'string',
    default: 'settings'
  },
  schema: {
    controllerRight: {
      type: 'selector',
      default: '#controller_right'
    },
    controllerLeft: {
      type: 'selector',
      default: '#controller_left'
    },
    name: {
      type: 'string',
      default: 'controls'
    }
  },
  init: function(){
    const gui = dat.GUIVR.create( this.data.name );
    this.el.setObject3D('gui', gui );
    this.el.gui = gui;

    const scene = this.el.sceneEl.object3D;

    function bindInput( el, input ){
      el.addEventListener('triggerdown', function(){
        input.pressed( true );
      });
      el.addEventListener('triggerup', function(){
        input.pressed( false );
      });
      el.addEventListener('gripdown', function(){
        input.gripped( true );
      });
      el.addEventListener('gripup', function(){
        input.gripped( false );
      });
    }

    const controllerRightEl = this.data.controllerRight;
    if( controllerRightEl ){
      const right = dat.GUIVR.addInputObject( controllerRightEl.object3D );
      scene.add( right );
      bindInput( controllerRightEl, right );
    }

    const controllerLeftEl = this.data.controllerLeft;
    if( controllerLeftEl ){
      const left = dat.GUIVR.addInputObject( controllerRightEl.object3D );
      scene.add( left );
      bindInput( controllerLeftEl, left );
    }

    const { camera, renderer } = this.el.sceneEl;
    dat.GUIVR.enableMouse( camera, renderer );

    if( this.data.name ){
      gui.name( this.data.name );
    }
  }
});

AFRAME.registerPrimitive('a-datgui', {
  defaultComponents: {
    datgui: {}
  },
  mappings: {
    name: 'datgui.name',
    controllerRight: 'datgui.controllerRight',
    controllerLeft: 'datgui.controllerLeft'
  }
});

AFRAME.registerComponent( 'guicontroller', {
  schema: {
    min: {
      type: 'number',
      default: 0
    },
    max: {
      type: 'number',
      default: 1
    },
    step: {
      type: 'number',
      default: 0.1
    },
    type: {
      type: 'string',
      default: 'slider'
    },
    defaultValue: {
      type: 'boolean',
      default: false
    },
    name: {
      type: 'string',
      default: undefined
    }
  },
  init: function(){
    const that = this;

    const gui = that.el.parentNode.gui;

    switch( that.data.type ){
      case 'slider':
        that.controller = gui.addSlider( that.data.min, that.data.max ).step( that.data.step );
        break;
      case 'dropdown':
        const options = {};
        Array
          .from( that.el.getElementsByTagName('a-gui-option') )
          .forEach( function( el ){
            options[ el.textContent ] = el.getAttribute('value');
          });
        that.controller = gui.addDropdown( options );
        break;
      case 'checkbox':
        that.controller = gui.addCheckbox( that.data.defaultValue );
        break;
      case 'button':
        that.controller = gui.addButton( function(){} );
        break;
    }

    if( that.controller && that.data.name !== undefined ){
      that.controller.name( that.data.name );
    }

    if( that.controller && that.controller.onChange ){
      that.controller.onChange( function( v ){
        that.el.emit( 'onChanged', {value: v} );
      });
    }

  }
});

AFRAME.registerPrimitive( 'a-gui-slider', {
  defaultComponents: {
    'guicontroller': {
      type: 'slider'
    }
  },
  mappings: {
    name: 'guicontroller.name',
    min: 'guicontroller.min',
    max: 'guicontroller.max',
    step: 'guicontroller.step'
  }
});

AFRAME.registerPrimitive( 'a-gui-dropdown', {
  defaultComponents: {
    'guicontroller': {
      type: 'dropdown'
    }
  },
  mappings: {
    name: 'guicontroller.name'
  }
});

AFRAME.registerPrimitive( 'a-gui-checkbox', {
  defaultComponents: {
    'guicontroller': {
      type: 'checkbox'
    }
  },
  mappings: {
    name: 'guicontroller.name',
    default: 'guicontroller.defaultValue'
  }
});

AFRAME.registerPrimitive( 'a-gui-button', {
  defaultComponents: {
    'guicontroller': {
      type: 'button'
    }
  },
  mappings: {
    name: 'guicontroller.name'
  }
});

const guiOptionTag = document.registerElement('a-gui-option');
@@ -1867,19 +1867,20 @@
 : %7B%0A    
-min
+type
 : %7B%0A    
@@ -1880,38 +1880,38 @@
  %7B%0A      type: '
-number
+string
 ',%0A      default
@@ -1912,17 +1912,123 @@
 efault: 
-0
+'slider',%0A      oneOf: %5B%0A        'slider',%0A        'dropdown',%0A        'checkbox',%0A        'button'%0A      %5D
 %0A    %7D,%0A
@@ -2032,18 +2032,18 @@
 %7D,%0A    m
-ax
+in
 : %7B%0A    
@@ -2075,17 +2075,45 @@
 efault: 
-1
+0,%0A      if: %7Btype: 'slider'%7D
 %0A    %7D,%0A
@@ -2116,20 +2116,19 @@
  %7D,%0A    
-step
+max
 : %7B%0A    
@@ -2164,11 +2164,37 @@
 lt: 
-0.1
+1,%0A      if: %7Btype: 'slider'%7D
 %0A   
@@ -2197,28 +2197,28 @@
 %0A    %7D,%0A    
-type
+step
 : %7B%0A      ty
@@ -2214,38 +2214,38 @@
  %7B%0A      type: '
-string
+number
 ',%0A      default
@@ -2237,32 +2237,54 @@
 ,%0A      default:
+ 0.1,%0A      if: %7Btype:
  'slider'%0A    %7D,
@@ -2272,24 +2272,25 @@
 pe: 'slider'
+%7D
 %0A    %7D,%0A    
@@ -2348,16 +2348,46 @@
 t: false
+,%0A      if: %7Btype: 'checkbox'%7D
 %0A    %7D,%0A
@@ -4422,8 +4422,9 @@
 ption');
+%0A

Add schema validation.