├── .bowerrc ├── .gitignore ├── LICENSE ├── README.md ├── app ├── images │ └── .gitkeep ├── index.html ├── scripts │ ├── actions │ │ └── itemActions.js │ ├── components │ │ ├── rdb-board.jsx │ │ └── rdb-sidebar.jsx │ ├── config │ │ └── rdb-default.config.js │ ├── factories │ │ ├── rdb-board-factory.jsx │ │ ├── rdb-errorpage-factory.jsx │ │ └── rdb-widget-factory.jsx │ ├── helper │ │ ├── rdb-config-validator.js │ │ ├── rdb-font-loader.js │ │ ├── rdb-styler.js │ │ └── rdb-utils.js │ ├── main.js │ ├── mixins │ │ ├── rdb-chart-mixin.js │ │ └── rdb-widget-mixin.jsx │ ├── pages │ │ ├── rdb-app.jsx │ │ └── rdb-errorpage.jsx │ ├── routes.jsx │ ├── stores │ │ └── itemStore.js │ └── widgets │ │ ├── barchart │ │ └── index.jsx │ │ ├── iframe │ │ └── index.jsx │ │ ├── linechart │ │ └── index.jsx │ │ ├── list │ │ └── index.jsx │ │ ├── map │ │ └── index.jsx │ │ ├── rdb-base-widget.jsx │ │ └── todos │ │ ├── index.jsx │ │ ├── style.css │ │ ├── todo.jsx │ │ ├── todosActions-factory.js │ │ └── todosStore-factory.js └── stylus │ ├── helper.styl │ ├── main.styl │ ├── normalize.styl │ └── widgets.styl ├── bower.json ├── gulpfile.js ├── package.json ├── rdb.config.js └── webpack.config.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory" : "app/bower_components" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | app/bower_components 4 | app/bundles 5 | *.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 webkid 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Dashboard (RDB) 2 | 3 | **This project is at an early stage of development!** 4 | 5 | RDB helps you to generate dashboard prototypes very quickly with "configuration over code". 6 | You just need to specify the boards and the belonging widgets in the [RDB config file](#rdb-config-file) file and you are ready to build your dashboard. 7 | It's also easy to extend RDB by [writing your own widgets](#writing-your-own-widgets). 8 | 9 | RDB is based on our [react starterkit](https://github.com/wbkd/react-starterkit). 10 | 11 | * [Get Started](#get-started) 12 | * [Documentation](#documentation) 13 | * [Extend RDB](#extend-rdb) 14 | 15 | 16 | # Get Started 17 | 18 | #### Clone RDB 19 | 20 | ``` 21 | $ git clone https://github.com/wbkd/react-dashboard.git && cd react-dashboard 22 | ``` 23 | 24 | #### Installation 25 | 26 | Install all dependencies. 27 | 28 | ``` 29 | $ npm install 30 | ``` 31 | 32 | #### Development 33 | 34 | Builds the application and starts a webserver with livereload. By default the webserver starts at port 1337. 35 | You can define a port with `$ gulp --port 3333`. 36 | 37 | ``` 38 | $ gulp 39 | ``` 40 | 41 | #### Build 42 | 43 | Builds a minified version of the application in the dist folder. 44 | 45 | ``` 46 | $ gulp build --type production 47 | ``` 48 | 49 | # Documentation 50 | 51 | ### RDB config file 52 | 53 | You can find the [config file](rdb.config.js) in the root folder. 54 | It has three attributes (name, [style](#style) and [boards](#boards)). 55 | The name is displayed at the top of the sidebar as the title of the dashboard. 56 | Style indicates stuff like the font, colors etc and under boards (the important part) you configure the different boards and their related [widgets](#widgets). 57 | If you do not specify a property we use the default one, you can find in the [default config](app/scripts/config/rdb-default.config.js). 58 | 59 | **Properties:** 60 | 61 | * `name` String that defines the dashboard title. Default: 'RDB Dashboard'. 62 | * `style` Object with [style](#style) properties 63 | * `boards` Array with [boards](#boards) 64 | 65 | **Example config file:** 66 | 67 | ```javascript 68 | 69 | module.exports = { 70 | name: 'Awesome dashboard', 71 | style: { 72 | font : 'Roboto', 73 | titlebg : '#fff', 74 | titlecol : 'red' 75 | }, 76 | boards: [ 77 | { 78 | name: 'Site A', 79 | widgets: [ 80 | {type : 'map', properties: { center : [52.25, 13.4] }}, 81 | {type : 'line', properties: { data : jsonObjA }} 82 | ] 83 | }, 84 | { 85 | name: 'Site B', 86 | widgets: [ 87 | {type : 'map', properties: { center : [52.25, 13.4] }}, 88 | {type : 'bar', properties: { data : jsonObjB }} 89 | ] 90 | } 91 | ] 92 | }; 93 | 94 | ``` 95 | 96 | ### Style 97 | 98 | For now it's only possible to tweak some colors and change the font type. 99 | You can find the available fonts and the font loading logic in the [Font Loader](app/scripts/helper/rdb-font-loader.js). 100 | The styles are applied in the [Styler Module](app/scripts/helper/rdb-styler.js). 101 | 102 | Available fonts are: 103 | * Droid Sans 104 | * Lato 105 | * PT Sans 106 | * Roboto 107 | * Open Sans 108 | 109 | **Properties:** 110 | 111 | * `font` Font type. Default: `'PT Sans'` 112 | * `titlebg` Background color of the title. Default: `'#eeeeee'`. 113 | * `titlecol` Font color of title. Default: `'#222222'` 114 | * `sidebarbg` Background color of the sidebar. Default: `'#303030'` 115 | * `sidebarcol` Font color of the sidebar. Default: `'#f4f4f4'` 116 | * `boardbg` Background color of the boards. Default: `'#f4f4f4'` 117 | * `boardcol` Font color of the boards. Default: `'#222222'` 118 | * `widgetbg` Background color of the widgets. Default: `'#ffffff'` 119 | * `widgetcol` Font color of the widgets. Default: `'#222222'` 120 | 121 | 122 | **Example style:** 123 | 124 | ```javascript 125 | style: { 126 | font : 'PT Sans', 127 | titlebg : '#eeeeee', 128 | titlecol : '#222222', 129 | sidebarbg : '#303030', 130 | sidebarcol : '#f4f4f4', 131 | boardbg : '#f4f4f4', 132 | boardcol : '#222222' 133 | } 134 | 135 | ``` 136 | 137 | 138 | ### Boards 139 | 140 | The boards property is an array with board objects. A board object has a name and and several widgets. 141 | When you build the dashboard, the several boards are dynamically generated in [Routes](app/scripts/routes.jsx) with the help of the [Board Factory](app/scripts/factories/rdb-board-factory.jsx). 142 | The [Board Component](app/scripts/components/rdb-board.jsx) then displays the title and loads its widgets. 143 | 144 | **Example Board:** 145 | 146 | ```javascript 147 | 148 | { 149 | name: 'Site A', 150 | widgets: [ 151 | {type : 'map', properties: { center : [52.25, 13.4] }}, 152 | {type : 'linechart', properties: { data : jsonObj }} 153 | ] 154 | } 155 | 156 | ``` 157 | 158 | ### Widgets 159 | 160 | The widgets are defined in the board objects. A widget object has a type and properties. 161 | The type specifies the widget type and the properties define all data that get passed to the widget. 162 | Widgets are created in the [Board Component](app/scripts/components/rdb-board.jsx) with the help of [Widget Factory](app/scripts/factories/rdb-widget-factory.jsx). 163 | 164 | For now there are these widgets available: 165 | 166 | * [Map Widget](#map-widget) 167 | * [Bar Chart Widget](#bar-chart-widget) 168 | * [Line Chart Widget](#line-chart-widget) 169 | * [IFrame Widget](#iframe-widget) 170 | 171 | ### Map Widget 172 | 173 | The map widget is based on [Leaflet](http://leafletjs.com). It displays a map with a certain center and markers with tooltips. 174 | 175 | **Properties:** 176 | 177 | * `title` String that defines the widget title. Default: No Title. 178 | * `center` Array with Coordinates. Default: `[0,0]` 179 | * `zoom` Integer with the starting zoom level. Default: `13`. 180 | * `wmsTileLayerUrl` String that declares the WMS tile layer. Default: `http://tile.stamen.com/toner/{z}/{x}/{y}.png`. 181 | * `marker` Array with marker objects. Default : No marker. 182 | * `latlng` Array with Coordinates. 183 | * `text` String with tooltip content. Default: No content. 184 | 185 | **Example Map Widget Configuration:** 186 | 187 | ```javascript 188 | 189 | { type: 'map', 190 | properties: { 191 | title: 'map title', 192 | center: [52.52, 13.4], 193 | zoom : 10, 194 | marker: [ 195 | { latlng: [52.52, 13.4], text : 'This is a marker.'}, 196 | {latlng: [52.55, 13.35]} 197 | ] 198 | } 199 | } 200 | 201 | ``` 202 | Source: [Map Widget](app/scripts/widgets/map/index.jsx) 203 | 204 | 205 | #### Bar Chart Widget 206 | 207 | The bar chart widget is based on [c3](http://c3js.org). 208 | 209 | **Properties:** 210 | 211 | * `title` String that defines the widget title. Default: No Title. 212 | * `data` Array with JSON data. Default: [{ alpha: 100, beta : 120, gamma: 110 },{ alpha: 120, beta : 110, gamma: 90 },{ alpha: 75, beta : 100,gamma: 80 }] 213 | * `keys` Array with keys you want to display. Default: Every key found in the passed data. 214 | 215 | **Example Bar Chart Widget Configuration:** 216 | 217 | ```javascript 218 | 219 | { 220 | type: 'barchart', 221 | properties: { 222 | title : 'A Bar Chart \o/', 223 | data: [{ alpha: 100, beta : 120, gamma: 110 },{ alpha: 120, beta : 110, gamma: 90 },{ alpha: 75, beta : 100,gamma: 80 }], 224 | keys : ['alpha', 'gamma'] 225 | } 226 | } 227 | 228 | ``` 229 | Source: [Bar Chart Widget](app/scripts/widgets/bar/index.jsx) 230 | 231 | #### Line Chart Widget 232 | 233 | Almost the same configuration as the bar chart widget but type is equal `linechart` in this case. 234 | 235 | ```javascript 236 | 237 | { 238 | type: 'linechart', 239 | properties: { 240 | title : 'A Line Chart \o/', 241 | data: [{ alpha: 100, beta : 120, gamma: 110 },{ alpha: 120, beta : 110, gamma: 90 },{ alpha: 75, beta : 100,gamma: 80 }], 242 | keys : ['beta'] 243 | } 244 | } 245 | 246 | ``` 247 | 248 | Source: [Line Chart Widget](app/scripts/widgets/line/index.jsx) 249 | 250 | #### IFrame Widget 251 | 252 | Displays a certain webpage. 253 | 254 | **Properties:** 255 | * `title` String that defines the widget title. Default: No Title. 256 | * `src` String that defines the url you want to display. Default: 'http://news.ycombinator.com'. 257 | 258 | **Example IFrame Widget Configuration:** 259 | 260 | ```javascript 261 | { 262 | type: 'iframe', 263 | properties: { 264 | title : 'True Reddit', 265 | src: 'http://www.reddit.com/r/TrueReddit/' 266 | } 267 | } 268 | ``` 269 | 270 | Source: [IFrame Widget](app/scripts/widgets/iframe/index.jsx) 271 | 272 | 273 | 274 | # Extend RDB 275 | 276 | #### Widgets 277 | 278 | Every widgets ships its own dependencies. You can use the css-loader to load CSS dependencies. 279 | The map widget for example loads leaflet.js and the related CSS file: 280 | 281 | ```javascript 282 | 283 | var L = require('leaflet'); 284 | require('../../../../node_modules/leaflet/dist/leaflet.css'); 285 | 286 | ``` 287 | 288 | If we want to check out a widget we don't want to specify any properties. We just want to see the widget. 289 | Because of that it's important that the widget has at least some default properties, so that something get rendered if we add the widget to a board. 290 | 291 | 292 | #### Writing your own Widgets 293 | 294 | Todo 295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /app/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbkd/dashboard-prototyper/efbf098bc4c978f7ccc0f70ae1f539f4f12deacb/app/images/.gitkeep -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | webkid react starterkit 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/scripts/actions/itemActions.js: -------------------------------------------------------------------------------- 1 | var Reflux = require('reflux'); 2 | 3 | var ItemActions = Reflux.createActions([ 4 | 'loadItems', 5 | 'loadItemsSuccess', 6 | 'loadItemsError' 7 | ]); 8 | 9 | ItemActions.loadItems.preEmit = function(data){ 10 | // make your api call/ async stuff here 11 | // we use setTimeout for faking async behaviour here 12 | setTimeout(function(){ 13 | var items = ['Foo', 'Bar', 'Lorem']; 14 | ItemActions.loadItemsSuccess(items); 15 | 16 | // on error 17 | // ItemActions.loadItemsError('an error occured'); 18 | },500); 19 | }; 20 | 21 | module.exports = ItemActions; -------------------------------------------------------------------------------- /app/scripts/components/rdb-board.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * The Board renders its title and the belonging widgets. 3 | */ 4 | 5 | var React = require('react'); 6 | var widgetFactory = require('../factories/rdb-widget-factory.jsx'); 7 | 8 | var Board = React.createClass({ 9 | 10 | render: function() { 11 | 12 | var widgets = this.props.widgets.map(function(widget,i){ 13 | return widgetFactory.create(widget); 14 | }); 15 | 16 | return ( 17 |
18 |

{this.props.name}

19 | { widgets } 20 |
21 | ); 22 | } 23 | 24 | }); 25 | 26 | module.exports = Board; -------------------------------------------------------------------------------- /app/scripts/components/rdb-sidebar.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * The Sidebar displays the links to the boards and the title of the dashboard 3 | * application. 4 | */ 5 | 6 | var React = require('react'); 7 | var Link = require('react-router').Link; 8 | var utils = require('rdbutils'); 9 | var rdbConf = require('rdbconf'); 10 | 11 | var Sidebar = React.createClass({ 12 | 13 | render: function() { 14 | 15 | var navItems = rdbConf.boards.map(function(board,i){ 16 | 17 | var boardName = board.name, 18 | boardUrl = utils.boardNameToUrl(boardName); 19 | 20 | return { boardName } 21 | }); 22 | 23 | return ( 24 |
25 |
{ rdbConf.name || 'RDB Dashboard' }
26 | 27 | 30 |
31 | ); 32 | } 33 | 34 | }); 35 | 36 | module.exports = Sidebar; -------------------------------------------------------------------------------- /app/scripts/config/rdb-default.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'RDB Dashboard', 3 | style: { 4 | font: 'PT Sans', 5 | titlebg : '#eeeeee', 6 | titlecol : '#222222', 7 | sidebarbg : '#303030', 8 | sidebarcol : '#f4f4f4', 9 | boardbg : '#f4f4f4', 10 | boardcol : '#222222', 11 | widgetbg: '#fff', 12 | widgetcol: '#222' 13 | }, 14 | boards: [] 15 | }; -------------------------------------------------------------------------------- /app/scripts/factories/rdb-board-factory.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Factory that creates Board objects. 3 | */ 4 | 5 | var React = require('react'); 6 | var Board = require('../components/rdb-board.jsx'); 7 | 8 | module.exports.create = function(props) { 9 | return React.createClass({ 10 | render: function() { 11 | return 12 | } 13 | }) 14 | }; -------------------------------------------------------------------------------- /app/scripts/factories/rdb-errorpage-factory.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Factory that creates error pages. 3 | */ 4 | 5 | var React = require('react'); 6 | var Error = require('../pages/rdb-errorpage.jsx'); 7 | 8 | module.exports.create = function(props) { 9 | return React.createClass({ 10 | render: function() { 11 | return 12 | } 13 | }) 14 | }; -------------------------------------------------------------------------------- /app/scripts/factories/rdb-widget-factory.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Factory that creates Widget objects with a unique key. 3 | */ 4 | 5 | var React = require('react'); 6 | var utils = require('rdbutils'); 7 | var shortId = require('shortid'); 8 | 9 | module.exports.create = function(widget) { 10 | var Widget = require('../widgets/' + widget.type + '/index.jsx'); 11 | 12 | if(utils.isUndefined(widget.properties)){ 13 | widget.properties = {}; 14 | } 15 | 16 | widget.properties._id = widget.type + '-' + shortId.generate(); 17 | 18 | return 19 | }; -------------------------------------------------------------------------------- /app/scripts/helper/rdb-config-validator.js: -------------------------------------------------------------------------------- 1 | // Do we need something like this? 2 | 3 | var utils = require('rdbutils'); 4 | 5 | module.exports.validate = function(config){ 6 | 7 | if(utils.isUndefined(config)){ 8 | return false; 9 | } 10 | 11 | if(utils.isUndefined(config.boards)){ 12 | config.boards = []; 13 | } 14 | 15 | if(!utils.isUndefined(config.name) && typeof config.name !== 'String' ){ 16 | return false; 17 | } 18 | 19 | return config; 20 | } -------------------------------------------------------------------------------- /app/scripts/helper/rdb-font-loader.js: -------------------------------------------------------------------------------- 1 | // available fonts 2 | var fonts = { 3 | 'Droid Sans' : 'Droid+Sans:400,700:latin', 4 | 'Lato' : 'Lato:400,700:latin', 5 | 'PT Sans': 'PT+Sans:400,700:latin', 6 | 'Roboto': 'Roboto:400,700:latin', 7 | 'Open Sans': 'Open+Sans:400,700:latin' 8 | }; 9 | 10 | var defaultFont = require('rdbDefault').style.font; 11 | 12 | /** 13 | * Loads related font. If the passed font name is not in the fonts list, 14 | * we load the default font. 15 | * 16 | * @param {string} fontName Name of the font we want to load. Has to be 17 | * in the 'fonts' list. 18 | */ 19 | module.exports.loadFont = function (fontName) { 20 | 21 | var fontToLoad = fonts[fontName]; 22 | 23 | if (typeof fontToLoad === 'undefined') { 24 | fontName = defaultFont; 25 | fontToLoad = fonts[fontName]; 26 | } 27 | 28 | loadWebfont(fontToLoad); 29 | 30 | document.body.style.fontFamily = '"' + fontName + '", sans-serif'; 31 | }; 32 | 33 | 34 | /** 35 | * Helper method that downloads google web font. 36 | * @param {string} font Font identifier and font weights. 37 | */ 38 | function loadWebfont(font){ 39 | window.WebFontConfig = { 40 | google: { 41 | families: [font] 42 | } 43 | }; 44 | 45 | var wf = document.createElement('script'); 46 | wf.src = ('https:' == document.location.protocol ? 'https' : 'http') + '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js'; 47 | wf.type = 'text/javascript'; 48 | wf.async = 'true'; 49 | 50 | var s = document.getElementsByTagName('script')[0]; 51 | s.parentNode.insertBefore(wf, s); 52 | } -------------------------------------------------------------------------------- /app/scripts/helper/rdb-styler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module merges the default with config style and creates a styesheet 3 | * with the certain css rules. We create a stylesheet instead of applying it 4 | * to the elements via js, because we don't want to wait until the components 5 | * got loaded. 6 | * 7 | */ 8 | 9 | var dom = require('rdbutils').dom; 10 | var merge = require('merge'); 11 | var fontLoader = require('./rdb-font-loader'); 12 | 13 | var defaultStyle = require('rdbDefault').style; 14 | 15 | /* 16 | * Merges the default with config style and creates a styesheet with the certain 17 | * css rules. 18 | * 19 | * @param {object} newStyle Custom Style object 20 | */ 21 | module.exports.applyStyles = function(newStyle){ 22 | 23 | var style = merge(defaultStyle, newStyle); 24 | 25 | createStylesheet(style); 26 | fontLoader.loadFont(style.fontName); 27 | } 28 | 29 | function createStylesheet(style){ 30 | // TODO: is there a better way to apply the styles?! 31 | 32 | var stylesheet = dom.create('style'); 33 | 34 | stylesheet.type = 'text/css'; 35 | stylesheet.innerHTML = [ 36 | getCSSRule('.rdb-title', style.titlebg, style.titlecol), 37 | getCSSRule('.rdb-sidebar a', style.sidebarbg, style.sidebarcol), 38 | getCSSRule('.rdb-sidebar', style.sidebarbg, style.sidebarcol), 39 | getCSSRule('body', style.boardbg, style.boardcol), 40 | getCSSRule('.rdb-widget-content', style.widgetbg, style.widgetcol) 41 | ].join(''); 42 | 43 | dom.get('head').appendChild(stylesheet); 44 | } 45 | 46 | /** 47 | * Helper method to create CSS Rule. 48 | * 49 | * @param {string} selector Name of the selector. 50 | * @param {string} bg Value of the background style 51 | * @param {string} col Value of the color style 52 | * @returns {string} CSS Rule 53 | */ 54 | function getCSSRule(selector, bg, col){ 55 | return selector + '{ color :' + col + '; background: ' + bg + ';}'; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /app/scripts/helper/rdb-utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Some helper methods we use all over the application. 3 | */ 4 | 5 | module.exports = { 6 | 7 | /** 8 | * Translates a name of a board to the related URL. 9 | * @param {string} boardName Name of the board 10 | * @returns {string} URL of the passed board name. 11 | */ 12 | boardNameToUrl : function(boardName){ 13 | return boardName.toLowerCase().replace(/\s/g,'-'); 14 | }, 15 | 16 | isUndefined: function(obj){ 17 | return typeof obj === 'undefined'; 18 | }, 19 | 20 | /** 21 | * Helper methods to work with the DOM. 22 | */ 23 | dom : { 24 | create: function(element){ 25 | return document.createElement(element); 26 | }, 27 | get : function(selector){ 28 | return document.querySelector(selector); 29 | }, 30 | getAll : function(selector){ 31 | return document.querySelectorAll(selector); 32 | } 33 | 34 | } 35 | 36 | }; -------------------------------------------------------------------------------- /app/scripts/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entry point of RDB. 3 | */ 4 | 5 | var React = require('react'); 6 | var Router = require('react-router'); 7 | var routes = require('./routes.jsx'); 8 | 9 | var style = require('rdbconf').style; 10 | 11 | require('./helper/rdb-styler').applyStyles(style); 12 | 13 | Router.run(routes, function (Handler) { 14 | React.render(, document.body); 15 | }); -------------------------------------------------------------------------------- /app/scripts/mixins/rdb-chart-mixin.js: -------------------------------------------------------------------------------- 1 | var c3 = require('c3'); 2 | 3 | require('../../../node_modules/c3/c3.css'); 4 | 5 | var ChartMixin = { 6 | createChart: function (options) { 7 | // TODO: find a better way to get the keys. 8 | var keys = this.props.keys || Object.keys(this.props.data[0]); 9 | 10 | return c3.generate({ 11 | bindto: '#' + this.props._id, 12 | data: { 13 | json: this.props.data, 14 | type: options.type, 15 | keys: { 16 | value: keys 17 | } 18 | } 19 | }); 20 | } 21 | }; 22 | 23 | module.exports = ChartMixin; -------------------------------------------------------------------------------- /app/scripts/mixins/rdb-widget-mixin.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This Mixin holds functions and properties that are used by all widgets. 3 | * 4 | */ 5 | 6 | var React = require('react'); 7 | 8 | var WidgetMixin = { 9 | 10 | getDefaultProps: function(){ 11 | return { 12 | title : '' 13 | } 14 | }, 15 | 16 | propTypes: { 17 | title : React.PropTypes.string 18 | }, 19 | 20 | getTitle : function(){ 21 | return this.props.title ?
{this.props.title}
: ''; 22 | } 23 | 24 | } 25 | 26 | module.exports = WidgetMixin; -------------------------------------------------------------------------------- /app/scripts/pages/rdb-app.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var RouteHandler = require('react-router').RouteHandler; 3 | var Sidebar = require('../components/rdb-sidebar.jsx') 4 | 5 | var App = React.createClass({ 6 | render: function () { 7 | return ( 8 |
9 | 10 |
11 | 12 |
13 |
14 | ); 15 | } 16 | }); 17 | 18 | module.exports = App; -------------------------------------------------------------------------------- /app/scripts/pages/rdb-errorpage.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var ErrorPage = React.createClass({ 4 | 5 | render: function() { 6 | return ( 7 |
8 |

{ this.props.title }

9 |

{ this.props.text }

10 |
11 | ); 12 | } 13 | 14 | }); 15 | 16 | module.exports = ErrorPage; -------------------------------------------------------------------------------- /app/scripts/routes.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Router = require('react-router'); 3 | var { Route, DefaultRoute, NotFoundRoute } = Router; 4 | 5 | var App = require('./pages/rdb-app.jsx'); 6 | 7 | var ErrorFactory = require('./factories/rdb-errorpage-factory.jsx'); 8 | var NotFound = ErrorFactory.create({title: '404', text : 'Route not found.'}); 9 | var NoBoard = ErrorFactory.create({title: 'No Boards specified.', text : 'Please specify at least one board in the config file.'}); 10 | 11 | var utils = require('rdbutils'); 12 | var boards = require('rdbconf').boards; 13 | var BoardFactory = require('./factories/rdb-board-factory.jsx'); 14 | 15 | if(utils.isUndefined(boards)){ 16 | boards = []; 17 | } 18 | 19 | var defaultRoute = boards && boards.length > 0 ? BoardFactory.create(boards[0]) : NoBoard; 20 | 21 | var dynamicRoutes = boards.map(function(board,i){ 22 | var boardUrl = utils.boardNameToUrl(board.name), 23 | Board = BoardFactory.create(board); 24 | 25 | return ; 26 | }); 27 | 28 | var routes = ( 29 | 30 | { dynamicRoutes } 31 | 32 | 33 | 34 | ); 35 | 36 | module.exports = routes; -------------------------------------------------------------------------------- /app/scripts/stores/itemStore.js: -------------------------------------------------------------------------------- 1 | var Reflux = require('reflux'); 2 | var ItemActions = require('../actions/itemActions'); 3 | 4 | var ItemStore = Reflux.createStore({ 5 | 6 | init : function(){ 7 | this.items = []; 8 | 9 | this.listenTo(ItemActions.loadItems,this.loadItems); 10 | this.listenTo(ItemActions.loadItemsSuccess,this.loadItemsSuccess); 11 | this.listenTo(ItemActions.loadItemsError,this.loadItemsError); 12 | }, 13 | 14 | loadItems: function(){ 15 | this.trigger({ 16 | loading: true 17 | }); 18 | }, 19 | 20 | loadItemsSuccess: function(items){ 21 | this.items = items; 22 | 23 | this.trigger({ 24 | items : this.items, 25 | loading: false 26 | }); 27 | }, 28 | 29 | loadItemsError: function(error){ 30 | this.trigger({ 31 | error : error, 32 | loading: false 33 | }); 34 | } 35 | 36 | }); 37 | 38 | module.exports = ItemStore; -------------------------------------------------------------------------------- /app/scripts/widgets/barchart/index.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ChartMixin = require('../../mixins/rdb-chart-mixin.js'); 3 | var BaseWidget = require('BaseWidget'); 4 | 5 | var Widget = React.createClass({ 6 | mixins: [ChartMixin], 7 | 8 | getDefaultProps: function(){ 9 | return { 10 | data : [{ alpha: 100, beta : 120, gamma: 110 },{ alpha: 120, beta : 110, gamma: 90 },{ alpha: 75, beta : 100,gamma: 80 }] 11 | } 12 | }, 13 | 14 | propTypes: { 15 | data: React.PropTypes.array 16 | }, 17 | 18 | componentDidMount: function () { 19 | this.chart = this.createChart({ type : 'bar' }); 20 | }, 21 | 22 | componentWillUnmount: function(){ 23 | this.chart = this.chart.destroy(); 24 | }, 25 | 26 | render: function() { 27 | 28 | var style = { padding : '1rem' }, 29 | widget = ( 30 |
31 |
32 |
33 |
34 |
); 35 | 36 | return ( 37 | 38 | ); 39 | } 40 | }); 41 | 42 | module.exports = Widget; -------------------------------------------------------------------------------- /app/scripts/widgets/iframe/index.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var BaseWidget = require('BaseWidget'); 3 | 4 | var Widget = React.createClass({ 5 | 6 | getDefaultProps: function(){ 7 | return { 8 | src : 'http://webkid.io' 9 | } 10 | }, 11 | 12 | propTypes: { 13 | src: React.PropTypes.string 14 | }, 15 | 16 | render: function() { 17 | 18 | var style = { width : '100%', height: '100%' }, 19 | widget = ( 20 |
21 | 22 |
); 23 | 24 | return ( 25 | 26 | ); 27 | } 28 | 29 | }); 30 | 31 | module.exports = Widget; -------------------------------------------------------------------------------- /app/scripts/widgets/linechart/index.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ChartMixin = require('../../mixins/rdb-chart-mixin.js'); 3 | var BaseWidget = require('BaseWidget'); 4 | 5 | var Widget = React.createClass({ 6 | mixins: [ChartMixin], 7 | 8 | getDefaultProps: function(){ 9 | return { 10 | data : [{ alpha: 100, beta : 120, gamma: 110 },{ alpha: 120, beta : 110, gamma: 90 },{ alpha: 75, beta : 100,gamma: 80 }, 11 | { alpha: 100, beta : 120, gamma: 110 },{ alpha: 120, beta : 110, gamma: 90 },{ alpha: 75, beta : 100,gamma: 80 }] 12 | } 13 | }, 14 | 15 | propTypes: { 16 | data: React.PropTypes.array 17 | }, 18 | 19 | componentDidMount: function () { 20 | this.chart = this.createChart({ type : 'line' }); 21 | }, 22 | 23 | componentWillUnmount: function(){ 24 | this.chart = this.chart.destroy(); 25 | }, 26 | 27 | render: function() { 28 | 29 | var style = { padding : '1rem' }, 30 | widget = ( 31 |
32 |
33 |
34 |
35 |
); 36 | 37 | return ( 38 | 39 | ); 40 | } 41 | }); 42 | 43 | module.exports = Widget; -------------------------------------------------------------------------------- /app/scripts/widgets/list/index.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var BaseWidget = require('BaseWidget'); 3 | 4 | var Widget = React.createClass({ 5 | 6 | render: function() { 7 | 8 | var items = this.props.data.map(function(item,i){ 9 | return
  • {item}
  • 10 | }),widget = ( 11 |
    12 |
      { items }
    13 |
    ); 14 | 15 | return ( 16 | 17 | ); 18 | } 19 | 20 | }); 21 | 22 | module.exports = Widget; -------------------------------------------------------------------------------- /app/scripts/widgets/map/index.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var BaseWidget = require('BaseWidget'); 3 | 4 | var L = require('leaflet'); 5 | require('../../../../node_modules/leaflet/dist/leaflet.css'); 6 | L.Icon.Default.imagePath = 'images'; 7 | 8 | var Widget = React.createClass({ 9 | getDefaultProps: function(){ 10 | return { 11 | center : [52.52,13.4], 12 | marker : [], 13 | zoomLevel : 10, 14 | wmsTileLayerUrl : 'http://tile.stamen.com/toner/{z}/{x}/{y}.png' 15 | } 16 | }, 17 | 18 | propTypes: { 19 | center: React.PropTypes.array, 20 | marker : React.PropTypes.array, 21 | zoomLevel : React.PropTypes.number, 22 | wmsTileLayerUrl : React.PropTypes.string 23 | }, 24 | 25 | componentDidMount: function(){ 26 | this.zoomControlOptions = { position: 'bottomright' }; 27 | this.mapDefaultOptions = {zoomControl : false}; 28 | 29 | this.createMap(); 30 | }, 31 | 32 | componentWillUnmount: function(){ 33 | this.map.remove(); 34 | }, 35 | 36 | createMap : function(){ 37 | 38 | this.map = L.map(this.props._id, this.mapDefaultOptions) 39 | .setView(this.props.center, this.props.zoomLevel) 40 | .addControl(L.control.zoom(this.zoomControlOptions)) 41 | .addLayer(L.tileLayer(this.props.wmsTileLayerUrl, {maxZoom: 18})) 42 | 43 | this.createMarker(); 44 | }, 45 | 46 | createMarker : function(){ 47 | if(this.props.marker){ 48 | this.props.marker.forEach(function(marker,i){ 49 | var mapMarker = L.marker(marker.latlng, {title: marker.text}).addTo(this.map); 50 | 51 | if(marker.text){ 52 | mapMarker.bindPopup(marker.text) 53 | } 54 | }.bind(this)); 55 | } 56 | }, 57 | 58 | 59 | render: function() { 60 | 61 | var style = { height : '100%' }, 62 | widget = ( 63 |
    64 |
    65 |
    66 | ); 67 | 68 | return ( 69 | 70 | ); 71 | } 72 | 73 | }); 74 | 75 | module.exports = Widget; -------------------------------------------------------------------------------- /app/scripts/widgets/rdb-base-widget.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Widgets can use this class to display their content. 3 | * Usage: 4 | */ 5 | 6 | var React = require('react'); 7 | var WidgetMixin = require('WidgetMixin'); 8 | 9 | var BaseWidget = React.createClass({ 10 | mixins: [WidgetMixin], 11 | 12 | render: function() { 13 | return ( 14 |
    15 |
    16 | { this.getTitle() } 17 | { this.props.widget } 18 |
    19 |
    20 | ); 21 | } 22 | 23 | }); 24 | 25 | module.exports = BaseWidget; -------------------------------------------------------------------------------- /app/scripts/widgets/todos/index.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var BaseWidget = require('BaseWidget'); 3 | 4 | var TodosActionsFactory = require('./todosActions-factory'); 5 | var TodosStoreFactory = require('./todosStore-factory'); 6 | 7 | var Todo = require('./todo.jsx'); 8 | 9 | require('./style.css'); 10 | 11 | var Widget = React.createClass({ 12 | 13 | getInitialState: function(){ 14 | return { 15 | data : [] 16 | } 17 | }, 18 | 19 | componentDidMount: function(){ 20 | 21 | this.TodosActions = TodosActionsFactory.get(this.props._id); 22 | this.TodosStore = TodosStoreFactory.get(this.props._id); 23 | this.unsubscribe = this.TodosStore.listen(this.onStatusChange); 24 | 25 | this.TodosActions.get(this.props.data); 26 | }, 27 | 28 | conponentWillUnmount: function(){ 29 | this.unsubscribe(); 30 | }, 31 | 32 | onStatusChange: function(state){ 33 | this.setState(state); 34 | }, 35 | 36 | add: function(){ 37 | var newTodo = {}; 38 | newTodo.isDone = false; 39 | newTodo.text = 'New Todo'; 40 | newTodo.createdAt = new Date(); 41 | 42 | this.TodosActions.add(newTodo); 43 | }, 44 | 45 | render: function() { 46 | 47 | var style = { width : '100%', height: '100%' }, 48 | todos = this.state.data.map(function(todo,i){ 49 | 50 | todo.actions = this.TodosActions; 51 | return ; 52 | }.bind(this)), 53 | widget = ( 54 |
    55 |
      56 | { todos } 57 |
    58 | 59 |
    Add Todo
    60 |
    ); 61 | 62 | return ( 63 | 64 | ); 65 | } 66 | 67 | }); 68 | 69 | module.exports = Widget; -------------------------------------------------------------------------------- /app/scripts/widgets/todos/style.css: -------------------------------------------------------------------------------- 1 | .rdb-widget-todos ul{ 2 | list-style: none; 3 | margin:0; 4 | padding:0; 5 | } 6 | .rdb-widget-todos ul li{ 7 | display:block; 8 | padding:.5rem 1rem; 9 | border-bottom:1px solid #eee; 10 | cursor:pointer; 11 | } 12 | .rdb-widget-todos ul li.is-done{ 13 | text-decoration: line-through; 14 | } 15 | .rdb-widget-todos ul li:hover{ 16 | background:#eee; 17 | } 18 | .rdb-widget-todos .rdb-widget-todos-add{ 19 | position: absolute; 20 | bottom:0; 21 | text-align: center; 22 | width: 100%; 23 | padding:.5rem 0; 24 | border-top:1px solid #eee; 25 | cursor: pointer; 26 | background:#eee; 27 | user-select:none; 28 | } -------------------------------------------------------------------------------- /app/scripts/widgets/todos/todo.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react/addons'); 2 | var cx = React.addons.classSet; 3 | 4 | 5 | var Todo = React.createClass({ 6 | 7 | toggle : function(){ 8 | this.props['actions'].toggle(this.props); 9 | }, 10 | 11 | render: function(){ 12 | var classes = cx({ 13 | 'is-done': this.props.isDone 14 | }); 15 | 16 | return ( 17 |
  • 18 | { this.props.text } 19 |
  • 20 | ); 21 | } 22 | 23 | }); 24 | 25 | module.exports = Todo; -------------------------------------------------------------------------------- /app/scripts/widgets/todos/todosActions-factory.js: -------------------------------------------------------------------------------- 1 | var Reflux = require('reflux'); 2 | var utils = require('rdbutils'); 3 | var actionsCache = {}; 4 | 5 | module.exports.get = function(id){ 6 | if(!utils.isUndefined(actionsCache[id])){ 7 | return actionsCache[id]; 8 | } 9 | 10 | return create(id); 11 | }; 12 | 13 | function create(id){ 14 | 15 | actionsCache[id] = Reflux.createActions([ 16 | 'get', 17 | 'add', 18 | 'toggle' 19 | ]); 20 | 21 | return actionsCache[id]; 22 | } 23 | -------------------------------------------------------------------------------- /app/scripts/widgets/todos/todosStore-factory.js: -------------------------------------------------------------------------------- 1 | var Reflux = require('reflux'); 2 | var utils = require('rdbutils'); 3 | 4 | var storage = require('store'); 5 | var storeCache = {}; 6 | 7 | var TodosActionsFactory = require('./todosActions-factory'); 8 | 9 | module.exports.get = function(id){ 10 | if(!utils.isUndefined(storeCache[id])){ 11 | return storeCache[id]; 12 | } 13 | 14 | return create(id); 15 | }; 16 | 17 | function create(id){ 18 | 19 | storeCache[id] = Reflux.createStore({ 20 | 21 | init : function(){ 22 | 23 | this.todos = storage.get('rdb-widget-todos-' + id) || []; 24 | var TodosActions = TodosActionsFactory.get(id); 25 | 26 | this.listenTo(TodosActions.get,this.get); 27 | this.listenTo(TodosActions.add,this.add); 28 | this.listenTo(TodosActions.toggle,this.toggle); 29 | }, 30 | 31 | get: function(initialTodos){ 32 | 33 | // merge todos from local storage with passed todos from the config file. 34 | initialTodos.forEach(function(el){ 35 | if(!containsTodo(this.todos, el)){ 36 | 37 | var newTodo = {}; 38 | newTodo.text = el.text; 39 | newTodo.isDone = el.isDone; 40 | newTodo.createdAt = new Date(); 41 | 42 | if(utils.isUndefined(newTodo.isDone)){ 43 | newTodo.isDone = false; 44 | } 45 | 46 | this.todos.push(newTodo); 47 | } 48 | }.bind(this)); 49 | 50 | updateStorage(id, this.todos); 51 | 52 | this.trigger({ 53 | data: this.todos 54 | }); 55 | }, 56 | 57 | add: function(newTodo){ 58 | 59 | this.todos.push(newTodo); 60 | 61 | storage.set('rdb-widget-todos-' + id, this.todos); 62 | 63 | this.trigger({ 64 | data: this.todos 65 | }); 66 | 67 | }, 68 | 69 | toggle: function(todo){ 70 | var toggleTodo = containsTodo(this.todos, todo); 71 | toggleTodo.isDone = !toggleTodo.isDone; 72 | updateStorage(id, this.todos); 73 | 74 | this.trigger({ 75 | data: this.todos 76 | }); 77 | } 78 | 79 | }); 80 | 81 | return storeCache[id]; 82 | } 83 | 84 | 85 | function updateStorage(id, todos){ 86 | storage.set('rdb-widget-todos-' + id, todos); 87 | } 88 | 89 | function containsTodo(list, todo){ 90 | 91 | var itemFound = false; 92 | 93 | for(var i = 0; i < list.length; i++){ 94 | if(list[i].text === todo.text){ 95 | itemFound = list[i]; 96 | break; 97 | } 98 | } 99 | 100 | return itemFound; 101 | } -------------------------------------------------------------------------------- /app/stylus/helper.styl: -------------------------------------------------------------------------------- 1 | /* helper classes */ 2 | 3 | .hidden 4 | display: none !important 5 | visibility: hidden 6 | 7 | .clearfix 8 | zoom: 1 9 | 10 | .clearfix:before 11 | .clearfix:after 12 | content: "" 13 | display: table 14 | 15 | .clearfix:after 16 | clear: both 17 | -------------------------------------------------------------------------------- /app/stylus/main.styl: -------------------------------------------------------------------------------- 1 | @import 'normalize' 2 | @import 'helper' 3 | @import 'widgets' 4 | 5 | /* variables */ 6 | $lightgrey = #999 7 | $linkColor = #555 8 | $grey = #eee 9 | $black = #222 10 | $white = #f7f7f7 11 | 12 | * 13 | box-sizing: border-box 14 | padding: 0 15 | margin: 0 16 | 17 | html 18 | body 19 | .dashboard 20 | height:100% 21 | 22 | html 23 | button 24 | input 25 | select 26 | textarea 27 | color: $black 28 | 29 | a 30 | a:visited 31 | color: $linkColor 32 | text-decoration: none 33 | 34 | a:hover 35 | color: lighten($linkColor,10%) 36 | 37 | body 38 | color: $black 39 | font-size: 1em 40 | line-height: 1.5 41 | 42 | .rdb-errorpage 43 | padding:1rem 44 | 45 | .rdb-sidebar 46 | position:fixed 47 | background: #303030 48 | width:20% 49 | height:100% 50 | 51 | .rdb-title 52 | background:#eee 53 | padding: 1rem 54 | font-size:1.25rem 55 | font-weight:700 56 | 57 | .nav-item 58 | padding: 1rem 59 | display:block 60 | 61 | &.active 62 | color: #f9f9f9 63 | background: #444 64 | 65 | a 66 | a:visited 67 | a:hover 68 | color: #ddd 69 | background: #303030 70 | 71 | 72 | .content 73 | padding-left:20%; 74 | 75 | .rdb-board-title 76 | padding:1rem -------------------------------------------------------------------------------- /app/stylus/normalize.styl: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.0 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html 10 | font-family: sans-serif // 1 11 | -ms-text-size-adjust: 100% // 2 12 | -webkit-text-size-adjust: 100% // 2 13 | 14 | /** 15 | * Remove default margin. 16 | */ 17 | 18 | body 19 | margin: 0 20 | 21 | /* HTML5 display definitions 22 | ========================================================================== */ 23 | 24 | /** 25 | * Correct `block` display not defined in IE 8/9. 26 | */ 27 | 28 | article, 29 | aside, 30 | details, 31 | figcaption, 32 | figure, 33 | footer, 34 | header, 35 | hgroup, 36 | main, 37 | nav, 38 | section, 39 | div, 40 | summary 41 | -webkit-box-sizing: border-box 42 | -moz-box-sizing: border-box 43 | box-sizing: border-box 44 | display: block 45 | 46 | /** 47 | * 1. Correct `inline-block` display not defined in IE 8/9. 48 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 49 | */ 50 | 51 | audio, 52 | canvas, 53 | progress, 54 | video 55 | display: inline-block // 1 56 | vertical-align: baseline // 2 57 | 58 | /** 59 | * Prevent modern browsers from displaying `audio` without controls. 60 | * Remove excess height in iOS 5 devices. 61 | */ 62 | 63 | audio:not([controls]) 64 | display: none 65 | height: 0 66 | 67 | /** 68 | * Address `[hidden]` styling not present in IE 8/9. 69 | * Hide the `template` element in IE, Safari, and Firefox < 22. 70 | */ 71 | 72 | [hidden], 73 | template 74 | display: none 75 | 76 | /* Links 77 | ========================================================================== */ 78 | 79 | /** 80 | * 1. Remove the gray background color from active links in IE 10. 81 | * 2. Improve readability when focused and also mouse hovered in all browsers. 82 | */ 83 | 84 | a 85 | background: transparent // 1 86 | &:active, 87 | &:hover 88 | outline: 0 // 2 89 | 90 | /* Text-level semantics 91 | ========================================================================== */ 92 | 93 | /** 94 | * Address styling not present in IE 8/9, Safari 5, and Chrome. 95 | */ 96 | 97 | abbr[title] 98 | border-bottom: 1px dotted 99 | 100 | /** 101 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 102 | */ 103 | 104 | b, 105 | strong 106 | font-weight: bold 107 | 108 | /** 109 | * Address styling not present in Safari 5 and Chrome. 110 | */ 111 | 112 | dfn 113 | font-style: italic 114 | 115 | /** 116 | * Address variable `h1` font-size and margin within `section` and `article` 117 | * contexts in Firefox 4+, Safari 5, and Chrome. 118 | */ 119 | 120 | h1 121 | font-size: 2em 122 | 123 | /** 124 | * Address styling not present in IE 8/9. 125 | */ 126 | 127 | mark 128 | background: #ff0 129 | color: #000 130 | 131 | /** 132 | * Address inconsistent and variable font size in all browsers. 133 | */ 134 | 135 | small 136 | font-size: 80% 137 | 138 | /** 139 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 140 | */ 141 | 142 | sub, 143 | sup 144 | font-size: 75% 145 | line-height: 0 146 | position: relative 147 | vertical-align: baseline 148 | 149 | sup 150 | top: -0.5em 151 | 152 | sub 153 | bottom: -0.25em 154 | 155 | /* Embedded content 156 | ========================================================================== */ 157 | 158 | /** 159 | * Remove border when inside `a` element in IE 8/9. 160 | */ 161 | 162 | img 163 | border: 0 164 | 165 | /** 166 | * Correct overflow displayed oddly in IE 9. 167 | */ 168 | 169 | svg:not(:root) 170 | overflow: hidden 171 | 172 | /* Grouping content 173 | ========================================================================== */ 174 | 175 | /** 176 | * Address margin not present in IE 8/9 and Safari 5. 177 | */ 178 | 179 | figure 180 | margin: 1em 40px 181 | 182 | /** 183 | * Address differences between Firefox and other browsers. 184 | */ 185 | 186 | hr 187 | -webkit-box-sizing: content-box 188 | -moz-box-sizing: content-box 189 | box-sizing: content-box 190 | height: 0 191 | 192 | /** 193 | * Contain overflow in all browsers. 194 | */ 195 | 196 | pre 197 | overflow: auto 198 | 199 | /** 200 | * Address odd `em`-unit font size rendering in all browsers. 201 | */ 202 | 203 | code, 204 | kbd, 205 | pre, 206 | samp 207 | font-family: monospace, monospace 208 | font-size: 1em 209 | 210 | /* Forms 211 | ========================================================================== */ 212 | 213 | /** 214 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 215 | * styling of `select`, unless a `border` property is set. 216 | */ 217 | 218 | /** 219 | * 1. Correct color not being inherited. 220 | * Known issue: affects color of disabled elements. 221 | * 2. Correct font properties not being inherited. 222 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. 223 | */ 224 | 225 | button, 226 | input, 227 | optgroup, 228 | select, 229 | textarea 230 | color: inherit // 1 231 | font: inherit // 2 232 | margin: 0 // 3 233 | 234 | /** 235 | * Address `overflow` set to `hidden` in IE 8/9/10. 236 | */ 237 | 238 | button 239 | overflow: visible 240 | 241 | /** 242 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 243 | * All other form control elements do not inherit `text-transform` values. 244 | * Correct `button` style inheritance in Firefox, IE 8+, and Opera 245 | * Correct `select` style inheritance in Firefox. 246 | */ 247 | 248 | button, 249 | select 250 | text-transform: none 251 | 252 | /** 253 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 254 | * and `video` controls. 255 | * 2. Correct inability to style clickable `input` types in iOS. 256 | * 3. Improve usability and consistency of cursor style between image-type 257 | * `input` and others. 258 | */ 259 | 260 | button, 261 | html input[type="button"], // 1 262 | input[type="reset"], 263 | input[type="submit"] 264 | -webkit-appearance: button // 2 265 | cursor: pointer // 3 266 | 267 | /** 268 | * Re-set default cursor for disabled elements. 269 | */ 270 | 271 | button[disabled], 272 | html input[disabled] 273 | cursor: default 274 | 275 | /** 276 | * Remove inner padding and border in Firefox 4+. 277 | */ 278 | 279 | button::-moz-focus-inner, 280 | input::-moz-focus-inner 281 | border: 0 282 | padding: 0 283 | 284 | /** 285 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 286 | * the UA stylesheet. 287 | */ 288 | 289 | input 290 | line-height: normal 291 | 292 | /** 293 | * It's recommended that you don't attempt to style these elements. 294 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 295 | * 296 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 297 | * 2. Remove excess padding in IE 8/9/10. 298 | */ 299 | 300 | input[type="checkbox"], 301 | input[type="radio"] 302 | box-sizing: border-box // 1 303 | padding: 0 // 2 304 | 305 | /** 306 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 307 | * `font-size` values of the `input`, it causes the cursor style of the 308 | * decrement button to change from `default` to `text`. 309 | */ 310 | 311 | input[type="number"]::-webkit-inner-spin-button, 312 | input[type="number"]::-webkit-outer-spin-button 313 | height: auto 314 | 315 | /** 316 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 317 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 318 | * (include `-moz` to future-proof). 319 | */ 320 | 321 | input[type="search"] 322 | -webkit-appearance: textfield // 1 323 | -moz-box-sizing: content-box 324 | -webkit-box-sizing: content-box // 2 325 | box-sizing: content-box 326 | 327 | /** 328 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 329 | * Safari (but not Chrome) clips the cancel button when the search input has 330 | * padding (and `textfield` appearance). 331 | */ 332 | 333 | input[type="search"]::-webkit-search-cancel-button, 334 | input[type="search"]::-webkit-search-decoration 335 | -webkit-appearance: none 336 | 337 | /** 338 | * Define consistent border, margin, and padding. 339 | */ 340 | 341 | fieldset 342 | border: 1px solid #c0c0c0 343 | margin: 0 2px 344 | padding: 0.35em 0.625em 0.75em 345 | 346 | /** 347 | * 1. Correct `color` not being inherited in IE 8/9. 348 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 349 | */ 350 | 351 | legend 352 | border: 0 // 1 353 | padding: 0 // 2 354 | 355 | /** 356 | * Remove default vertical scrollbar in IE 8/9. 357 | */ 358 | 359 | textarea 360 | overflow: auto 361 | 362 | /** 363 | * Don't inherit the `font-weight` (applied by a rule above). 364 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 365 | */ 366 | 367 | optgroup 368 | font-weight: bold 369 | 370 | /* Tables 371 | ========================================================================== */ 372 | 373 | /** 374 | * Remove most spacing between table cells. 375 | */ 376 | 377 | table 378 | border-collapse: collapse 379 | border-spacing: 0 380 | 381 | td, 382 | th 383 | padding: 0 -------------------------------------------------------------------------------- /app/stylus/widgets.styl: -------------------------------------------------------------------------------- 1 | .rdb-widget-wrapper 2 | width: 100% 3 | padding:1rem 4 | margin-bottom:1rem 5 | 6 | .rdb-widget-content 7 | border:1px solid #eee 8 | 9 | .rdb-widget-title 10 | padding:.5rem 0 11 | text-align:center 12 | font-weight:bold 13 | border-bottom: 1px solid #eee 14 | 15 | .rdb-widget 16 | height:20rem; 17 | position:relative 18 | 19 | 20 | .rdb-widget-map .map 21 | height:100% 22 | 23 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webkid-react-starterkit", 3 | "version": "0.0.1", 4 | "authors": [ 5 | "@moklick " 6 | ], 7 | "description": "Lightweight starting point for a react web app.", 8 | "main": "index.html", 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "app/bower_components", 15 | "test", 16 | "tests" 17 | ], 18 | "dependencies": { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var path = require('path'); 3 | var $ = require('gulp-load-plugins')(); 4 | var del = require('del'); 5 | // set variable via $ gulp --type production 6 | var environment = $.util.env.type || 'development'; 7 | var isProduction = environment === 'production'; 8 | var webpackConfig = require('./webpack.config.js')[environment]; 9 | 10 | var port = $.util.env.port || 1337; 11 | var app = 'app/'; 12 | var dist = 'dist/'; 13 | 14 | // https://github.com/ai/autoprefixer 15 | var autoprefixerBrowsers = [ 16 | 'ie >= 9', 17 | 'ie_mob >= 10', 18 | 'ff >= 30', 19 | 'chrome >= 34', 20 | 'safari >= 6', 21 | 'opera >= 23', 22 | 'ios >= 6', 23 | 'android >= 4.4', 24 | 'bb >= 10' 25 | ]; 26 | 27 | var jsFilesFilter = $.filter('*.js'); 28 | 29 | gulp.task('scripts', function() { 30 | return gulp.src(webpackConfig.entry) 31 | .pipe($.webpack(webpackConfig)) 32 | 33 | // TODO: we get an error here "Unhandled stream error in pipe." 34 | //.pipe(jsFilesFilter) 35 | //.pipe(isProduction ? $.uglifyjs() : $.util.noop()) 36 | 37 | .pipe(gulp.dest(dist + 'js/')) 38 | .pipe($.size({ title : 'js' })) 39 | .pipe($.connect.reload()); 40 | }); 41 | 42 | // copy html from app to dist 43 | gulp.task('html', function() { 44 | return gulp.src(app + 'index.html') 45 | .pipe(gulp.dest(dist)) 46 | .pipe($.size({ title : 'html' })) 47 | .pipe($.connect.reload()); 48 | }); 49 | 50 | gulp.task('styles',function(cb) { 51 | 52 | // convert stylus to css 53 | return gulp.src(app + 'stylus/main.styl') 54 | .pipe($.stylus({ 55 | // only compress if we are in production 56 | compress: isProduction, 57 | // include 'normal' css into main.css 58 | 'include css' : true 59 | })) 60 | .pipe($.autoprefixer({browsers: autoprefixerBrowsers})) 61 | .pipe(gulp.dest(dist + 'css/')) 62 | .pipe($.size({ title : 'css' })) 63 | .pipe($.connect.reload()); 64 | 65 | }); 66 | 67 | // add livereload on the given port 68 | gulp.task('serve', function() { 69 | $.connect.server({ 70 | root: dist, 71 | port: port, 72 | livereload: { 73 | port: 35729 74 | } 75 | }); 76 | }); 77 | 78 | // copy images 79 | gulp.task('images', function(cb) { 80 | return gulp.src(app + 'images/**/*.{png,jpg,jpeg,gif}') 81 | .pipe($.size({ title : 'images' })) 82 | .pipe(gulp.dest(dist + 'images/')); 83 | }); 84 | 85 | // copy images 86 | gulp.task('leaflet', function(cb) { 87 | return gulp.src('node_modules/leaflet/dist/images/**/*.{png,jpg,jpeg,gif}') 88 | .pipe($.size({ title : 'images' })) 89 | .pipe(gulp.dest(dist + 'images/')); 90 | }); 91 | 92 | // watch styl, html and js file changes 93 | gulp.task('watch', function() { 94 | gulp.watch(app + 'stylus/*.styl', ['styles']); 95 | gulp.watch(app + 'index.html', ['html']); 96 | gulp.watch('rdb.config.js', ['scripts']); 97 | gulp.watch(app + 'scripts/**/*.js', ['scripts']); 98 | gulp.watch(app + 'scripts/**/*.jsx', ['scripts']); 99 | }); 100 | 101 | // remove bundels 102 | gulp.task('clean', function(cb) { 103 | del([dist], cb); 104 | }); 105 | 106 | 107 | // by default build project and then watch files in order to trigger livereload 108 | gulp.task('default', ['build', 'serve', 'watch']); 109 | 110 | // waits until clean is finished then builds the project 111 | gulp.task('build', ['clean'], function(){ 112 | gulp.start(['images','html','scripts','styles','leaflet']); 113 | }); 114 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webkid-react-starterkit", 3 | "version": "0.0.1", 4 | "description": "Lightweight starting point for a react web app.", 5 | "main": "index.html", 6 | "scripts": { 7 | "start": "gulp" 8 | }, 9 | "author": "@moklick ", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "css-loader": "^0.9.1", 13 | "del": "^0.1.3", 14 | "file-loader": "^0.8.1", 15 | "gulp": "^3.8.8", 16 | "gulp-autoprefixer": "^2.0.0", 17 | "gulp-concat": "^2.4.1", 18 | "gulp-connect": "^2.0.6", 19 | "gulp-filter": "^2.0.0", 20 | "gulp-livereload": "^2.1.1", 21 | "gulp-load-plugins": "^0.7.0", 22 | "gulp-size": "^1.1.0", 23 | "gulp-stylus": "^1.3.3", 24 | "gulp-uglifyjs": "^0.4.3", 25 | "gulp-util": "^3.0.1", 26 | "gulp-watch": "^1.0.7", 27 | "gulp-webpack": "^0.4.1", 28 | "style-loader": "^0.8.3", 29 | "url-loader": "^0.5.5" 30 | }, 31 | "dependencies": { 32 | "c3": "^0.4.8", 33 | "jsx-loader": "^0.12.2", 34 | "lodash": "^2.4.1", 35 | "merge": "^1.2.0", 36 | "react": "^0.12.1", 37 | "react-router": "^0.11.4", 38 | "reflux": "^0.2.0", 39 | "shortid": "^2.1.3", 40 | "leaflet": "^0.7.3", 41 | "store": "^1.3.17" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rdb.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'RDB', 3 | style: { 4 | font: 'PT Sans' 5 | }, 6 | boards: [ 7 | { 8 | name: 'site a', 9 | widgets: [ 10 | { type: 'map' }, 11 | { type: 'iframe' } 12 | ] 13 | }, { 14 | name: 'site b', 15 | widgets: [ 16 | { type: 'barchart', properties: { title : 'Bar Chart Widget' } }, 17 | { type: 'linechart' } 18 | ] 19 | }] 20 | }; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('merge'); 3 | 4 | var defaultConfig = { 5 | entry: './app/scripts/main.js', 6 | output: { 7 | path: __dirname, 8 | filename: 'main.js' 9 | }, 10 | module: { 11 | loaders: [ 12 | { 13 | test: /\.jsx?$/, 14 | loader: 'jsx-loader?harmony' 15 | }, 16 | { 17 | test: /\.css$/, 18 | loaders: ['style', 'css'] 19 | }, 20 | { 21 | test: /\.png$/, 22 | loader: 'file-loader' 23 | } 24 | ] 25 | }, 26 | resolve: { 27 | alias: { 28 | rdbconf: path.resolve(__dirname, './rdb.config.js'), 29 | rdbDefault: path.resolve(__dirname, './app/scripts/config/rdb-default.config.js'), 30 | rdbutils: path.resolve(__dirname, 'app/scripts/helper/rdb-utils.js'), 31 | WidgetMixin: path.resolve(__dirname, 'app/scripts/mixins/rdb-widget-mixin.jsx'), 32 | BaseWidget: path.resolve(__dirname, 'app/scripts/widgets/rdb-base-widget.jsx'), 33 | } 34 | } 35 | } 36 | 37 | module.exports.development = merge({ 38 | debug: true, 39 | devtool: 'eval' 40 | }, defaultConfig); 41 | 42 | module.exports.production = merge({ 43 | debug: false 44 | }, defaultConfig); --------------------------------------------------------------------------------