├── .editorconfig
├── .gitignore
├── .jscsrc
├── .jshintrc
├── LICENSE
├── README.md
├── dist
└── .gitkeep
├── favicon.ico
├── frameworks.json
├── index.html
├── package.json
├── src
├── App.js
├── Flux.js
├── actions
│ ├── FavoriteActions.js
│ └── FrameworkActions.js
├── api
│ └── FrameworkApi.js
├── client.js
├── components
│ ├── FrameworkDetail.js
│ ├── FrameworkListElement.js
│ ├── TopBar.js
│ └── handlers
│ │ ├── Detail.js
│ │ ├── List.js
│ │ └── Search.js
├── css
│ ├── main.css
│ ├── ratchet.css
│ └── ratchet.min.css
├── fonts
│ ├── ratchicons.eot
│ ├── ratchicons.svg
│ ├── ratchicons.ttf
│ └── ratchicons.woff
├── stores
│ ├── FrameworkStore.js
│ └── SearchFrameworkStore.js
└── utils
│ ├── ApiUtils.js
│ ├── ArrayUtils.js
│ ├── BaseContentStore.js
│ ├── BaseLinkedListStore.js
│ ├── BaseStore.js
│ └── LocalStorageUtils.js
├── webpack.config.js
└── webpack.config.production.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 | root = true
5 | [*]
6 | # Change these settings to your own preference
7 | indent_style = space
8 | indent_size = 2
9 | # We recommend you to keep these unchanged
10 | end_of_line = lf
11 | charset = utf-8
12 | trim_trailing_whitespace = true
13 | insert_final_newline = true
14 | [*.md]
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | dist/*
4 | !dist/.gitkeep
5 |
--------------------------------------------------------------------------------
/.jscsrc:
--------------------------------------------------------------------------------
1 | {
2 | "esnext": true,
3 | "requireCurlyBraces": ["else", "for", "while", "do", "try", "catch", "case", "default"],
4 | "requireSpaceAfterKeywords": [
5 | "if",
6 | "else",
7 | "for", "while", "do", "switch", "return", "try", "catch"
8 | ],
9 | "requireSpaceBeforeBlockStatements": true,
10 | "requireParenthesesAroundIIFE": true,
11 | "requireSpacesInConditionalExpression": true,
12 | "requireSpacesInFunctionExpression": {
13 | "beforeOpeningRoundBrace": true,
14 | "beforeOpeningCurlyBrace": true
15 | },
16 | "requireSpacesInFunctionDeclaration": {
17 | "beforeOpeningRoundBrace": true,
18 | "beforeOpeningCurlyBrace": true
19 | },
20 | "disallowMultipleVarDecl": "exceptUndefined",
21 | "requireBlocksOnNewline": 1,
22 | "disallowPaddingNewlinesInBlocks": true,
23 | "disallowEmptyBlocks": true,
24 | "disallowSpacesInsideObjectBrackets": "all",
25 | "disallowSpacesInsideArrayBrackets": "all",
26 | "disallowSpacesInsideParentheses": true,
27 | "disallowSpaceAfterObjectKeys": true,
28 | "requireCommaBeforeLineBreak": true,
29 | "disallowSpacesInCallExpression": true,
30 | "requireSpaceBeforeObjectValues": true,
31 | "requireOperatorBeforeLineBreak": [
32 | "?",
33 | "+",
34 | "-",
35 | "/",
36 | "*",
37 | "=",
38 | "==",
39 | "===",
40 | "!=",
41 | "!==",
42 | ">",
43 | ">=",
44 | "<",
45 | "<=",
46 | "||",
47 | "&&"
48 | ],
49 | "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
50 | "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
51 | "requireSpaceBeforeBinaryOperators": [
52 | "?"
53 | , "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="
54 | ],
55 | "requireSpaceAfterBinaryOperators": [
56 | "?"
57 | , "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="
58 | ],
59 | "disallowSpaceAfterBinaryOperators": ["!"],
60 | "disallowImplicitTypeConversion": ["numeric", "boolean", "binary", "string"],
61 | "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties",
62 | "disallowKeywords": ["with"],
63 | "disallowMultipleLineStrings": true,
64 | "disallowMultipleLineBreaks": true,
65 | "validateLineBreaks": "LF",
66 | "validateQuoteMarks": true,
67 | "validateIndentation": 2,
68 | "disallowMixedSpacesAndTabs": true,
69 | "disallowTrailingWhitespace": true,
70 | "disallowTrailingComma": true,
71 | "disallowKeywordsOnNewLine": ["else", "catch"],
72 | "requireLineFeedAtFileEnd": true,
73 | "maximumLineLength": {
74 | "value": 100,
75 | "allowUrlComments": true
76 | },
77 | "requireCapitalizedConstructors": true,
78 | "safeContextKeyword": "_this",
79 | "requireDotNotation": true,
80 | "validateJSDoc": {
81 | "checkParamNames": true,
82 | "checkRedundantParams": true,
83 | "requireParamTypes": true
84 | },
85 | "requireSpaceAfterLineComment": true,
86 | "requirePaddingNewlinesBeforeKeywords": [
87 | "do",
88 | "for",
89 | "if",
90 | "switch",
91 | "try",
92 | "void",
93 | "while",
94 | "with",
95 | "return"
96 | ],
97 | "validateParameterSeparator": ", ",
98 | // "requireLineBreakAfterVariableAssignment": true,
99 | "disallowNewlineBeforeBlockStatements": true,
100 | "excludeFiles": []
101 | }
102 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "immed": true,
8 | "latedef": true,
9 | "newcap": true,
10 | "trailing": true,
11 | "quotmark": "single",
12 | "strict": true,
13 | "multistr": true,
14 | "debug": false,
15 | "forin": true,
16 | "undef": true,
17 | "plusplus": true,
18 | "eqeqeq": true,
19 | "validthis": false,
20 | "unused": true,
21 | "jasmine": true,
22 | "esnext": true
23 | }
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Alessandro Arnodo
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 | # flux-immutable-example
2 |
3 |
4 | **Brought to you by [Alessandro Arnodo](http://alessandro.arnodo.net) [[@vesparny](https://twitter.com/vesparny)]**
5 |
6 | [](https://david-dm.org/vesparny/flux-immutable-example
8 | "Dependency status")
9 |
10 | **This is just my attempt to dig into flux, React, webpack, es6 syntax and immutability**
11 |
12 | It's an app written in few days while learning those technologies.
13 |
14 | #### See a [working demo](http://vesparny.github.io/flux-immutable-example/).
15 |
16 | 
17 |
18 | #### Install dependencies
19 |
20 | ```shell
21 | npm install
22 | ```
23 |
24 | #### Run
25 |
26 | ```shell
27 | npm start
28 | ```
29 |
30 | then open http://localhost:3333
31 |
32 | #### Build for production with Webpack
33 |
34 | ```shell
35 | npm run build
36 | ```
37 |
38 | #### Technology stack
39 |
40 | * [React](http://facebook.github.io/react/), of course
41 | * flux stuff handled by [flummox](https://github.com/acdlite/flummox) by [acdlite](https://github.com/acdlite/) (which is the best flux library I played with)
42 | * Stores classification based on [gaearon](https://github.com/gaearon/flux-react-router-example) example.
43 | * [webpack](https://github.com/webpack/webpack)
44 | * [Immutable.js](https://github.com/facebook/immutable-js)
45 | * [react-router](https://github.com/rackt/react-router)
46 | * [react-immutable-render-mixin](https://www.npmjs.com/package/react-immutable-render-mixin)
47 |
48 | #### Useful links
49 |
50 | * http://stackoverflow.com/questions/23591325/in-flux-architecture-how-do-you-manage-store-lifecycle
51 | * http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html
52 | * http://stackoverflow.com/questions/23931416/react-flux-state-and-stores
53 |
54 |
55 | ### License
56 |
57 | See LICENSE file
58 |
--------------------------------------------------------------------------------
/dist/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vesparny/flux-immutable-example/c25c73ec31533054c877990cbf2fbbe46c96c789/dist/.gitkeep
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vesparny/flux-immutable-example/c25c73ec31533054c877990cbf2fbbe46c96c789/favicon.ico
--------------------------------------------------------------------------------
/frameworks.json:
--------------------------------------------------------------------------------
1 | {
2 | "results": [{
3 | "id": "FadFAdas",
4 | "createdAt": "2015-02-20T13:31:59.519Z",
5 | "description": "AngularJS is a toolset for building the framework most suited to your application development. It is fully extensible and works well with other libraries. Every feature can be modified or replaced to suit your unique development workflow and feature needs",
6 | "image": "https://cloud.githubusercontent.com/assets/82070/6286967/94f8f0ca-b90d-11e4-88e0-4f213058537d.jpg",
7 | "name": "angularjs",
8 | "nameDesc": "AngularJS",
9 | "objectId": "G5bhJdQW9u",
10 | "updatedAt": "2015-02-20T14:23:14.398Z"
11 | }, {
12 | "id": "hjMhjhjm",
13 | "createdAt": "2015-02-20T13:37:51.427Z",
14 | "description": "Ember.js is built for productivity. Designed with developer ergonomics in mind, its friendly APIs help you get your job done—fast.",
15 | "image": "https://cloud.githubusercontent.com/assets/82070/6287020/f5fc2c52-b90d-11e4-9823-4b9887176ac1.png",
16 | "name": "ember",
17 | "nameDesc": "Ember.js",
18 | "objectId": "WXg6HTrpQ8",
19 | "updatedAt": "2015-02-20T14:23:01.291Z"
20 | }, {
21 | "id": "ShkmshKm",
22 | "createdAt": "2015-02-20T13:35:47.372Z",
23 | "description": "Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it's easy to try it out on a small feature in an existing project.",
24 | "image": "https://cloud.githubusercontent.com/assets/82070/6287019/f3db3a3a-b90d-11e4-8df2-fc9fb4420ac4.png",
25 | "name": "react",
26 | "nameDesc": "React",
27 | "objectId": "jLVZHaZPTr",
28 | "updatedAt": "2015-02-20T14:23:07.561Z"
29 | }]
30 | }
31 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | flux-immutable-example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flux-immutable-example",
3 | "repository": "https://github.com/vesparny/flux-immutable-example",
4 | "description": "A trivial example app with flux, flummox, react-router, webpack and Immutable.js",
5 | "version": "0.0.0",
6 | "scripts": {
7 | "start": "npm run dev",
8 | "build": "./node_modules/.bin/httpster & ./node_modules/.bin/webpack --config ./webpack.config.production.js",
9 | "dev": "./node_modules/.bin/httpster & ./node_modules/.bin/webpack --config ./webpack.config.js"
10 | },
11 | "devDependencies": {
12 | "babel-core": "~4.3.0",
13 | "babel-loader": "~4.0.0",
14 | "httpster": "~1.0.1",
15 | "webpack": "~1.5.3"
16 | },
17 | "dependencies": {
18 | "es6-promise": "~2.0.1",
19 | "fastclick": "~1.0.6",
20 | "flummox": "~2.12.4",
21 | "immutable": "~3.6.2",
22 | "object-assign": "~2.0.0",
23 | "react": "~0.12.2",
24 | "react-immutable-render-mixin": "~0.8.0",
25 | "react-router": "~0.12.4",
26 | "superagent": "~0.21.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var { RouteHandler } = require('react-router');
5 | var TopBar = require('./components/TopBar');
6 | var { State } = require('react-router');
7 |
8 | var App = React.createClass({
9 |
10 | mixins: [ State ],
11 |
12 | render() {
13 | return (
14 |
20 | );
21 | }
22 | });
23 | module.exports = App;
24 |
--------------------------------------------------------------------------------
/src/Flux.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var { Flummox } = require('flummox');
4 |
5 | var FrameworkActions = require('./actions/FrameworkActions');
6 | var FavoriteActions = require('./actions/FavoriteActions');
7 |
8 | var FrameworkStore = require('./stores/FrameworkStore');
9 | var SearchFrameworkStore = require('./stores/SearchFrameworkStore');
10 |
11 | class Flux extends Flummox {
12 | constructor() {
13 | super();
14 | this.createActions('framework', FrameworkActions);
15 | this.createActions('favorite', FavoriteActions);
16 |
17 | this.createStore('searchFramework', SearchFrameworkStore, this);
18 | this.createStore('framework', FrameworkStore, this);
19 |
20 | this.on('dispatch', function (payload) {
21 | console.log('dispatching -> ', payload);
22 | });
23 | this.on('error', function (err) {
24 | console.error(err.stack);
25 | });
26 | }
27 | }
28 |
29 | module.exports = Flux;
30 |
--------------------------------------------------------------------------------
/src/actions/FavoriteActions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var { Actions } = require('flummox');
4 | var localStorageUtils = require('../utils/localStorageUtils');
5 |
6 | class FavoriteActions extends Actions {
7 |
8 | addFavorite (id) {
9 | localStorageUtils.push('favorites', id);
10 |
11 | return id;
12 | }
13 |
14 | removeFavorite (id) {
15 | localStorageUtils.pop('favorites', id);
16 |
17 | return id;
18 | }
19 |
20 | }
21 |
22 | module.exports = FavoriteActions;
23 |
--------------------------------------------------------------------------------
/src/actions/FrameworkActions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var { Actions } = require('flummox');
4 | var FrameworkApi = require('../api/FrameworkApi');
5 |
6 | class FrameworkActions extends Actions {
7 |
8 | searchFrameworks(q) {
9 | return FrameworkApi.getFrameworks(q);
10 | }
11 |
12 | newSearch(q) {
13 | return q;
14 | }
15 |
16 | getFrameworkById(id) {
17 | return FrameworkApi.getFrameworkById(id);
18 | }
19 | }
20 |
21 | module.exports = FrameworkActions;
22 |
--------------------------------------------------------------------------------
/src/api/FrameworkApi.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Promise = require('es6-promise').Promise; // jshint ignore:line
4 | var assign = require('object-assign');
5 | var localStorageUtils = require('../utils/localStorageUtils');
6 | var ApiUtils = require('../utils/ApiUtils');
7 |
8 | var FrameworkApi = {
9 | getFrameworks(q = '') {
10 | return new Promise(function (resolve, reject) {
11 | ApiUtils.get('frameworks.json', {q}).then(function (res) {
12 | var favs = localStorageUtils.getAll('favorites');
13 | var results = res.results.filter(framework =>
14 | framework.name.indexOf(q.toLowerCase()) > -1);
15 | resolve({
16 | response: results.map(framework => {
17 | framework.isFavorited = favs.indexOf(framework.id) > -1;
18 |
19 | return assign({id: framework.id}, framework);
20 | }),
21 | query: q
22 | });
23 | }).catch(function (err) {
24 | reject(err);
25 | });
26 | });
27 | },
28 |
29 | getFrameworkById(id) {
30 | return new Promise(function (resolve, reject) {
31 | ApiUtils.get('frameworks.json').then(function (res) {
32 | var favs = localStorageUtils.getAll('favorites');
33 | var framework = res.results.filter(framework => framework.id === id)[0] || {};
34 | var result = {};
35 |
36 | if (framework.id) {
37 | framework.isFavorited = favs.indexOf(framework.id) > -1;
38 | result = assign({id: framework.id}, framework);
39 | }
40 | resolve({
41 | response: result
42 | });
43 | }).catch(function (err) {
44 | reject(err);
45 | });
46 | });
47 | }
48 | };
49 |
50 | module.exports = FrameworkApi;
51 |
--------------------------------------------------------------------------------
/src/client.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var Router = require('react-router');
5 | var { Route, DefaultRoute } = Router;
6 | var App = require('./App');
7 | var List = require('./components/handlers/List');
8 | var Detail = require('./components/handlers/Detail');
9 | var Search = require('./components/handlers/Search');
10 | var flux = new (require('./Flux'))();
11 |
12 | // Expose globally
13 | window.React = React;
14 |
15 | var FastClick = require('fastclick');
16 | FastClick.attach(document.body);
17 |
18 | var routes = (
19 |
20 |
21 |
22 |
23 |
24 | );
25 |
26 | Router.run(routes, function (Handler, state) { // jshint unused:false
27 | React.withContext({flux}, function() {
28 | React.render(, document.getElementById('app'));
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/components/FrameworkDetail.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var { PropTypes } = React;
5 | var ImmutableRenderMixin = require('react-immutable-render-mixin');
6 |
7 | var FrameworkDetailElement = React.createClass({
8 |
9 | mixins: [ImmutableRenderMixin],
10 |
11 | propTypes: {
12 | framework: PropTypes.object.isRequired,
13 | onAddToFavoritesClick: PropTypes.func.isRequired,
14 | onRemoveFromFavoritesClick: PropTypes.func.isRequired
15 | },
16 |
17 | render() {
18 | var framework = this.props.framework.toJS();
19 | return (
20 |
21 |
29 |
30 |
31 |

32 |
{framework.description}
33 |
34 |
35 | {
36 | framework.isFavorited ?
37 |
:
43 |
49 | }
50 |
51 |
52 | );
53 | }
54 |
55 | });
56 |
57 | module.exports = FrameworkDetailElement;
58 |
--------------------------------------------------------------------------------
/src/components/FrameworkListElement.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var { PropTypes } = React;
5 | var { Link } = require('react-router');
6 | var ImmutableRenderMixin = require('react-immutable-render-mixin');
7 |
8 | var FrameworkListElement = React.createClass({
9 |
10 | mixins: [ImmutableRenderMixin],
11 |
12 | propTypes: {
13 | framework: PropTypes.object.isRequired
14 | },
15 |
16 | render() {
17 | var { framework } = this.props;
18 | return (
19 |
20 |
21 |
22 |
23 |
{framework.nameDesc}
24 |
{framework.description.substring(0, 250) + '...'}
25 |
26 |
27 |
28 | );
29 | }
30 | });
31 |
32 | module.exports = FrameworkListElement;
33 |
--------------------------------------------------------------------------------
/src/components/TopBar.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var { PropTypes } = React;
5 | var { Link } = require('react-router');
6 |
7 | var TopBar = React.createClass({
8 |
9 | propTypes: {
10 | pathName: PropTypes.string.isRequired
11 | },
12 |
13 | render() {
14 | var pathName = this.props.pathName;
15 | return (
16 |
17 | {
18 | pathName !== '/' ?
19 | Search :
20 | null
21 | }
22 |
23 |
24 | Flux Immutable Example
25 |
26 | );
27 | }
28 | });
29 |
30 | module.exports = TopBar;
31 |
--------------------------------------------------------------------------------
/src/components/handlers/Detail.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var FluxMixin = require('flummox/mixin');
5 | var FrameworkDetail = require('../FrameworkDetail');
6 |
7 | var { State} = require('react-router');
8 |
9 | //jshint newcap:false
10 | var Detail = React.createClass({
11 | mixins: [State, FluxMixin({
12 | framework:function(store){
13 | return {
14 | framework: store.getById(this.getParams().id),
15 | isLoading: store.isLoading()
16 | };
17 | }
18 | })],
19 |
20 | componentDidMount() {
21 | this.flux.getActions('framework').getFrameworkById(this.getParams().id);
22 | },
23 |
24 | render() {
25 | var { framework, isLoading } = this.state;
26 | if (isLoading){
27 | return Loading...
;
28 | }
29 | if (!framework){
30 | return Nothing to display
;
31 | }
32 | return (
33 |
34 |
38 |
39 | );
40 | },
41 |
42 | handleAddToFavorites (id) {
43 | this.context.flux.getActions('favorite').addFavorite(id);
44 | },
45 |
46 | handleRemoveFromFavorites (id) {
47 | this.context.flux.getActions('favorite').removeFavorite(id);
48 | }
49 | });
50 |
51 | module.exports = Detail;
52 |
--------------------------------------------------------------------------------
/src/components/handlers/List.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var FrameworkListElement = require('../FrameworkListElement');
5 | var FluxMixin = require('flummox/mixin');
6 | var { State} = require('react-router');
7 |
8 | var List = React.createClass({
9 | //jshint newcap:false
10 | mixins: [State, FluxMixin({
11 | searchFramework: function(store){
12 | return {
13 | frameworks: store.getframeworks(this.getQuery().q),
14 | isLoading: store.isLoading()
15 | };
16 | }
17 | })],
18 |
19 | componentDidMount() {
20 | this.flux.getActions('framework').searchFrameworks(this.getQuery().q);
21 | },
22 |
23 | render() {
24 | var { frameworks, isLoading } = this.state;
25 | if (isLoading){
26 | return Loading...
;
27 | }
28 | if (frameworks.size === 0){
29 | return Try another search
;
30 | }
31 | return (
32 |
33 |
34 | {this.state.frameworks.toJS().map(framework =>
35 | )
36 | }
37 |
38 |
39 | );
40 | }
41 | });
42 | module.exports = List;
43 |
--------------------------------------------------------------------------------
/src/components/handlers/Search.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var { Navigation } = require('react-router');
5 | var LinkedStateMixin = require('react/lib/LinkedStateMixin');
6 |
7 | var Search = React.createClass({
8 | mixins: [LinkedStateMixin, Navigation],
9 |
10 | getInitialState() {
11 | return {
12 | search: 'React'
13 | };
14 | },
15 |
16 | render() {
17 | return (
18 |
29 | );
30 | },
31 |
32 | handleSubmit(e) {
33 | e.preventDefault();
34 | this.transitionTo('list', null, {
35 | q: this.state.search
36 | });
37 | }
38 | });
39 | module.exports = Search;
40 |
--------------------------------------------------------------------------------
/src/css/main.css:
--------------------------------------------------------------------------------
1 | .small-img{
2 | height: 80px;
3 | width:80px;
4 | }
5 |
6 | .btn.active, .btn:active:not(.btn-block){
7 | background-color: inherit;
8 | }
9 |
10 | .padding {
11 | padding: 10px;
12 | }
13 |
14 | .list h2 {
15 | margin: 0px 0px 2px;
16 | font-size: 16px;
17 | font-weight: 400;
18 | line-height: 1.2;
19 | }
20 |
21 | .list h3 {
22 | margin: 0px 0px 4px;
23 | font-size: 14px;
24 | line-height: 1.2;
25 | }
26 |
27 | .now {
28 | overflow: hidden;
29 | text-overflow: ellipsis;
30 | white-space: nowrap;
31 | }
32 |
33 |
34 | .table-view-cell > a:not(.btn){
35 | margin: -11px -100px -11px -15px;
36 | }
37 |
38 | .full-image {
39 | width: 100%
40 | }
41 |
42 | .media-body {
43 | padding: 18px;
44 | }
45 |
46 | .fav {
47 | text-align: center;
48 | }
49 |
50 | .detail .table-view-cell {
51 | padding: 11px 15px 11px 15px;
52 | }
53 |
54 | .favLink {
55 | cursor: pointer;
56 | }
57 |
58 | .text-center {
59 | text-align: center;
60 | }
61 |
--------------------------------------------------------------------------------
/src/css/ratchet.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * =====================================================
3 | * Ratchet v2.0.2 (http://goratchet.com)
4 | * Copyright 2014 Connor Sears
5 | * Licensed under MIT (https://github.com/twbs/ratchet/blob/master/LICENSE)
6 | *
7 | * v2.0.2 designed by @connors.
8 | * =====================================================
9 | */
10 |
11 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */
12 | html {
13 | font-family: sans-serif;
14 | -webkit-text-size-adjust: 100%;
15 | -ms-text-size-adjust: 100%;
16 | }
17 |
18 | body {
19 | margin: 0;
20 | }
21 |
22 | article,
23 | aside,
24 | details,
25 | figcaption,
26 | figure,
27 | footer,
28 | header,
29 | hgroup,
30 | main,
31 | nav,
32 | section,
33 | summary {
34 | display: block;
35 | }
36 |
37 | audio,
38 | canvas,
39 | progress,
40 | video {
41 | display: inline-block;
42 | vertical-align: baseline;
43 | }
44 |
45 | audio:not([controls]) {
46 | display: none;
47 | height: 0;
48 | }
49 |
50 | [hidden],
51 | template {
52 | display: none;
53 | }
54 |
55 | a {
56 | background: transparent;
57 | }
58 |
59 | a:active,
60 | a:hover {
61 | outline: 0;
62 | }
63 |
64 | abbr[title] {
65 | border-bottom: 1px dotted;
66 | }
67 |
68 | b,
69 | strong {
70 | font-weight: bold;
71 | }
72 |
73 | dfn {
74 | font-style: italic;
75 | }
76 |
77 | h1 {
78 | margin: .67em 0;
79 | font-size: 2em;
80 | }
81 |
82 | mark {
83 | color: #000;
84 | background: #ff0;
85 | }
86 |
87 | small {
88 | font-size: 80%;
89 | }
90 |
91 | sub,
92 | sup {
93 | position: relative;
94 | font-size: 75%;
95 | line-height: 0;
96 | vertical-align: baseline;
97 | }
98 |
99 | sup {
100 | top: -.5em;
101 | }
102 |
103 | sub {
104 | bottom: -.25em;
105 | }
106 |
107 | img {
108 | border: 0;
109 | }
110 |
111 | svg:not(:root) {
112 | overflow: hidden;
113 | }
114 |
115 | figure {
116 | margin: 1em 40px;
117 | }
118 |
119 | hr {
120 | height: 0;
121 | -moz-box-sizing: content-box;
122 | box-sizing: content-box;
123 | }
124 |
125 | pre {
126 | overflow: auto;
127 | }
128 |
129 | code,
130 | kbd,
131 | pre,
132 | samp {
133 | font-family: monospace, monospace;
134 | font-size: 1em;
135 | }
136 |
137 | button,
138 | input,
139 | optgroup,
140 | select,
141 | textarea {
142 | margin: 0;
143 | font: inherit;
144 | color: inherit;
145 | }
146 |
147 | button {
148 | overflow: visible;
149 | }
150 |
151 | button,
152 | select {
153 | text-transform: none;
154 | }
155 |
156 | button,
157 | html input[type="button"],
158 | input[type="reset"],
159 | input[type="submit"] {
160 | -webkit-appearance: button;
161 | cursor: pointer;
162 | }
163 |
164 | button[disabled],
165 | html input[disabled] {
166 | cursor: default;
167 | }
168 |
169 | button::-moz-focus-inner,
170 | input::-moz-focus-inner {
171 | padding: 0;
172 | border: 0;
173 | }
174 |
175 | input {
176 | line-height: normal;
177 | }
178 |
179 | input[type="checkbox"],
180 | input[type="radio"] {
181 | box-sizing: border-box;
182 | padding: 0;
183 | }
184 |
185 | input[type="number"]::-webkit-inner-spin-button,
186 | input[type="number"]::-webkit-outer-spin-button {
187 | height: auto;
188 | }
189 |
190 | input[type="search"] {
191 | -webkit-box-sizing: content-box;
192 | -moz-box-sizing: content-box;
193 | box-sizing: content-box;
194 | -webkit-appearance: textfield;
195 | }
196 |
197 | input[type="search"]::-webkit-search-cancel-button,
198 | input[type="search"]::-webkit-search-decoration {
199 | -webkit-appearance: none;
200 | }
201 |
202 | fieldset {
203 | padding: .35em .625em .75em;
204 | margin: 0 2px;
205 | border: 1px solid #c0c0c0;
206 | }
207 |
208 | legend {
209 | padding: 0;
210 | border: 0;
211 | }
212 |
213 | textarea {
214 | overflow: auto;
215 | }
216 |
217 | optgroup {
218 | font-weight: bold;
219 | }
220 |
221 | table {
222 | border-spacing: 0;
223 | border-collapse: collapse;
224 | }
225 |
226 | td,
227 | th {
228 | padding: 0;
229 | }
230 |
231 | * {
232 | -webkit-box-sizing: border-box;
233 | -moz-box-sizing: border-box;
234 | box-sizing: border-box;
235 | }
236 |
237 | body {
238 | position: fixed;
239 | top: 0;
240 | right: 0;
241 | bottom: 0;
242 | left: 0;
243 | font-family: "Helvetica Neue", Helvetica, sans-serif;
244 | font-size: 17px;
245 | line-height: 21px;
246 | color: #000;
247 | background-color: #fff;
248 | }
249 |
250 | a {
251 | color: #428bca;
252 | text-decoration: none;
253 |
254 | -webkit-tap-highlight-color: transparent;
255 | }
256 | a:active {
257 | color: #3071a9;
258 | }
259 |
260 | .content {
261 | position: absolute;
262 | top: 0;
263 | right: 0;
264 | bottom: 0;
265 | left: 0;
266 | overflow: auto;
267 | -webkit-overflow-scrolling: touch;
268 | background-color: #fff;
269 | }
270 |
271 | .content > * {
272 | -webkit-transform: translateZ(0);
273 | -ms-transform: translateZ(0);
274 | transform: translateZ(0);
275 | }
276 |
277 | .bar-nav ~ .content {
278 | padding-top: 44px;
279 | }
280 |
281 | .bar-header-secondary ~ .content {
282 | padding-top: 88px;
283 | }
284 |
285 | .bar-footer ~ .content {
286 | padding-bottom: 44px;
287 | }
288 |
289 | .bar-footer-secondary ~ .content {
290 | padding-bottom: 88px;
291 | }
292 |
293 | .bar-tab ~ .content {
294 | padding-bottom: 50px;
295 | }
296 |
297 | .bar-footer-secondary-tab ~ .content {
298 | padding-bottom: 94px;
299 | }
300 |
301 | .content-padded {
302 | margin: 10px;
303 | }
304 |
305 | .pull-left {
306 | float: left;
307 | }
308 |
309 | .pull-right {
310 | float: right;
311 | }
312 |
313 | .clearfix:before, .clearfix:after {
314 | display: table;
315 | content: " ";
316 | }
317 | .clearfix:after {
318 | clear: both;
319 | }
320 |
321 | h1, h2, h3, h4, h5, h6 {
322 | margin-top: 0;
323 | margin-bottom: 10px;
324 | line-height: 1;
325 | }
326 |
327 | h1, .h1 {
328 | font-size: 36px;
329 | }
330 |
331 | h2, .h2 {
332 | font-size: 30px;
333 | }
334 |
335 | h3, .h3 {
336 | font-size: 24px;
337 | }
338 |
339 | h4, .h4 {
340 | font-size: 18px;
341 | }
342 |
343 | h5, .h5 {
344 | margin-top: 20px;
345 | font-size: 14px;
346 | }
347 |
348 | h6, .h6 {
349 | margin-top: 20px;
350 | font-size: 12px;
351 | }
352 |
353 | p {
354 | margin-top: 0;
355 | margin-bottom: 10px;
356 | font-size: 14px;
357 | color: #777;
358 | }
359 |
360 | .btn {
361 | position: relative;
362 | display: inline-block;
363 | padding: 6px 8px 7px;
364 | margin-bottom: 0;
365 | font-size: 12px;
366 | font-weight: 400;
367 | line-height: 1;
368 | color: #333;
369 | text-align: center;
370 | white-space: nowrap;
371 | vertical-align: top;
372 | cursor: pointer;
373 | background-color: white;
374 | border: 1px solid #ccc;
375 | border-radius: 3px;
376 | }
377 | .btn:active, .btn.active {
378 | color: inherit;
379 | background-color: #ccc;
380 | }
381 | .btn:disabled, .btn.disabled {
382 | opacity: .6;
383 | }
384 |
385 | .btn-primary {
386 | color: #fff;
387 | background-color: #428bca;
388 | border: 1px solid #428bca;
389 | }
390 | .btn-primary:active, .btn-primary.active {
391 | color: #fff;
392 | background-color: #3071a9;
393 | border: 1px solid #3071a9;
394 | }
395 |
396 | .btn-positive {
397 | color: #fff;
398 | background-color: #5cb85c;
399 | border: 1px solid #5cb85c;
400 | }
401 | .btn-positive:active, .btn-positive.active {
402 | color: #fff;
403 | background-color: #449d44;
404 | border: 1px solid #449d44;
405 | }
406 |
407 | .btn-negative {
408 | color: #fff;
409 | background-color: #d9534f;
410 | border: 1px solid #d9534f;
411 | }
412 | .btn-negative:active, .btn-negative.active {
413 | color: #fff;
414 | background-color: #c9302c;
415 | border: 1px solid #c9302c;
416 | }
417 |
418 | .btn-outlined {
419 | background-color: transparent;
420 | }
421 | .btn-outlined.btn-primary {
422 | color: #428bca;
423 | }
424 | .btn-outlined.btn-positive {
425 | color: #5cb85c;
426 | }
427 | .btn-outlined.btn-negative {
428 | color: #d9534f;
429 | }
430 | .btn-outlined.btn-primary:active, .btn-outlined.btn-positive:active, .btn-outlined.btn-negative:active {
431 | color: #fff;
432 | }
433 |
434 | .btn-link {
435 | padding-top: 6px;
436 | padding-bottom: 6px;
437 | color: #428bca;
438 | background-color: transparent;
439 | border: 0;
440 | }
441 | .btn-link:active, .btn-link.active {
442 | color: #3071a9;
443 | background-color: transparent;
444 | }
445 |
446 | .btn-block {
447 | display: block;
448 | width: 100%;
449 | padding: 15px 0;
450 | margin-bottom: 10px;
451 | font-size: 18px;
452 | }
453 |
454 | input[type="submit"],
455 | input[type="reset"],
456 | input[type="button"] {
457 | width: 100%;
458 | }
459 |
460 | .btn .badge {
461 | margin: -2px -4px -2px 4px;
462 | font-size: 12px;
463 | background-color: rgba(0, 0, 0, .15);
464 | }
465 |
466 | .btn .badge-inverted,
467 | .btn:active .badge-inverted {
468 | background-color: transparent;
469 | }
470 |
471 | .btn-primary:active .badge-inverted,
472 | .btn-positive:active .badge-inverted,
473 | .btn-negative:active .badge-inverted {
474 | color: #fff;
475 | }
476 |
477 | .btn-block .badge {
478 | position: absolute;
479 | right: 0;
480 | margin-right: 10px;
481 | }
482 |
483 | .btn .icon {
484 | font-size: inherit;
485 | }
486 |
487 | .bar {
488 | position: fixed;
489 | right: 0;
490 | left: 0;
491 | z-index: 10;
492 | height: 44px;
493 | padding-right: 10px;
494 | padding-left: 10px;
495 | background-color: white;
496 | border-bottom: 1px solid #ddd;
497 |
498 | -webkit-backface-visibility: hidden;
499 | backface-visibility: hidden;
500 | }
501 |
502 | .bar-header-secondary {
503 | top: 44px;
504 | }
505 |
506 | .bar-footer {
507 | bottom: 0;
508 | }
509 |
510 | .bar-footer-secondary {
511 | bottom: 44px;
512 | }
513 |
514 | .bar-footer-secondary-tab {
515 | bottom: 50px;
516 | }
517 |
518 | .bar-footer,
519 | .bar-footer-secondary,
520 | .bar-footer-secondary-tab {
521 | border-top: 1px solid #ddd;
522 | border-bottom: 0;
523 | }
524 |
525 | .bar-nav {
526 | top: 0;
527 | }
528 |
529 | .title {
530 | position: absolute;
531 | display: block;
532 | width: 100%;
533 | padding: 0;
534 | margin: 0 -10px;
535 | font-size: 17px;
536 | font-weight: 500;
537 | line-height: 44px;
538 | color: #000;
539 | text-align: center;
540 | white-space: nowrap;
541 | }
542 |
543 | .title a {
544 | color: inherit;
545 | }
546 |
547 | .bar-tab {
548 | bottom: 0;
549 | display: table;
550 | width: 100%;
551 | height: 50px;
552 | padding: 0;
553 | table-layout: fixed;
554 | border-top: 1px solid #ddd;
555 | border-bottom: 0;
556 | }
557 | .bar-tab .tab-item {
558 | display: table-cell;
559 | width: 1%;
560 | height: 50px;
561 | color: #929292;
562 | text-align: center;
563 | vertical-align: middle;
564 | }
565 | .bar-tab .tab-item.active, .bar-tab .tab-item:active {
566 | color: #428bca;
567 | }
568 | .bar-tab .tab-item .icon {
569 | top: 3px;
570 | width: 24px;
571 | height: 24px;
572 | padding-top: 0;
573 | padding-bottom: 0;
574 | }
575 | .bar-tab .tab-item .icon ~ .tab-label {
576 | display: block;
577 | font-size: 11px;
578 | }
579 |
580 | .bar .btn {
581 | position: relative;
582 | top: 7px;
583 | z-index: 20;
584 | padding: 6px 12px 7px;
585 | margin-top: 0;
586 | font-weight: 400;
587 | }
588 | .bar .btn.pull-right {
589 | margin-left: 10px;
590 | }
591 | .bar .btn.pull-left {
592 | margin-right: 10px;
593 | }
594 |
595 | .bar .btn-link {
596 | top: 0;
597 | padding: 0;
598 | font-size: 16px;
599 | line-height: 44px;
600 | color: #428bca;
601 | border: 0;
602 | }
603 | .bar .btn-link:active, .bar .btn-link.active {
604 | color: #3071a9;
605 | }
606 |
607 | .bar .btn-block {
608 | top: 6px;
609 | padding: 7px 0;
610 | margin-bottom: 0;
611 | font-size: 16px;
612 | }
613 |
614 | .bar .btn-nav.pull-left {
615 | margin-left: -5px;
616 | }
617 | .bar .btn-nav.pull-left .icon-left-nav {
618 | margin-right: -3px;
619 | }
620 | .bar .btn-nav.pull-right {
621 | margin-right: -5px;
622 | }
623 | .bar .btn-nav.pull-right .icon-right-nav {
624 | margin-left: -3px;
625 | }
626 |
627 | .bar .icon {
628 | position: relative;
629 | z-index: 20;
630 | padding-top: 10px;
631 | padding-bottom: 10px;
632 | font-size: 24px;
633 | }
634 | .bar .btn .icon {
635 | top: 3px;
636 | padding: 0;
637 | }
638 | .bar .title .icon {
639 | padding: 0;
640 | }
641 | .bar .title .icon.icon-caret {
642 | top: 4px;
643 | margin-left: -5px;
644 | }
645 |
646 | .bar input[type="search"] {
647 | height: 29px;
648 | margin: 6px 0;
649 | }
650 |
651 | .bar .segmented-control {
652 | top: 7px;
653 | margin: 0 auto;
654 | }
655 |
656 | .badge {
657 | display: inline-block;
658 | padding: 2px 9px 3px;
659 | font-size: 12px;
660 | line-height: 1;
661 | color: #333;
662 | background-color: rgba(0, 0, 0, .15);
663 | border-radius: 100px;
664 | }
665 | .badge.badge-inverted {
666 | padding: 0 5px 0 0;
667 | background-color: transparent;
668 | }
669 |
670 | .badge-primary {
671 | color: #fff;
672 | background-color: #428bca;
673 | }
674 | .badge-primary.badge-inverted {
675 | color: #428bca;
676 | }
677 |
678 | .badge-positive {
679 | color: #fff;
680 | background-color: #5cb85c;
681 | }
682 | .badge-positive.badge-inverted {
683 | color: #5cb85c;
684 | }
685 |
686 | .badge-negative {
687 | color: #fff;
688 | background-color: #d9534f;
689 | }
690 | .badge-negative.badge-inverted {
691 | color: #d9534f;
692 | }
693 |
694 | .card {
695 | margin: 10px;
696 | overflow: hidden;
697 | background-color: white;
698 | border: 1px solid #ddd;
699 | border-radius: 6px;
700 | }
701 |
702 | .card .table-view {
703 | margin-bottom: 0;
704 | border-top: 0;
705 | border-bottom: 0;
706 | }
707 | .card .table-view .table-view-divider:first-child {
708 | top: 0;
709 | border-top-left-radius: 6px;
710 | border-top-right-radius: 6px;
711 | }
712 | .card .table-view .table-view-divider:last-child {
713 | border-bottom-right-radius: 6px;
714 | border-bottom-left-radius: 6px;
715 | }
716 |
717 | .card .table-view-cell:last-child {
718 | border-bottom: 0;
719 | }
720 |
721 | .table-view {
722 | padding-left: 0;
723 | margin-top: 0;
724 | margin-bottom: 15px;
725 | list-style: none;
726 | background-color: #fff;
727 | border-top: 1px solid #ddd;
728 | border-bottom: 1px solid #ddd;
729 | }
730 |
731 | .table-view-cell {
732 | position: relative;
733 | padding: 11px 65px 11px 15px;
734 | overflow: hidden;
735 | border-bottom: 1px solid #ddd;
736 | }
737 | .table-view-cell:last-child {
738 | border-bottom: 0;
739 | }
740 | .table-view-cell > a:not(.btn) {
741 | position: relative;
742 | display: block;
743 | padding: inherit;
744 | margin: -11px -65px -11px -15px;
745 | overflow: hidden;
746 | color: inherit;
747 | }
748 | .table-view-cell > a:not(.btn):active {
749 | background-color: #eee;
750 | }
751 | .table-view-cell p {
752 | margin-bottom: 0;
753 | }
754 |
755 | .table-view-divider {
756 | padding-top: 6px;
757 | padding-bottom: 6px;
758 | padding-left: 15px;
759 | margin-top: -1px;
760 | margin-left: 0;
761 | font-weight: 500;
762 | color: #999;
763 | background-color: #fafafa;
764 | border-top: 1px solid #ddd;
765 | border-bottom: 1px solid #ddd;
766 | }
767 |
768 | .table-view .media,
769 | .table-view .media-body {
770 | overflow: hidden;
771 | }
772 |
773 | .table-view .media-object.pull-left {
774 | margin-right: 10px;
775 | }
776 | .table-view .media-object.pull-right {
777 | margin-left: 10px;
778 | }
779 |
780 | .table-view-cell > .btn,
781 | .table-view-cell > .badge,
782 | .table-view-cell > .toggle,
783 | .table-view-cell > a > .btn,
784 | .table-view-cell > a > .badge,
785 | .table-view-cell > a > .toggle {
786 | position: absolute;
787 | top: 50%;
788 | right: 15px;
789 | -webkit-transform: translateY(-50%);
790 | -ms-transform: translateY(-50%);
791 | transform: translateY(-50%);
792 | }
793 | .table-view-cell .navigate-left > .btn,
794 | .table-view-cell .navigate-left > .badge,
795 | .table-view-cell .navigate-left > .toggle,
796 | .table-view-cell .navigate-right > .btn,
797 | .table-view-cell .navigate-right > .badge,
798 | .table-view-cell .navigate-right > .toggle,
799 | .table-view-cell .push-left > .btn,
800 | .table-view-cell .push-left > .badge,
801 | .table-view-cell .push-left > .toggle,
802 | .table-view-cell .push-right > .btn,
803 | .table-view-cell .push-right > .badge,
804 | .table-view-cell .push-right > .toggle,
805 | .table-view-cell > a .navigate-left > .btn,
806 | .table-view-cell > a .navigate-left > .badge,
807 | .table-view-cell > a .navigate-left > .toggle,
808 | .table-view-cell > a .navigate-right > .btn,
809 | .table-view-cell > a .navigate-right > .badge,
810 | .table-view-cell > a .navigate-right > .toggle,
811 | .table-view-cell > a .push-left > .btn,
812 | .table-view-cell > a .push-left > .badge,
813 | .table-view-cell > a .push-left > .toggle,
814 | .table-view-cell > a .push-right > .btn,
815 | .table-view-cell > a .push-right > .badge,
816 | .table-view-cell > a .push-right > .toggle {
817 | right: 35px;
818 | }
819 |
820 | .content > .table-view:first-child {
821 | margin-top: 15px;
822 | }
823 |
824 | input,
825 | textarea,
826 | button,
827 | select {
828 | font-family: "Helvetica Neue", Helvetica, sans-serif;
829 | font-size: 17px;
830 | }
831 |
832 | select,
833 | textarea,
834 | input[type="text"],
835 | input[type="search"],
836 | input[type="password"],
837 | input[type="datetime"],
838 | input[type="datetime-local"],
839 | input[type="date"],
840 | input[type="month"],
841 | input[type="time"],
842 | input[type="week"],
843 | input[type="number"],
844 | input[type="email"],
845 | input[type="url"],
846 | input[type="tel"],
847 | input[type="color"] {
848 | width: 100%;
849 | height: 35px;
850 | -webkit-appearance: none;
851 | padding: 0 15px;
852 | margin-bottom: 15px;
853 | line-height: 21px;
854 | background-color: #fff;
855 | border: 1px solid #ddd;
856 | border-radius: 3px;
857 | outline: none;
858 | }
859 |
860 | input[type="search"] {
861 | -webkit-box-sizing: border-box;
862 | -moz-box-sizing: border-box;
863 | box-sizing: border-box;
864 | padding: 0 10px;
865 | font-size: 16px;
866 | border-radius: 20px;
867 | }
868 |
869 | input[type="search"]:focus {
870 | text-align: left;
871 | }
872 |
873 | textarea {
874 | height: auto;
875 | }
876 |
877 | select {
878 | height: auto;
879 | font-size: 14px;
880 | background-color: #f8f8f8;
881 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .1);
882 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .1);
883 | }
884 |
885 | .input-group {
886 | background-color: #fff;
887 | }
888 |
889 | .input-group input,
890 | .input-group textarea {
891 | margin-bottom: 0;
892 | background-color: transparent;
893 | border-top: 0;
894 | border-right: 0;
895 | border-left: 0;
896 | border-radius: 0;
897 | -webkit-box-shadow: none;
898 | box-shadow: none;
899 | }
900 |
901 | .input-row {
902 | height: 35px;
903 | overflow: hidden;
904 | border-bottom: 1px solid #ddd;
905 | }
906 |
907 | .input-row label {
908 | float: left;
909 | width: 35%;
910 | padding: 8px 15px;
911 | font-family: "Helvetica Neue", Helvetica, sans-serif;
912 | line-height: 1.1;
913 | }
914 |
915 | .input-row input {
916 | float: right;
917 | width: 65%;
918 | padding-left: 0;
919 | margin-bottom: 0;
920 | border: 0;
921 | }
922 |
923 | .segmented-control {
924 | position: relative;
925 | display: table;
926 | overflow: hidden;
927 | font-size: 12px;
928 | font-weight: 400;
929 | background-color: white;
930 | border: 1px solid #ccc;
931 | border-radius: 3px;
932 | }
933 | .segmented-control .control-item {
934 | display: table-cell;
935 | width: 1%;
936 | padding-top: 6px;
937 | padding-bottom: 7px;
938 | overflow: hidden;
939 | line-height: 1;
940 | color: #333;
941 | text-align: center;
942 | text-overflow: ellipsis;
943 | white-space: nowrap;
944 | border-left: 1px solid #ccc;
945 | }
946 | .segmented-control .control-item:first-child {
947 | border-left-width: 0;
948 | }
949 | .segmented-control .control-item:active {
950 | background-color: #eee;
951 | }
952 | .segmented-control .control-item.active {
953 | background-color: #ccc;
954 | }
955 |
956 | .segmented-control-primary {
957 | border-color: #428bca;
958 | }
959 | .segmented-control-primary .control-item {
960 | color: #428bca;
961 | border-color: inherit;
962 | }
963 | .segmented-control-primary .control-item:active {
964 | background-color: #cde1f1;
965 | }
966 | .segmented-control-primary .control-item.active {
967 | color: #fff;
968 | background-color: #428bca;
969 | }
970 |
971 | .segmented-control-positive {
972 | border-color: #5cb85c;
973 | }
974 | .segmented-control-positive .control-item {
975 | color: #5cb85c;
976 | border-color: inherit;
977 | }
978 | .segmented-control-positive .control-item:active {
979 | background-color: #d8eed8;
980 | }
981 | .segmented-control-positive .control-item.active {
982 | color: #fff;
983 | background-color: #5cb85c;
984 | }
985 |
986 | .segmented-control-negative {
987 | border-color: #d9534f;
988 | }
989 | .segmented-control-negative .control-item {
990 | color: #d9534f;
991 | border-color: inherit;
992 | }
993 | .segmented-control-negative .control-item:active {
994 | background-color: #f9e2e2;
995 | }
996 | .segmented-control-negative .control-item.active {
997 | color: #fff;
998 | background-color: #d9534f;
999 | }
1000 |
1001 | .control-content {
1002 | display: none;
1003 | }
1004 | .control-content.active {
1005 | display: block;
1006 | }
1007 |
1008 | .popover {
1009 | position: fixed;
1010 | top: 55px;
1011 | left: 50%;
1012 | z-index: 20;
1013 | display: none;
1014 | width: 280px;
1015 | margin-left: -140px;
1016 | background-color: white;
1017 | border-radius: 6px;
1018 | -webkit-box-shadow: 0 0 15px rgba(0, 0, 0, .1);
1019 | box-shadow: 0 0 15px rgba(0, 0, 0, .1);
1020 | opacity: 0;
1021 | -webkit-transition: all .25s linear;
1022 | -moz-transition: all .25s linear;
1023 | transition: all .25s linear;
1024 | -webkit-transform: translate3d(0, -15px, 0);
1025 | -ms-transform: translate3d(0, -15px, 0);
1026 | transform: translate3d(0, -15px, 0);
1027 | }
1028 | .popover:before {
1029 | position: absolute;
1030 | top: -15px;
1031 | left: 50%;
1032 | width: 0;
1033 | height: 0;
1034 | margin-left: -15px;
1035 | content: '';
1036 | border-right: 15px solid transparent;
1037 | border-bottom: 15px solid white;
1038 | border-left: 15px solid transparent;
1039 | }
1040 | .popover.visible {
1041 | opacity: 1;
1042 | -webkit-transform: translate3d(0, 0, 0);
1043 | -ms-transform: translate3d(0, 0, 0);
1044 | transform: translate3d(0, 0, 0);
1045 | }
1046 | .popover .bar ~ .table-view {
1047 | padding-top: 44px;
1048 | }
1049 |
1050 | .backdrop {
1051 | position: fixed;
1052 | top: 0;
1053 | right: 0;
1054 | bottom: 0;
1055 | left: 0;
1056 | z-index: 15;
1057 | background-color: rgba(0, 0, 0, .3);
1058 | }
1059 |
1060 | .popover .btn-block {
1061 | margin-bottom: 5px;
1062 | }
1063 | .popover .btn-block:last-child {
1064 | margin-bottom: 0;
1065 | }
1066 |
1067 | .popover .bar-nav {
1068 | border-bottom: 1px solid #ddd;
1069 | border-top-left-radius: 12px;
1070 | border-top-right-radius: 12px;
1071 | -webkit-box-shadow: none;
1072 | box-shadow: none;
1073 | }
1074 |
1075 | .popover .table-view {
1076 | max-height: 300px;
1077 | margin-bottom: 0;
1078 | overflow: auto;
1079 | -webkit-overflow-scrolling: touch;
1080 | background-color: #fff;
1081 | border-top: 0;
1082 | border-bottom: 0;
1083 | border-radius: 6px;
1084 | }
1085 |
1086 | .modal {
1087 | position: fixed;
1088 | top: 0;
1089 | z-index: 11;
1090 | width: 100%;
1091 | min-height: 100%;
1092 | overflow: hidden;
1093 | background-color: #fff;
1094 | opacity: 0;
1095 | -webkit-transition: -webkit-transform .25s, opacity 1ms .25s;
1096 | -moz-transition: -moz-transform .25s, opacity 1ms .25s;
1097 | transition: transform .25s, opacity 1ms .25s;
1098 | -webkit-transform: translate3d(0, 100%, 0);
1099 | -ms-transform: translate3d(0, 100%, 0);
1100 | transform: translate3d(0, 100%, 0);
1101 | }
1102 | .modal.active {
1103 | height: 100%;
1104 | opacity: 1;
1105 | -webkit-transition: -webkit-transform .25s;
1106 | -moz-transition: -moz-transform .25s;
1107 | transition: transform .25s;
1108 | -webkit-transform: translate3d(0, 0, 0);
1109 | -ms-transform: translate3d(0, 0, 0);
1110 | transform: translate3d(0, 0, 0);
1111 | }
1112 |
1113 | .slider {
1114 | width: 100%;
1115 | }
1116 |
1117 | .slider {
1118 | overflow: hidden;
1119 | background-color: #000;
1120 | }
1121 | .slider .slide-group {
1122 | position: relative;
1123 | font-size: 0;
1124 | white-space: nowrap;
1125 | -webkit-transition: all 0s linear;
1126 | -moz-transition: all 0s linear;
1127 | transition: all 0s linear;
1128 | }
1129 | .slider .slide-group .slide {
1130 | display: inline-block;
1131 | width: 100%;
1132 | height: 100%;
1133 | font-size: 14px;
1134 | vertical-align: top;
1135 | }
1136 |
1137 | .toggle {
1138 | position: relative;
1139 | display: block;
1140 | width: 74px;
1141 | height: 30px;
1142 | background-color: #fff;
1143 | border: 2px solid #ddd;
1144 | border-radius: 20px;
1145 | -webkit-transition-duration: .2s;
1146 | -moz-transition-duration: .2s;
1147 | transition-duration: .2s;
1148 | -webkit-transition-property: background-color, border;
1149 | -moz-transition-property: background-color, border;
1150 | transition-property: background-color, border;
1151 | }
1152 | .toggle .toggle-handle {
1153 | position: absolute;
1154 | top: -1px;
1155 | left: -1px;
1156 | z-index: 2;
1157 | width: 28px;
1158 | height: 28px;
1159 | background-color: #fff;
1160 | border: 1px solid #ddd;
1161 | border-radius: 100px;
1162 | -webkit-transition-duration: .2s;
1163 | -moz-transition-duration: .2s;
1164 | transition-duration: .2s;
1165 | -webkit-transition-property: -webkit-transform, border, width;
1166 | -moz-transition-property: -moz-transform, border, width;
1167 | transition-property: transform, border, width;
1168 | }
1169 | .toggle:before {
1170 | position: absolute;
1171 | top: 3px;
1172 | right: 11px;
1173 | font-size: 13px;
1174 | color: #999;
1175 | text-transform: uppercase;
1176 | content: "Off";
1177 | }
1178 | .toggle.active {
1179 | background-color: #5cb85c;
1180 | border: 2px solid #5cb85c;
1181 | }
1182 | .toggle.active .toggle-handle {
1183 | border-color: #5cb85c;
1184 | -webkit-transform: translate3d(44px, 0, 0);
1185 | -ms-transform: translate3d(44px, 0, 0);
1186 | transform: translate3d(44px, 0, 0);
1187 | }
1188 | .toggle.active:before {
1189 | right: auto;
1190 | left: 15px;
1191 | color: #fff;
1192 | content: "On";
1193 | }
1194 | .toggle input[type="checkbox"] {
1195 | display: none;
1196 | }
1197 |
1198 | .content.fade {
1199 | left: 0;
1200 | opacity: 0;
1201 | }
1202 | .content.fade.in {
1203 | opacity: 1;
1204 | }
1205 | .content.sliding {
1206 | z-index: 2;
1207 | -webkit-transition: -webkit-transform .4s;
1208 | -moz-transition: -moz-transform .4s;
1209 | transition: transform .4s;
1210 | -webkit-transform: translate3d(0, 0, 0);
1211 | -ms-transform: translate3d(0, 0, 0);
1212 | transform: translate3d(0, 0, 0);
1213 | }
1214 | .content.sliding.left {
1215 | z-index: 1;
1216 | -webkit-transform: translate3d(-100%, 0, 0);
1217 | -ms-transform: translate3d(-100%, 0, 0);
1218 | transform: translate3d(-100%, 0, 0);
1219 | }
1220 | .content.sliding.right {
1221 | z-index: 3;
1222 | -webkit-transform: translate3d(100%, 0, 0);
1223 | -ms-transform: translate3d(100%, 0, 0);
1224 | transform: translate3d(100%, 0, 0);
1225 | }
1226 |
1227 | .navigate-left:after,
1228 | .navigate-right:after,
1229 | .push-left:after,
1230 | .push-right:after {
1231 | position: absolute;
1232 | top: 50%;
1233 | display: inline-block;
1234 | font-family: Ratchicons;
1235 | font-size: inherit;
1236 | line-height: 1;
1237 | color: #bbb;
1238 | text-decoration: none;
1239 | -webkit-transform: translateY(-50%);
1240 | -ms-transform: translateY(-50%);
1241 | transform: translateY(-50%);
1242 |
1243 | -webkit-font-smoothing: antialiased;
1244 | }
1245 |
1246 | .navigate-left:after,
1247 | .push-left:after {
1248 | left: 15px;
1249 | content: '\e822';
1250 | }
1251 |
1252 | .navigate-right:after,
1253 | .push-right:after {
1254 | right: 15px;
1255 | content: '\e826';
1256 | }
1257 |
1258 | @font-face {
1259 | font-family: Ratchicons;
1260 | font-style: normal;
1261 | font-weight: normal;
1262 |
1263 | src: url("../fonts/ratchicons.eot");
1264 | src: url("../fonts/ratchicons.eot?#iefix") format("embedded-opentype"), url("../fonts/ratchicons.woff") format("woff"), url("../fonts/ratchicons.ttf") format("truetype"), url("../fonts/ratchicons.svg#svgFontName") format("svg");
1265 | }
1266 | .icon {
1267 | display: inline-block;
1268 | font-family: Ratchicons;
1269 | font-size: 24px;
1270 | line-height: 1;
1271 | text-decoration: none;
1272 |
1273 | -webkit-font-smoothing: antialiased;
1274 | }
1275 |
1276 | .icon-back:before {
1277 | content: '\e80a';
1278 | }
1279 |
1280 | .icon-bars:before {
1281 | content: '\e80e';
1282 | }
1283 |
1284 | .icon-caret:before {
1285 | content: '\e80f';
1286 | }
1287 |
1288 | .icon-check:before {
1289 | content: '\e810';
1290 | }
1291 |
1292 | .icon-close:before {
1293 | content: '\e811';
1294 | }
1295 |
1296 | .icon-code:before {
1297 | content: '\e812';
1298 | }
1299 |
1300 | .icon-compose:before {
1301 | content: '\e813';
1302 | }
1303 |
1304 | .icon-download:before {
1305 | content: '\e815';
1306 | }
1307 |
1308 | .icon-edit:before {
1309 | content: '\e829';
1310 | }
1311 |
1312 | .icon-forward:before {
1313 | content: '\e82a';
1314 | }
1315 |
1316 | .icon-gear:before {
1317 | content: '\e821';
1318 | }
1319 |
1320 | .icon-home:before {
1321 | content: '\e82b';
1322 | }
1323 |
1324 | .icon-info:before {
1325 | content: '\e82c';
1326 | }
1327 |
1328 | .icon-list:before {
1329 | content: '\e823';
1330 | }
1331 |
1332 | .icon-more-vertical:before {
1333 | content: '\e82e';
1334 | }
1335 |
1336 | .icon-more:before {
1337 | content: '\e82f';
1338 | }
1339 |
1340 | .icon-pages:before {
1341 | content: '\e824';
1342 | }
1343 |
1344 | .icon-pause:before {
1345 | content: '\e830';
1346 | }
1347 |
1348 | .icon-person:before {
1349 | content: '\e832';
1350 | }
1351 |
1352 | .icon-play:before {
1353 | content: '\e816';
1354 | }
1355 |
1356 | .icon-plus:before {
1357 | content: '\e817';
1358 | }
1359 |
1360 | .icon-refresh:before {
1361 | content: '\e825';
1362 | }
1363 |
1364 | .icon-search:before {
1365 | content: '\e819';
1366 | }
1367 |
1368 | .icon-share:before {
1369 | content: '\e81a';
1370 | }
1371 |
1372 | .icon-sound:before {
1373 | content: '\e827';
1374 | }
1375 |
1376 | .icon-sound2:before {
1377 | content: '\e828';
1378 | }
1379 |
1380 | .icon-sound3:before {
1381 | content: '\e80b';
1382 | }
1383 |
1384 | .icon-sound4:before {
1385 | content: '\e80c';
1386 | }
1387 |
1388 | .icon-star-filled:before {
1389 | content: '\e81b';
1390 | }
1391 |
1392 | .icon-star:before {
1393 | content: '\e81c';
1394 | }
1395 |
1396 | .icon-stop:before {
1397 | content: '\e81d';
1398 | }
1399 |
1400 | .icon-trash:before {
1401 | content: '\e81e';
1402 | }
1403 |
1404 | .icon-up-nav:before {
1405 | content: '\e81f';
1406 | }
1407 |
1408 | .icon-up:before {
1409 | content: '\e80d';
1410 | }
1411 |
1412 | .icon-right-nav:before {
1413 | content: '\e818';
1414 | }
1415 |
1416 | .icon-right:before {
1417 | content: '\e826';
1418 | }
1419 |
1420 | .icon-down-nav:before {
1421 | content: '\e814';
1422 | }
1423 |
1424 | .icon-down:before {
1425 | content: '\e820';
1426 | }
1427 |
1428 | .icon-left-nav:before {
1429 | content: '\e82d';
1430 | }
1431 |
1432 | .icon-left:before {
1433 | content: '\e822';
1434 | }
1435 |
--------------------------------------------------------------------------------
/src/css/ratchet.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * =====================================================
3 | * Ratchet v2.0.2 (http://goratchet.com)
4 | * Copyright 2014 Connor Sears
5 | * Licensed under MIT (https://github.com/twbs/ratchet/blob/master/LICENSE)
6 | *
7 | * v2.0.2 designed by @connors.
8 | * =====================================================
9 | *//*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{position:fixed;top:0;right:0;bottom:0;left:0;font-family:"Helvetica Neue",Helvetica,sans-serif;font-size:17px;line-height:21px;color:#000;background-color:#fff}a{color:#428bca;text-decoration:none;-webkit-tap-highlight-color:transparent}a:active{color:#3071a9}.content{position:absolute;top:0;right:0;bottom:0;left:0;overflow:auto;-webkit-overflow-scrolling:touch;background-color:#fff}.content>*{-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}.bar-nav~.content{padding-top:44px}.bar-header-secondary~.content{padding-top:88px}.bar-footer~.content{padding-bottom:44px}.bar-footer-secondary~.content{padding-bottom:88px}.bar-tab~.content{padding-bottom:50px}.bar-footer-secondary-tab~.content{padding-bottom:94px}.content-padded{margin:10px}.pull-left{float:left}.pull-right{float:right}.clearfix:after,.clearfix:before{display:table;content:" "}.clearfix:after{clear:both}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:10px;line-height:1}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{margin-top:20px;font-size:14px}.h6,h6{margin-top:20px;font-size:12px}p{margin-top:0;margin-bottom:10px;font-size:14px;color:#777}.btn{position:relative;display:inline-block;padding:6px 8px 7px;margin-bottom:0;font-size:12px;font-weight:400;line-height:1;color:#333;text-align:center;white-space:nowrap;vertical-align:top;cursor:pointer;background-color:#fff;border:1px solid #ccc;border-radius:3px}.btn.active,.btn:active{color:inherit;background-color:#ccc}.btn.disabled,.btn:disabled{opacity:.6}.btn-primary{color:#fff;background-color:#428bca;border:1px solid #428bca}.btn-primary.active,.btn-primary:active{color:#fff;background-color:#3071a9;border:1px solid #3071a9}.btn-positive{color:#fff;background-color:#5cb85c;border:1px solid #5cb85c}.btn-positive.active,.btn-positive:active{color:#fff;background-color:#449d44;border:1px solid #449d44}.btn-negative{color:#fff;background-color:#d9534f;border:1px solid #d9534f}.btn-negative.active,.btn-negative:active{color:#fff;background-color:#c9302c;border:1px solid #c9302c}.btn-outlined{background-color:transparent}.btn-outlined.btn-primary{color:#428bca}.btn-outlined.btn-positive{color:#5cb85c}.btn-outlined.btn-negative{color:#d9534f}.btn-outlined.btn-negative:active,.btn-outlined.btn-positive:active,.btn-outlined.btn-primary:active{color:#fff}.btn-link{padding-top:6px;padding-bottom:6px;color:#428bca;background-color:transparent;border:0}.btn-link.active,.btn-link:active{color:#3071a9;background-color:transparent}.btn-block{display:block;width:100%;padding:15px 0;margin-bottom:10px;font-size:18px}input[type=button],input[type=reset],input[type=submit]{width:100%}.btn .badge{margin:-2px -4px -2px 4px;font-size:12px;background-color:rgba(0,0,0,.15)}.btn .badge-inverted,.btn:active .badge-inverted{background-color:transparent}.btn-negative:active .badge-inverted,.btn-positive:active .badge-inverted,.btn-primary:active .badge-inverted{color:#fff}.btn-block .badge{position:absolute;right:0;margin-right:10px}.btn .icon{font-size:inherit}.bar{position:fixed;right:0;left:0;z-index:10;height:44px;padding-right:10px;padding-left:10px;background-color:#fff;border-bottom:1px solid #ddd;-webkit-backface-visibility:hidden;backface-visibility:hidden}.bar-header-secondary{top:44px}.bar-footer{bottom:0}.bar-footer-secondary{bottom:44px}.bar-footer-secondary-tab{bottom:50px}.bar-footer,.bar-footer-secondary,.bar-footer-secondary-tab{border-top:1px solid #ddd;border-bottom:0}.bar-nav{top:0}.title{position:absolute;display:block;width:100%;padding:0;margin:0 -10px;font-size:17px;font-weight:500;line-height:44px;color:#000;text-align:center;white-space:nowrap}.title a{color:inherit}.bar-tab{bottom:0;display:table;width:100%;height:50px;padding:0;table-layout:fixed;border-top:1px solid #ddd;border-bottom:0}.bar-tab .tab-item{display:table-cell;width:1%;height:50px;color:#929292;text-align:center;vertical-align:middle}.bar-tab .tab-item.active,.bar-tab .tab-item:active{color:#428bca}.bar-tab .tab-item .icon{top:3px;width:24px;height:24px;padding-top:0;padding-bottom:0}.bar-tab .tab-item .icon~.tab-label{display:block;font-size:11px}.bar .btn{position:relative;top:7px;z-index:20;padding:6px 12px 7px;margin-top:0;font-weight:400}.bar .btn.pull-right{margin-left:10px}.bar .btn.pull-left{margin-right:10px}.bar .btn-link{top:0;padding:0;font-size:16px;line-height:44px;color:#428bca;border:0}.bar .btn-link.active,.bar .btn-link:active{color:#3071a9}.bar .btn-block{top:6px;padding:7px 0;margin-bottom:0;font-size:16px}.bar .btn-nav.pull-left{margin-left:-5px}.bar .btn-nav.pull-left .icon-left-nav{margin-right:-3px}.bar .btn-nav.pull-right{margin-right:-5px}.bar .btn-nav.pull-right .icon-right-nav{margin-left:-3px}.bar .icon{position:relative;z-index:20;padding-top:10px;padding-bottom:10px;font-size:24px}.bar .btn .icon{top:3px;padding:0}.bar .title .icon{padding:0}.bar .title .icon.icon-caret{top:4px;margin-left:-5px}.bar input[type=search]{height:29px;margin:6px 0}.bar .segmented-control{top:7px;margin:0 auto}.badge{display:inline-block;padding:2px 9px 3px;font-size:12px;line-height:1;color:#333;background-color:rgba(0,0,0,.15);border-radius:100px}.badge.badge-inverted{padding:0 5px 0 0;background-color:transparent}.badge-primary{color:#fff;background-color:#428bca}.badge-primary.badge-inverted{color:#428bca}.badge-positive{color:#fff;background-color:#5cb85c}.badge-positive.badge-inverted{color:#5cb85c}.badge-negative{color:#fff;background-color:#d9534f}.badge-negative.badge-inverted{color:#d9534f}.card{margin:10px;overflow:hidden;background-color:#fff;border:1px solid #ddd;border-radius:6px}.card .table-view{margin-bottom:0;border-top:0;border-bottom:0}.card .table-view .table-view-divider:first-child{top:0;border-top-left-radius:6px;border-top-right-radius:6px}.card .table-view .table-view-divider:last-child{border-bottom-right-radius:6px;border-bottom-left-radius:6px}.card .table-view-cell:last-child{border-bottom:0}.table-view{padding-left:0;margin-top:0;margin-bottom:15px;list-style:none;background-color:#fff;border-top:1px solid #ddd;border-bottom:1px solid #ddd}.table-view-cell{position:relative;padding:11px 65px 11px 15px;overflow:hidden;border-bottom:1px solid #ddd}.table-view-cell:last-child{border-bottom:0}.table-view-cell>a:not(.btn){position:relative;display:block;padding:inherit;margin:-11px -65px -11px -15px;overflow:hidden;color:inherit}.table-view-cell>a:not(.btn):active{background-color:#eee}.table-view-cell p{margin-bottom:0}.table-view-divider{padding-top:6px;padding-bottom:6px;padding-left:15px;margin-top:-1px;margin-left:0;font-weight:500;color:#999;background-color:#fafafa;border-top:1px solid #ddd;border-bottom:1px solid #ddd}.table-view .media,.table-view .media-body{overflow:hidden}.table-view .media-object.pull-left{margin-right:10px}.table-view .media-object.pull-right{margin-left:10px}.table-view-cell>.badge,.table-view-cell>.btn,.table-view-cell>.toggle,.table-view-cell>a>.badge,.table-view-cell>a>.btn,.table-view-cell>a>.toggle{position:absolute;top:50%;right:15px;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%)}.table-view-cell .navigate-left>.badge,.table-view-cell .navigate-left>.btn,.table-view-cell .navigate-left>.toggle,.table-view-cell .navigate-right>.badge,.table-view-cell .navigate-right>.btn,.table-view-cell .navigate-right>.toggle,.table-view-cell .push-left>.badge,.table-view-cell .push-left>.btn,.table-view-cell .push-left>.toggle,.table-view-cell .push-right>.badge,.table-view-cell .push-right>.btn,.table-view-cell .push-right>.toggle,.table-view-cell>a .navigate-left>.badge,.table-view-cell>a .navigate-left>.btn,.table-view-cell>a .navigate-left>.toggle,.table-view-cell>a .navigate-right>.badge,.table-view-cell>a .navigate-right>.btn,.table-view-cell>a .navigate-right>.toggle,.table-view-cell>a .push-left>.badge,.table-view-cell>a .push-left>.btn,.table-view-cell>a .push-left>.toggle,.table-view-cell>a .push-right>.badge,.table-view-cell>a .push-right>.btn,.table-view-cell>a .push-right>.toggle{right:35px}.content>.table-view:first-child{margin-top:15px}button,input,select,textarea{font-family:"Helvetica Neue",Helvetica,sans-serif;font-size:17px}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{width:100%;height:35px;-webkit-appearance:none;padding:0 15px;margin-bottom:15px;line-height:21px;background-color:#fff;border:1px solid #ddd;border-radius:3px;outline:0}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0 10px;font-size:16px;border-radius:20px}input[type=search]:focus{text-align:left}textarea{height:auto}select{height:auto;font-size:14px;background-color:#f8f8f8;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.1);box-shadow:inset 0 1px 1px rgba(0,0,0,.1)}.input-group{background-color:#fff}.input-group input,.input-group textarea{margin-bottom:0;background-color:transparent;border-top:0;border-right:0;border-left:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.input-row{height:35px;overflow:hidden;border-bottom:1px solid #ddd}.input-row label{float:left;width:35%;padding:8px 15px;font-family:"Helvetica Neue",Helvetica,sans-serif;line-height:1.1}.input-row input{float:right;width:65%;padding-left:0;margin-bottom:0;border:0}.segmented-control{position:relative;display:table;overflow:hidden;font-size:12px;font-weight:400;background-color:#fff;border:1px solid #ccc;border-radius:3px}.segmented-control .control-item{display:table-cell;width:1%;padding-top:6px;padding-bottom:7px;overflow:hidden;line-height:1;color:#333;text-align:center;text-overflow:ellipsis;white-space:nowrap;border-left:1px solid #ccc}.segmented-control .control-item:first-child{border-left-width:0}.segmented-control .control-item:active{background-color:#eee}.segmented-control .control-item.active{background-color:#ccc}.segmented-control-primary{border-color:#428bca}.segmented-control-primary .control-item{color:#428bca;border-color:inherit}.segmented-control-primary .control-item:active{background-color:#cde1f1}.segmented-control-primary .control-item.active{color:#fff;background-color:#428bca}.segmented-control-positive{border-color:#5cb85c}.segmented-control-positive .control-item{color:#5cb85c;border-color:inherit}.segmented-control-positive .control-item:active{background-color:#d8eed8}.segmented-control-positive .control-item.active{color:#fff;background-color:#5cb85c}.segmented-control-negative{border-color:#d9534f}.segmented-control-negative .control-item{color:#d9534f;border-color:inherit}.segmented-control-negative .control-item:active{background-color:#f9e2e2}.segmented-control-negative .control-item.active{color:#fff;background-color:#d9534f}.control-content{display:none}.control-content.active{display:block}.popover{position:fixed;top:55px;left:50%;z-index:20;display:none;width:280px;margin-left:-140px;background-color:#fff;border-radius:6px;-webkit-box-shadow:0 0 15px rgba(0,0,0,.1);box-shadow:0 0 15px rgba(0,0,0,.1);opacity:0;-webkit-transition:all .25s linear;-moz-transition:all .25s linear;transition:all .25s linear;-webkit-transform:translate3d(0,-15px,0);-ms-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}.popover:before{position:absolute;top:-15px;left:50%;width:0;height:0;margin-left:-15px;content:'';border-right:15px solid transparent;border-bottom:15px solid #fff;border-left:15px solid transparent}.popover.visible{opacity:1;-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.popover .bar~.table-view{padding-top:44px}.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:15;background-color:rgba(0,0,0,.3)}.popover .btn-block{margin-bottom:5px}.popover .btn-block:last-child{margin-bottom:0}.popover .bar-nav{border-bottom:1px solid #ddd;border-top-left-radius:12px;border-top-right-radius:12px;-webkit-box-shadow:none;box-shadow:none}.popover .table-view{max-height:300px;margin-bottom:0;overflow:auto;-webkit-overflow-scrolling:touch;background-color:#fff;border-top:0;border-bottom:0;border-radius:6px}.modal{position:fixed;top:0;z-index:11;width:100%;min-height:100%;overflow:hidden;background-color:#fff;opacity:0;-webkit-transition:-webkit-transform .25s,opacity 1ms .25s;-moz-transition:-moz-transform .25s,opacity 1ms .25s;transition:transform .25s,opacity 1ms .25s;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}.modal.active{height:100%;opacity:1;-webkit-transition:-webkit-transform .25s;-moz-transition:-moz-transform .25s;transition:transform .25s;-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.slider{width:100%;overflow:hidden;background-color:#000}.slider .slide-group{position:relative;font-size:0;white-space:nowrap;-webkit-transition:all 0s linear;-moz-transition:all 0s linear;transition:all 0s linear}.slider .slide-group .slide{display:inline-block;width:100%;height:100%;font-size:14px;vertical-align:top}.toggle{position:relative;display:block;width:74px;height:30px;background-color:#fff;border:2px solid #ddd;border-radius:20px;-webkit-transition-duration:.2s;-moz-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:background-color,border;-moz-transition-property:background-color,border;transition-property:background-color,border}.toggle .toggle-handle{position:absolute;top:-1px;left:-1px;z-index:2;width:28px;height:28px;background-color:#fff;border:1px solid #ddd;border-radius:100px;-webkit-transition-duration:.2s;-moz-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform,border,width;-moz-transition-property:-moz-transform,border,width;transition-property:transform,border,width}.toggle:before{position:absolute;top:3px;right:11px;font-size:13px;color:#999;text-transform:uppercase;content:"Off"}.toggle.active{background-color:#5cb85c;border:2px solid #5cb85c}.toggle.active .toggle-handle{border-color:#5cb85c;-webkit-transform:translate3d(44px,0,0);-ms-transform:translate3d(44px,0,0);transform:translate3d(44px,0,0)}.toggle.active:before{right:auto;left:15px;color:#fff;content:"On"}.toggle input[type=checkbox]{display:none}.content.fade{left:0;opacity:0}.content.fade.in{opacity:1}.content.sliding{z-index:2;-webkit-transition:-webkit-transform .4s;-moz-transition:-moz-transform .4s;transition:transform .4s;-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.content.sliding.left{z-index:1;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.content.sliding.right{z-index:3;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.navigate-left:after,.navigate-right:after,.push-left:after,.push-right:after{position:absolute;top:50%;display:inline-block;font-family:Ratchicons;font-size:inherit;line-height:1;color:#bbb;text-decoration:none;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);-webkit-font-smoothing:antialiased}.navigate-left:after,.push-left:after{left:15px;content:'\e822'}.navigate-right:after,.push-right:after{right:15px;content:'\e826'}@font-face{font-family:Ratchicons;font-style:normal;font-weight:400;src:url(../fonts/ratchicons.eot);src:url(../fonts/ratchicons.eot?#iefix) format("embedded-opentype"),url(../fonts/ratchicons.woff) format("woff"),url(../fonts/ratchicons.ttf) format("truetype"),url(../fonts/ratchicons.svg#svgFontName) format("svg")}.icon{display:inline-block;font-family:Ratchicons;font-size:24px;line-height:1;text-decoration:none;-webkit-font-smoothing:antialiased}.icon-back:before{content:'\e80a'}.icon-bars:before{content:'\e80e'}.icon-caret:before{content:'\e80f'}.icon-check:before{content:'\e810'}.icon-close:before{content:'\e811'}.icon-code:before{content:'\e812'}.icon-compose:before{content:'\e813'}.icon-download:before{content:'\e815'}.icon-edit:before{content:'\e829'}.icon-forward:before{content:'\e82a'}.icon-gear:before{content:'\e821'}.icon-home:before{content:'\e82b'}.icon-info:before{content:'\e82c'}.icon-list:before{content:'\e823'}.icon-more-vertical:before{content:'\e82e'}.icon-more:before{content:'\e82f'}.icon-pages:before{content:'\e824'}.icon-pause:before{content:'\e830'}.icon-person:before{content:'\e832'}.icon-play:before{content:'\e816'}.icon-plus:before{content:'\e817'}.icon-refresh:before{content:'\e825'}.icon-search:before{content:'\e819'}.icon-share:before{content:'\e81a'}.icon-sound:before{content:'\e827'}.icon-sound2:before{content:'\e828'}.icon-sound3:before{content:'\e80b'}.icon-sound4:before{content:'\e80c'}.icon-star-filled:before{content:'\e81b'}.icon-star:before{content:'\e81c'}.icon-stop:before{content:'\e81d'}.icon-trash:before{content:'\e81e'}.icon-up-nav:before{content:'\e81f'}.icon-up:before{content:'\e80d'}.icon-right-nav:before{content:'\e818'}.icon-right:before{content:'\e826'}.icon-down-nav:before{content:'\e814'}.icon-down:before{content:'\e820'}.icon-left-nav:before{content:'\e82d'}.icon-left:before{content:'\e822'}
--------------------------------------------------------------------------------
/src/fonts/ratchicons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vesparny/flux-immutable-example/c25c73ec31533054c877990cbf2fbbe46c96c789/src/fonts/ratchicons.eot
--------------------------------------------------------------------------------
/src/fonts/ratchicons.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/fonts/ratchicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vesparny/flux-immutable-example/c25c73ec31533054c877990cbf2fbbe46c96c789/src/fonts/ratchicons.ttf
--------------------------------------------------------------------------------
/src/fonts/ratchicons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vesparny/flux-immutable-example/c25c73ec31533054c877990cbf2fbbe46c96c789/src/fonts/ratchicons.woff
--------------------------------------------------------------------------------
/src/stores/FrameworkStore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var Immutable = require('immutable');
3 | var BaseContentStore = require('../utils/BaseContentStore');
4 |
5 | class FrameworkStore extends BaseContentStore {
6 | constructor(flux) {
7 | var initialState = {
8 | frameworks: Immutable.Map(),
9 | isLoading: false
10 | };
11 | super(flux, initialState);
12 | var frameworksActionIds = flux.getActionIds('framework');
13 | var favoriteActionIds = flux.getActionIds('favorite');
14 | this.registerAsync(frameworksActionIds.searchFrameworks, this.handleBeginAsyncRequest,
15 | this.handleSearchframeworkSuccess, this.handleErrorAsyncRequest);
16 | this.registerAsync(frameworksActionIds.getFrameworkById, this.handleBeginAsyncRequest,
17 | this.handleFrameworkDetailSuccess, this.handleErrorAsyncRequest);
18 | this.register(favoriteActionIds.addFavorite, this.handleAddFavorite);
19 | this.register(favoriteActionIds.removeFavorite, this.handleRemoveFavorite);
20 | }
21 |
22 | handleSearchframeworkSuccess(payload) {
23 | this.setState({
24 | frameworks: this.mergeIntoBag(this.state.frameworks, payload.response)
25 | });
26 | }
27 |
28 | handleFrameworkDetailSuccess(payload) {
29 | this.setState({
30 | frameworks: this.addOneToBag(this.state.frameworks, payload.response),
31 | isLoading: false
32 | });
33 | }
34 |
35 | handleAddFavorite(id) {
36 | var edited = this.state.frameworks.updateIn([id], function (el) {
37 | return el.set('isFavorited', true);
38 | });
39 | this.setState({
40 | frameworks: edited
41 | });
42 | }
43 |
44 | handleRemoveFavorite(id) {
45 | var edited = this.state.frameworks.updateIn([id], function (el) {
46 | return el.set('isFavorited', false);
47 | });
48 | this.setState({
49 | frameworks: edited
50 | });
51 | }
52 |
53 | getById(id) {
54 | return this.state.frameworks.get(id);
55 | }
56 | }
57 |
58 | module.exports = FrameworkStore;
59 |
--------------------------------------------------------------------------------
/src/stores/SearchFrameworkStore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var BaseLinkedListStore = require('../utils/BaseLinkedListStore');
3 |
4 | class SearchFrameworkStore extends BaseLinkedListStore {
5 | constructor(flux) {
6 | super(flux);
7 | var frameworkActionIds = flux.getActionIds('framework');
8 | this.registerAsync(frameworkActionIds.searchFrameworks, this.handleBeginAsyncRequest,
9 | this.handleSearchFrameworkSuccess, this.handleErrorAsyncRequest);
10 | }
11 |
12 | handleSearchFrameworkSuccess(payload) {
13 | var frameworkStore = this.flux.getStore('framework');
14 | this.waitFor(frameworkStore);
15 | this.setData(payload.query, payload.response);
16 | }
17 |
18 | getframeworks(query) {
19 | return this.getIds(query).map(el =>
20 | this.flux.getStore('framework').getById(el));
21 | }
22 |
23 | }
24 |
25 | module.exports = SearchFrameworkStore;
26 |
--------------------------------------------------------------------------------
/src/utils/ApiUtils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Promise = require('es6-promise').Promise; // jshint ignore:line
4 | var request = require('superagent');
5 | var timeout = 10000;
6 |
7 | function makeCall (method, url, data = {}) {
8 | var req = request[method](url)
9 | .set('Accept', 'application/json')
10 | .timeout(timeout);
11 |
12 | if (method === 'get') {
13 | req.type('json')
14 | .query(data);
15 | } else {
16 | req.send(data);
17 | }
18 |
19 | return new Promise(function (resolve, reject) {
20 | req.end(function (err, res) {
21 | if (err) {
22 | reject(err);
23 | } else if (res.status === 400) {
24 | reject(err);
25 | } else if (!res.ok) {
26 | reject(err);
27 | } else {
28 | resolve(res.body);
29 | }
30 | });
31 | });
32 | }
33 | var ApiUtils = {
34 | get(url, data) {
35 | return makeCall('get', url, data);
36 | },
37 |
38 | post(url, data) {
39 | return makeCall('post', url, data);
40 | },
41 |
42 | put(url, data) {
43 | return makeCall('put', url, data);
44 | },
45 |
46 | patch(url, data) {
47 | return makeCall('patch', url, data);
48 | },
49 |
50 | delete(url, data) {
51 | return makeCall('del', url, data);
52 | }
53 | };
54 |
55 | module.exports = ApiUtils;
56 |
--------------------------------------------------------------------------------
/src/utils/ArrayUtils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var ArrayUtils = {
4 | remove(array, item) {
5 | for (var i = array.length - 1; i >= 0; i -= 1) {
6 | if (array[i] === item) {
7 | array.splice(i, 1);
8 | }
9 | }
10 | }
11 | };
12 |
13 | module.exports = ArrayUtils;
14 |
--------------------------------------------------------------------------------
/src/utils/BaseContentStore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var BaseStore = require('./BaseStore');
3 | var Immutable = require('immutable');
4 |
5 | class BaseContentStore extends BaseStore {
6 | constructor(flux, initialState) {
7 | super(flux, initialState);
8 | }
9 |
10 | mergeIntoBag(immutableBag, data) {
11 | var newData = Immutable.fromJS(data);
12 | return immutableBag.merge(newData.reduce((result, element) =>
13 | result.set(element.get('id'), element), Immutable.Map()));
14 | }
15 |
16 | addOneToBag(immutableBag, element) {
17 | var newData = Immutable.fromJS(element);
18 | return immutableBag.set(newData.get('id'), newData);
19 | }
20 | }
21 |
22 | module.exports = BaseContentStore;
23 |
--------------------------------------------------------------------------------
/src/utils/BaseLinkedListStore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var BaseStore = require('./BaseStore');
3 | var Immutable = require('immutable');
4 |
5 | class BaseLinkedListStore extends BaseStore {
6 | constructor(flux) {
7 | var initialState = {
8 | list: Immutable.Map(),
9 | isLoading: true
10 | };
11 | this.flux = flux;
12 | super(flux, initialState);
13 | }
14 |
15 | getIds(key) {
16 | return this.state.list.get(key, Immutable.List());
17 | }
18 |
19 | setData(key, data) {
20 | this.setState({
21 | list: this.state.list.set(key, Immutable.fromJS(data).map(el =>
22 | el.get('id'))),
23 | isLoading: false
24 | });
25 | }
26 | }
27 |
28 | module.exports = BaseLinkedListStore;
29 |
--------------------------------------------------------------------------------
/src/utils/BaseStore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var { Store } = require('flummox');
3 |
4 | class BaseStore extends Store {
5 | constructor(flux, initialState) {
6 | super();
7 | this.state = initialState;
8 | }
9 |
10 | handleBeginAsyncRequest() {
11 | this.setState({ isLoading: true });
12 | }
13 |
14 | handleErrorAsyncRequest(err) {
15 | this.setState({ isLoading: false });
16 | console.log(err);
17 | }
18 |
19 | isLoading(){
20 | return this.state.isLoading;
21 | }
22 | }
23 |
24 | module.exports = BaseStore;
25 |
--------------------------------------------------------------------------------
/src/utils/LocalStorageUtils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var ArrayUtils = require('./ArrayUtils');
4 |
5 | function getAll(key) {
6 | return JSON.parse(window.localStorage.getItem(key)) || [];
7 | }
8 |
9 | var LocalStorageUtils = {
10 | push(key, value) {
11 | var data = getAll(key);
12 | if (data.indexOf(value) === -1) {
13 | data.push(value);
14 | window.localStorage.setItem(key, JSON.stringify(data));
15 | }
16 | return data;
17 | },
18 | pop(key, value) {
19 | var data = getAll(key);
20 | ArrayUtils.remove(data, value);
21 | window.localStorage.setItem(key, JSON.stringify(data));
22 | return data;
23 | },
24 | getAll(key){
25 | return JSON.parse(window.localStorage.getItem(key)) || [];
26 | }
27 | };
28 |
29 | module.exports = LocalStorageUtils;
30 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | devtool: 'eval',
5 | entry: [
6 | './src/client.js'
7 | ],
8 | output: {
9 | path: __dirname + '/dist',
10 | filename: 'bundle.js'
11 | },
12 | externals:[{
13 | xmlhttprequest: '{XMLHttpRequest:XMLHttpRequest}'
14 | }],
15 | watch: true,
16 | module: {
17 | loaders: [{
18 | test: /\.js$/,
19 | exclude: /node_modules/,
20 | loader: 'babel-loader'
21 | }]
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/webpack.config.production.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var webpack = require('webpack');
4 |
5 | module.exports = {
6 | devtool: 'source-map',
7 | entry: './src/client.js',
8 | output: {
9 | path: __dirname + '/dist',
10 | filename: 'bundle.js'
11 | },
12 | externals:[{
13 | xmlhttprequest: '{XMLHttpRequest:XMLHttpRequest}'
14 | }],
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.optimize.UglifyJsPlugin({
18 | compressor: {
19 | warnings: false
20 | }
21 | }),
22 | new webpack.NoErrorsPlugin(),
23 | ],
24 | module: {
25 | loaders: [{
26 | test: /\.js$/,
27 | exclude: /node_modules/,
28 | loader: 'babel-loader'
29 | }]
30 | }
31 | };
32 |
--------------------------------------------------------------------------------