├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── examples
├── About.jsx
├── app.css
├── index.html
├── main-view.jsx
├── main.jsx
├── regions.jsx
└── routes.jsx
├── index.js
├── karma.conf.js
├── package.json
├── spec
├── Application.spec.jsx
├── Hook.spec.jsx
├── Region.spec.jsx
├── Regions.spec.jsx
├── helpers.js
└── support
│ └── jasmine.json
├── src
├── Application.jsx
├── ComponentValidator.jsx
├── Hook.jsx
├── Region.jsx
├── Regions.jsx
└── index.js
├── test-context.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "stage": 0
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | #http://EditorConfig.org
2 | root = true
3 |
4 | # Indentation override for all JS under lib directory
5 | [{src/**.js,src/**.jsx}]
6 | indent_style = space
7 | indent_size = 2
8 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "ecmaFeatures": {
3 | "jsx": true,
4 | "modules": true
5 | },
6 | "env": {
7 | "browser": true,
8 | "es6": true,
9 | "node": true
10 | },
11 | "parser": "babel-eslint",
12 | "rules": {
13 | "eol-last": 2,
14 | "max-len": [2, 120, 4],
15 | "no-underscore-dangle": [0],
16 | "no-var": 2,
17 | "no-multiple-empty-lines": [2, { "max": 2}],
18 | "semi": [2, "always"],
19 | "space-before-blocks": [2, "always"],
20 | "react/display-name": 0,
21 | "react/jsx-boolean-value": 2,
22 | "react/jsx-quotes": 2,
23 | "react/jsx-no-undef": 2,
24 | "react/jsx-sort-props": 0,
25 | "react/jsx-uses-react": 2,
26 | "react/jsx-uses-vars": 2,
27 | "react/no-did-mount-set-state": 2,
28 | "react/no-did-update-set-state": 2,
29 | "react/no-multi-comp": 0,
30 | "react/no-unknown-property": 2,
31 | "react/prop-types": 2,
32 | "react/react-in-jsx-scope": 2,
33 | "react/self-closing-comp": 2,
34 | "react/wrap-multilines": 2,
35 | "quotes": [2, "single"],
36 | "space-before-function-paren": [2, {
37 | "anonymous": "always",
38 | "named": "never"
39 | }],
40 | "strict": [2, "global"]
41 | },
42 | "plugins": [
43 | "react"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | karma-*
2 | .DS_Store
3 |
4 | # Dist directory created by webpack
5 | ./dist
6 |
7 | # Logs
8 | logs
9 | *.log
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
23 | .grunt
24 |
25 | # node-waf configuration
26 | .lock-wscript
27 |
28 | # Compiled binary addons (http://nodejs.org/api/addons.html)
29 | build/Release
30 |
31 | # Dependency directory
32 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
33 | node_modules
34 |
35 | lib
36 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.12"
4 |
5 | before_install:
6 | - wget https://s3.amazonaws.com/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2
7 | - tar -xvf phantomjs-2.0.0-ubuntu-12.04.tar.bz2
8 | - sudo rm -rf /usr/local/phantomjs/bin/phantomjs
9 | - sudo mv phantomjs /usr/local/phantomjs/bin/phantomjs
10 |
11 | script:
12 | - npm run testci
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ReactRegions
2 |
3 | 
4 |
5 | [](https://travis-ci.org/scup/react-regions)
6 |
7 | ## Application
8 | ## Hooks
9 | ## Regions
10 |
11 | ## Icons Credits
12 |
13 | - Atom vector By Lloyd Humphreys, DK (https://thenounproject.com/Lloyd)
14 |
15 | - Layout vector By Pham Thi Dieu Linh, VN (https://thenounproject.com/phdieuli)
16 |
--------------------------------------------------------------------------------
/examples/About.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default React.createClass({
4 | render() {
5 | return (
6 |
7 | About
8 | Pellentesque habitant morbi tristique senectus et netus et
9 | malesuada fames ac turpis egestas. Vestibulum tortor quam,
10 | feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu
11 | libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
12 | Mauris placerat eleifend leo.
13 |
14 | );
15 | }
16 | });
17 |
--------------------------------------------------------------------------------
/examples/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .container {
6 | display: flex;
7 | flex-flow: row wrap;
8 | }
9 |
10 | .container > * {
11 | flex: 1 100%;
12 | }
13 |
14 | .header, .main, .nav, .aside, .footer {
15 | flex: 1 100%;
16 | }
17 |
18 | .nav {
19 | list-style: none;
20 | margin: 0;
21 | background: deepskyblue;
22 | display: flex;
23 | flex-flow: row wrap;
24 | justify-content: flex-end;
25 | }
26 |
27 | .nav a {
28 | text-decoration: none;
29 | display: block;
30 | padding: 1em;
31 | color: white;
32 | }
33 |
34 | .nav a:hover {
35 | /*background: darken(deepskyblue, 2%);*/
36 | background: #00b7f5;
37 | }
38 |
39 | @media all and (max-width: 600px) {
40 | .nav {
41 | flex-flow: column wrap;
42 | padding: 0;
43 | }
44 | .nav a {
45 | text-align: center;
46 | padding: 10px;
47 | }
48 | .nav li:last-of-type a {
49 | border-bottom: none;
50 | }
51 | }
52 |
53 | @media all and (max-width: 800px) {
54 | .nav { justify-content: space-around; }
55 | }
56 |
57 | @media all and (min-width: 600px) {
58 | .aside { flex: 1 auto; }
59 | }
60 |
61 | @media all and (min-width: 800px) {
62 | .main { flex: 2 0px; }
63 | .aside-1 { order: 1; }
64 | .main { order: 2; }
65 | .aside-2 { order: 3; }
66 | .footer { order: 4; }
67 | }
68 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/main-view.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import myRegions from './regions.jsx';
4 | import myRoutes from './routes.jsx';
5 |
6 | import Region from '../src/Region.jsx';
7 | import Application from '../src/Application.jsx';
8 |
9 | export default class Main extends React.Component {
10 | constructor() {
11 | super();
12 | this.state = {};
13 | }
14 |
15 | render() {
16 | return (
17 |
18 |
24 |
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import myRoutes from './routes.jsx';
4 |
5 | ReactDOM.render(myRoutes, document.getElementById('app'));
6 |
--------------------------------------------------------------------------------
/examples/regions.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Regions from '../src/Regions.jsx';
4 | import Region from '../src/Region.jsx';
5 |
6 | const regionDefinition = [
7 | ,
8 |
9 | ];
10 |
11 | export default (
12 | { regionDefinition }
13 | );
14 |
--------------------------------------------------------------------------------
/examples/routes.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { Router, Route, Link, Redirect, useRouterHistory } from 'react-router';
3 | import { createHashHistory } from 'history';
4 |
5 | import Main from './main-view.jsx';
6 | import Hook from '../src/Hook.jsx';
7 | import Region from '../src/Region.jsx';
8 |
9 | const appHistory = useRouterHistory(createHashHistory)({ basename: '', queryKey: false });
10 |
11 | class One extends React.Component {
12 |
13 | static propTypes = {
14 | region: PropTypes.shape(Region.propTypes),
15 | regions: PropTypes.any
16 | };
17 |
18 | render() {
19 | return (
20 |
21 |
One
22 | OPEN
23 | \
24 |
25 | );
26 | }
27 | }
28 |
29 | class Two extends React.Component {
30 |
31 | static propTypes = {
32 | region: PropTypes.shape(Region.propTypes),
33 | regions: React.PropTypes.any
34 | };
35 |
36 | close() {
37 | Hook.close(this.props.region);
38 | }
39 |
40 | go() {
41 | Hook.go(this.props.region, '/one/teste');
42 | }
43 |
44 | render() {
45 | return (
46 |
47 |
Two
48 | LINK
49 |
50 | Virtual Hook
51 |
52 | Virtual Hook
53 | );
54 | }
55 | }
56 |
57 | export default (
58 |
59 |
60 |
61 |
62 | );
63 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | exports.Application = require('./lib/Application');
2 | exports.Hook = require('./lib/Hook');
3 | exports.Region = require('./lib/Region');
4 | exports.Regions = require('./lib/Regions');
5 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | config.set({
3 | port: 9876,
4 | browsers: ['PhantomJS2'],
5 | files: [
6 | {pattern: 'test-context.js', watched: false},
7 | 'node_modules/babel-polyfill/dist/polyfill.js'
8 | ],
9 | plugins: [
10 | 'karma-webpack',
11 | 'karma-jasmine',
12 | 'karma-coverage',
13 | 'karma-phantomjs2-launcher'
14 | ],
15 | frameworks: ['jasmine'],
16 | preprocessors: {
17 | 'test-context.js': ['webpack']
18 | },
19 | webpack: {
20 | module: {
21 | loaders: [
22 | {test: /\.js/, exclude: /node_modules/, loader: 'babel-loader'}
23 | ]
24 | },
25 | watch: true
26 | },
27 | webpackServer: {
28 | noInfo: true
29 | }
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-regions",
3 | "description": "Region rendering solution for react",
4 | "version": "0.2.1",
5 | "main": "lib/index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/scup/react-regions.git"
9 | },
10 | "directories": {
11 | "example": "examples",
12 | "docs": "docs",
13 | "test": "test"
14 | },
15 | "scripts": {
16 | "start": "node_modules/.bin/webpack-dev-server --content-base ./examples --progress --colors --host 0.0.0.0",
17 | "test": "karma start --single-run",
18 | "testci": "env PHANTOMJS_BIN=/usr/local/bin/phantomjs npm test",
19 | "test-dev": "karma start",
20 | "postinstall": "babel ./src -d lib"
21 | },
22 | "devDependencies": {
23 | "babel": "^5.8.23",
24 | "babel-core": "^5.8.25",
25 | "babel-eslint": "^4.1.6",
26 | "babel-loader": "^5.3.2",
27 | "babel-polyfill": "^6.3.14",
28 | "classnames": "^2.2.0",
29 | "es5-shim": "^4.1.14",
30 | "es6-promise": "^3.0.2",
31 | "eslint": "^1.10.3",
32 | "eslint-loader": "^1.2.0",
33 | "eslint-plugin-react": "^3.15.0",
34 | "faker": "^3.0.1",
35 | "history": "^1.12.5",
36 | "jasmine-core": "^2.3.4",
37 | "jasmine-es6": "0.0.18",
38 | "karma": "^0.13.19",
39 | "karma-coverage": "^0.5.3",
40 | "karma-jasmine": "^0.3.6",
41 | "karma-phantomjs2-launcher": "^0.3.2",
42 | "karma-webpack": "^1.7.0",
43 | "lolex": "^1.3.2",
44 | "marked": "^0.3.5",
45 | "phantomjs": "^1.9.18",
46 | "react": "^0.14.0",
47 | "react-addons-test-utils": "^0.14.0",
48 | "react-document-title": "^2.0.1",
49 | "react-dom": "^0.14.0",
50 | "react-mixin": "^3.0.2",
51 | "react-router": "latest",
52 | "twitter-text": "^1.13.2",
53 | "webpack": "^1.12.2",
54 | "webpack-dev-server": "^1.12.1"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/spec/Application.spec.jsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/react-regions/0c184ab43c0af4aa186f1f233a4e0caaba891a00/spec/Application.spec.jsx
--------------------------------------------------------------------------------
/spec/Hook.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import TestUtils from 'react-addons-test-utils';
4 |
5 | import Hook from '../src/Hook.jsx';
6 | import Region from '../src/Region.jsx';
7 |
8 | // var element = TestUtils.renderIntoDocument();
9 | describe('Hook Module ', function () {
10 |
11 | it('should include the auxiliar fragment to url',function(){
12 | //Given
13 | const region = {
14 | location: '/one/teste/aux/one/teste',
15 | regionProps: { routeFragment: '@/aux/*' }
16 | };
17 |
18 | const location = '/one/teste';
19 | spyOn(Region, "getLinkTo").and.returnValue('#one/teste/aux/one/teste///////////');
20 |
21 | window.location.hash = '#one/teste/';
22 | //When
23 | Hook.go(region, location);
24 |
25 | //Then
26 | expect(window.location.hash).toEqual('#one/teste/aux/one/teste/');
27 | expect(Region.getLinkTo).toHaveBeenCalledWith(region.location, region.regionProps.routeFragment, location);
28 | });
29 |
30 | it('should remove the auxiliar fragment from url',function(){
31 | //Given
32 | let region = {
33 | location: '/one/teste/aux/one/teste',
34 | regionProps: { routeFragment: '*/aux/@' }
35 | };
36 |
37 | window.location.hash = '/one/teste/aux/one/teste';
38 | //When
39 | Hook.close(region)
40 |
41 | //Then
42 | expect(window.location.hash).toEqual('#one/teste/')
43 | });
44 |
45 | });
46 |
--------------------------------------------------------------------------------
/spec/Region.spec.jsx:
--------------------------------------------------------------------------------
1 | import Region from '../src/Region.jsx';
2 |
3 | describe('Region Module ', function () {
4 |
5 | it('should return the separators in a string based pattern', function() {
6 | var givenPattern = '@/aux/*/xxx/*';
7 | var separators = Region.getSeparators(givenPattern);
8 | expect(separators).toEqual(['aux','xxx']);
9 | });
10 |
11 | it('should return the separators in a string based pattern', function() {
12 | var givenPattern = '@/aux/*';
13 | var separators = Region.getSeparators(givenPattern);
14 | expect(separators).toEqual(['aux']);
15 | });
16 |
17 | it('should return the separators in a array based pattern', function() {
18 | var givenPattern = '@/aux/*'.split('/').filter((frag) => frag !== '');
19 | var separators = Region.getSeparators(givenPattern);
20 | expect(separators).toEqual(['aux']);
21 | });
22 |
23 | it('should return the separators in a array based pattern', function() {
24 | var givenPattern = '@/aux/*/xxx/*'.split('/').filter((frag) => frag !== '');
25 | var separators = Region.getSeparators(givenPattern);
26 | expect(separators).toEqual(['aux','xxx']);
27 | });
28 |
29 | it('should serialize a given route', function() {
30 | var givenRoute = '/teste/xxx/aux/teste2/yyy';
31 | var givenSeparators = ['aux'];
32 | var serializedRoute = Region.serializeRoute(givenRoute,givenSeparators);
33 | expect(serializedRoute).toEqual(['/teste/xxx/','aux','/teste2/yyy']);
34 | });
35 |
36 | describe('Obtaining ', function() {
37 |
38 | it('should return a route fragment based on a pattern ', function() {
39 | var givenRoute = '/teste/xxx/teste2/';
40 | var givenPattern = '@/xxx/*';
41 | var myPortion = Region.getMyPortion(givenRoute,givenPattern);
42 | expect(myPortion).toEqual('teste/')
43 | });
44 |
45 | it('should return a route fragment based on a pattern ', function() {
46 | var givenRoute = '/teste/xxx/teste2/';
47 | var givenPattern = '*/xxx/@';
48 | var myPortion = Region.getMyPortion(givenRoute,givenPattern);
49 | expect(myPortion).toEqual('/teste2')
50 | });
51 |
52 | });
53 |
54 | describe('Removeing trailing separators from a given URL', function() {
55 |
56 | it('should remove last fragment', function() {
57 | var givenRoute = ['teste','xxx'];
58 | var givenSeparators = ['xxx'];
59 | var cleanUrl = Region.removeTrailingSeparators(givenRoute,givenSeparators);
60 | expect(cleanUrl).toEqual(['teste']);
61 | });
62 |
63 | it('should remove first fragment', function() {
64 | var givenRoute = ['xxx','teste'];
65 | var givenSeparators = ['xxx'];
66 | var cleanUrl = Region.removeTrailingSeparators(givenRoute,givenSeparators);
67 | expect(cleanUrl).toEqual(['teste']);
68 | });
69 |
70 | it('should remove first and last fragments', function() {
71 | var givenRoute = ['xxx','teste','abc'];
72 | var givenSeparators = ['xxx','abc'];
73 | var cleanUrl = Region.removeTrailingSeparators(givenRoute,givenSeparators);
74 | expect(cleanUrl).toEqual(['teste']);
75 | });
76 |
77 | });
78 |
79 | describe('Returning a link to a destiny based on current URL', function() {
80 |
81 | it('given a full URL, it should change the @ portion at the second fragment', function() {
82 | var givenRoute = '/teste/xxx/teste2/';
83 | var givenPattern = "*/xxx/@";
84 | var givenDestiny = 'oh/my/god';
85 | var resultUrl = Region.getLinkTo(givenRoute,givenPattern,givenDestiny);
86 | expect(resultUrl).toEqual('teste/xxx/oh/my/god');
87 | });
88 |
89 | it('given a full URL, it should change the @ portion at the first fragment', function() {
90 | var givenRoute = '/teste/xxx/teste2/';
91 | var givenPattern = "@/xxx/*";
92 | var givenDestiny = 'oh/my/god';
93 | var resultUrl = Region.getLinkTo(givenRoute,givenPattern,givenDestiny);
94 | expect(resultUrl).toEqual('oh/my/god/xxx/teste2');
95 | });
96 |
97 | it('given a partial URL, it should change the entire URL', function() {
98 | var givenRoute = '/teste/';
99 | var givenPattern = '@/xxx/*';
100 | var givenDestiny = 'oh/my/god';
101 | var resultUrl = Region.getLinkTo(givenRoute,givenPattern,givenDestiny);
102 | expect(resultUrl).toEqual('oh/my/god');
103 | });
104 |
105 | it('given a partial URL, it should increment the second fragment after the separator', function() {
106 | var givenRoute = '/teste/';
107 | var givenPattern = '*/xxx/@';
108 | var givenDestiny = 'oh/my/god';
109 | var resultUrl = Region.getLinkTo(givenRoute,givenPattern,givenDestiny);
110 | expect(resultUrl).toEqual('teste/xxx/oh/my/god');
111 | });
112 |
113 | });
114 |
115 | });
116 |
--------------------------------------------------------------------------------
/spec/Regions.spec.jsx:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom';
2 | import React from 'react';
3 | import TestUtils from 'react-addons-test-utils';
4 | import Region from '../src/Region.jsx'
5 | import Regions from '../src/Regions.jsx';
6 | import Route from 'react-router';
7 |
8 | import Faker from 'faker';
9 |
10 | class One extends React.Component {
11 | render(){
12 | return ()
13 | }
14 | }
15 |
16 | class Two extends React.Component {
17 | render(){
18 | return ()
19 | }
20 | }
21 |
22 | describe( 'Regions module', function () {
23 | let element, region, regionAux, routesMock;
24 | const randomFragmentOne = Faker.lorem.words()[0],
25 | randomFragmentTwo = Faker.lorem.words()[0],
26 | randomAuxFragment = Faker.lorem.words()[0],
27 | randomMainFragment = Faker.lorem.words()[0];
28 |
29 | beforeEach( function() {
30 | region = (),
31 | regionAux = (),
32 | element = TestUtils.renderIntoDocument({[region, regionAux]}),
33 | routesMock = {
34 | props: {
35 | children: [
36 | {
37 | props: {
38 | path: 'one/:paramOfOne/*'
39 | }
40 | },
41 | {
42 | props: {
43 | path: 'two/:paramOfTwo/*'
44 | }
45 | }
46 | ]
47 | }
48 | };
49 | });
50 |
51 | it( 'Should separate path by array index and remove separators', function () {
52 | //Given
53 | let path = "one/:paramOfOne/*";
54 | //When
55 | path = Regions._splitPath(path);
56 | //Then
57 | expect(path).toEqual(['one', ':paramOfOne', '*']);
58 | } );
59 |
60 | describe( 'evaluating _getMatchedRoute', function() {
61 |
62 | it( 'Should return object if it matches', function () {
63 | //Given
64 | let location = "one/teste/",
65 | route = { props: {
66 | path: "one/:teste/*"
67 | }};
68 | let expected =
69 | { originalRoute: {
70 | props: { path: "one/:teste/*" }
71 | },
72 | params: { teste: "teste"}
73 | };
74 | //When
75 | let matched = Regions._getMatchedRoute(location, route);
76 | //Then
77 | expect(expected).toEqual(matched);
78 | });
79 |
80 | it( 'Should return undefined if it does not matches', function () {
81 | //Given
82 | let location = "one/teste/",
83 | route = { props: {
84 | path: "one/:teste/*/aux"
85 | }};
86 | let expected =
87 | { originalRoute: {
88 | props: { path: "one/:teste/*" }
89 | },
90 | params: { teste: "teste"}
91 | };
92 | //When
93 | let matched = Regions._getMatchedRoute(location, route);
94 | //Then
95 | expect(undefined).toEqual(matched);
96 | });
97 | });
98 |
99 | it('should return create a regions instance and call refresh method', function() {
100 | //Given
101 | const region = (),
102 | regions = ({[region]});
103 |
104 | Regions.prototype.cachedView = undefined;
105 | spyOn(Regions.prototype, "refresh");
106 | //When
107 | const fetchCheck = Regions.fetch( regions, routesMock );
108 | //Then
109 | expect(Regions.prototype.cachedView).not.toBeUndefined();
110 | expect(Regions.prototype.refresh).toHaveBeenCalled();
111 | });
112 |
113 | describe(' - refresh - ', function(){
114 | const pathOne = `${randomFragmentOne}/:paramOfOne/*`;
115 | const pathTwo = `${randomFragmentTwo}/:paramOfTwo/*`;
116 | const routeToOne = ;
117 | const routeToTwo = ;
118 | const router = (
119 |
120 | {[routeToOne, routeToTwo]}
121 |
122 | );
123 |
124 | it('should set a component to main region with props and undefined to aux region', function(){
125 | //Given
126 | window.location.hash = `#/${randomFragmentOne}/teste/`;
127 | //When
128 | element.refresh( router );
129 | console.log('element.regions', element.regions.main);
130 | const main = element.regions[randomMainFragment];
131 | const aux = element.regions[randomAuxFragment];
132 |
133 | //Then - MAIN
134 | expect( TestUtils.isElementOfType(main.component, One) ).toBeTruthy();
135 | expect( main.regionProps.routeFragment ).toBe( `@/${randomAuxFragment}/*` );
136 | expect( main.regionProps.title ).toBe( randomMainFragment );
137 | expect( main.location ).toBe( `/${randomFragmentOne}/teste/` );
138 | expect( main.component.props.params.paramOfOne ).toBe( 'teste' );
139 |
140 | //Then - AUX
141 | expect( aux.regionProps.routeFragment ).toBe( `*/${randomAuxFragment}/@` );
142 | expect( aux.regionProps.title ).toBe( `${randomAuxFragment}` );
143 | expect( aux.location ).toBe( `/${randomFragmentOne}/teste/` );
144 | expect( aux.component ).toBe( false );
145 | });
146 |
147 | it('should set a component to main region and to aux region, both with props', function(){
148 | //Given
149 | window.location.hash = `#/${randomFragmentTwo}/teste/${randomAuxFragment}/${randomFragmentOne}/teste2`;
150 | //When
151 | element.refresh( router );
152 | const main = element.regions[randomMainFragment];
153 | const aux = element.regions[randomAuxFragment];
154 |
155 | //Then - MAIN
156 | expect(TestUtils.isElementOfType(main.component, Two)).toBeTruthy();
157 | expect( main.location ).toBe( `/${randomFragmentTwo}/teste/${randomAuxFragment}/${randomFragmentOne}/teste2` );
158 | expect( main.component.props.params.paramOfTwo ).toBe( 'teste' );
159 | expect( main.regionProps.routeFragment ).toBe( `@/${randomAuxFragment}/*` );
160 | expect( main.regionProps.title ).toBe( randomMainFragment );
161 |
162 | //Then - AUX
163 | expect(TestUtils.isElementOfType(aux.component, One)).toBeTruthy();
164 | expect( aux.location ).toBe( `/${randomFragmentTwo}/teste/${randomAuxFragment}/${randomFragmentOne}/teste2` );
165 | expect( aux.component.props.params.paramOfOne ).toBe( 'teste2' );
166 | expect( aux.regionProps.routeFragment ).toBe( `*/${randomAuxFragment}/@` );
167 | expect( aux.regionProps.title ).toBe( `${randomAuxFragment}` );
168 | });
169 | });
170 | });
171 |
--------------------------------------------------------------------------------
/spec/helpers.js:
--------------------------------------------------------------------------------
1 | let path = require('path');
2 |
3 | export let es5 = require('es5-shim');
4 | export let React = require('react');
5 | export let ReactDOM = require('react-dom');
6 | export let TestUtils = require('react-addons-test-utils');
7 |
--------------------------------------------------------------------------------
/spec/support/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "spec",
3 | "spec_files": [
4 | "**/*.spec.jsx"
5 | ],
6 | "helpers": [
7 | "../node_modules/jasmine-es6/lib/install.js",
8 | "helpers/**/*.js"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/src/Application.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Router from 'react-router';
3 | import ComponentValidator from './ComponentValidator.jsx';
4 | import Regions from './Regions';
5 | import Region from './Region';
6 |
7 | export default class Application extends React.Component {
8 | constructor() {
9 | super();
10 | this.state = {children: null};
11 | }
12 |
13 | static propTypes = {
14 | regions: React.PropTypes.any,
15 | routes: ComponentValidator.onlyComponentsOf(Router)
16 | };
17 |
18 | findView(node, regions) {
19 | let resultArray = React.Children.map(node.props.children, (child) => {
20 | if (child.type !== Region) {
21 | if (child.props.children) {
22 | return React.cloneElement(child, {
23 | children: this.findView(child, regions)
24 | });
25 | }
26 |
27 | return React.cloneElement(child, {
28 | children: this.findView(child, regions)
29 | });
30 | } else {
31 | return React.cloneElement(child, {
32 | shouldRender: true,
33 | regions: regions,
34 | history: this.props.routes.props.history
35 | });
36 | }
37 | });
38 |
39 | if (Array.isArray(resultArray)) {
40 | if (resultArray.length === 1) {
41 | return resultArray[0];
42 | }
43 | }
44 |
45 | return resultArray;
46 | }
47 |
48 | componentWillMount() {
49 | let regions = Regions.fetch(this.props.regions, this.props.routes);
50 | this.setState({children: this.findView(this, regions)});
51 | }
52 |
53 | componentWillReceiveProps() {
54 | let regions = Regions.fetch(this.props.regions, this.props.routes);
55 | this.setState({children: this.findView(this, regions)});
56 | }
57 |
58 | render() {
59 | return this.state.children;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/ComponentValidator.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default {
4 | onlyComponentsOf: function (type) {
5 | return function (props, propName, componentName) {
6 | const components = React.Children.toArray(props[propName]);
7 |
8 | for (let component of components) {
9 | if (component.type !== type) {
10 | return new Error(`${componentName} should be an instance of ${type.name}`);
11 | }
12 | }
13 | };
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/Hook.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Region from './Region.jsx';
3 |
4 | export default class Hook extends React.Component {
5 | static propTypes = {
6 | region: PropTypes.shape(Region.propTypes),
7 | where: PropTypes.any,
8 | to: PropTypes.any,
9 | children: PropTypes.any,
10 | remove: PropTypes.bool
11 | };
12 |
13 | static close(region) {
14 | const myRoute = Region.removePortion(
15 | region.location,
16 | region.regionProps.routeFragment
17 | );
18 | window.location.hash = myRoute[0].replace(/\/+$/, '/');
19 | }
20 |
21 | static go(region,to) {
22 | const myRoute = Region.getLinkTo(
23 | region.location,
24 | region.regionProps.routeFragment,
25 | to
26 | );
27 | window.location.hash = myRoute.replace(/\/+$/, '/');
28 | }
29 |
30 | constructor() {
31 | super();
32 | this.clickHandler = this.clickHandler.bind(this);
33 | }
34 |
35 | clickHandler(location) {
36 | window.location.hash = location.replace(/\/+$/, '/');
37 | }
38 |
39 | render() {
40 | let myRoute;
41 | if (this.props.remove) {
42 | myRoute = Region.removePortion(
43 | this.props.region.location,
44 | this.props.region.regionProps.routeFragment
45 | );
46 | if (location.hash.indexOf('#') !== -1) {
47 | myRoute = '#' + myRoute;
48 | }
49 | }
50 |
51 | if (this.props.to) {
52 | myRoute = Region.getLinkTo(
53 | this.props.region.location,
54 | this.props.region.regionProps.routeFragment,
55 | this.props.to
56 | );
57 |
58 | if (location.hash.indexOf('#') !== -1) {
59 | myRoute = '#' + myRoute;
60 | }
61 | }
62 |
63 | return (
64 |
65 | {this.props.children}
66 |
67 | );
68 | }
69 | };
70 |
--------------------------------------------------------------------------------
/src/Region.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | export default class Region extends React.Component {
4 |
5 | static ROUTE_SEPARATOR = '*****------*****';
6 |
7 | static propTypes = {
8 | regions: PropTypes.any,
9 | shouldRender: PropTypes.bool,
10 | title: PropTypes.string,
11 | history: PropTypes.shape({
12 | history: PropTypes.number,
13 | scrollRestoration: PropTypes.string,
14 | state: PropTypes.any
15 | })
16 | };
17 |
18 | static checkMatch(url, portion) {
19 | portion = portion.split('/').filter((frag)=>frag !== '' && frag !== '@' && frag !== '*');
20 | url = url.split('/').filter((frag)=>frag !== '');
21 | return url.indexOf(portion[0]) !== -1;
22 | }
23 |
24 | static getSeparators(pattern) {
25 | if (!Array.isArray(pattern))
26 | pattern = pattern.split('/').filter((frag)=>frag !== '');
27 | return pattern.filter(function (frag) {
28 | return frag.indexOf('*') === -1 && frag.indexOf('@') === -1;
29 | });
30 | }
31 |
32 | static serializeRoute(url, separators) {
33 | return url.split('/').map((frag)=> {
34 | if (separators.indexOf(frag) !== -1)
35 | return this.ROUTE_SEPARATOR + frag + this.ROUTE_SEPARATOR;
36 | return frag;
37 | })
38 | .join('/').split(this.ROUTE_SEPARATOR);
39 | }
40 |
41 | static getLinkTo(url, pattern, to) {
42 | pattern = pattern.split('/').filter((frag)=>frag !== '');
43 | url = url.split('/').filter((frag)=>frag !== '').join('/');
44 | let finalUrl;
45 | let separators = this.getSeparators(pattern);
46 | let serializedRoute = this.serializeRoute(url, separators);
47 |
48 | if (serializedRoute.length >= pattern.length)
49 | return serializedRoute.map((frag, index)=> {
50 | if (pattern[index] === '@')
51 | return to;
52 | return frag;
53 | }).join('/').replace(/\/\//gmi, '/');
54 |
55 | finalUrl = pattern.map((frag, index)=> {
56 | if (frag === '*') {
57 | return serializedRoute[index];
58 | }
59 |
60 | if (frag === '@') {
61 | return to;
62 | }
63 |
64 | return frag;
65 |
66 | }).filter((frag) => frag !== undefined);
67 |
68 | return this.removeTrailingSeparators(finalUrl, separators).join('/').replace(/\/\//g, '/');
69 |
70 | }
71 |
72 | static removeTrailingSeparators(url, separators) {
73 | if (separators.indexOf(url[0]) !== -1) {
74 | url.shift();
75 | }
76 | if (separators.indexOf(url[url.length - 1]) !== -1) {
77 | url.pop();
78 | }
79 | return url;
80 | }
81 |
82 | static getMyPortion(url, pattern) {
83 | let finalUrl;
84 |
85 | pattern = pattern.split('/').filter((frag)=>frag !== '');
86 | let separators = this.getSeparators(pattern);
87 | url = url.split('/').filter((frag)=>frag !== '').join('/');
88 |
89 | this.serializeRoute(url, separators).map((frag, index)=> {
90 | if (pattern[index] === '@')
91 | finalUrl = frag;
92 | });
93 |
94 | return finalUrl;
95 | }
96 |
97 | static removePortion(url, pattern) {
98 | let finalUrl;
99 | pattern = pattern.split('/').filter((frag)=>frag !== '');
100 | let separators = this.getSeparators(pattern);
101 | url = url.split('/').filter((frag)=>frag !== '').join('/');
102 |
103 | finalUrl = this.serializeRoute(url, separators)
104 | .reduce((acc, frag, index)=> {
105 | if (pattern[index] === '@') return acc;
106 | acc.push(frag);
107 | return acc;
108 | }, []);
109 |
110 | return this.removeTrailingSeparators(finalUrl, separators);
111 | }
112 |
113 | render() {
114 |
115 | if (this.props.shouldRender) {
116 |
117 | let component = this.props.regions.getRegion(this.props.title).component;
118 |
119 | if (component) {
120 | let regions = this.props.regions.regions;
121 |
122 | Object.keys(regions).forEach((key) => {
123 | regions[key].history = this.props.history;
124 | });
125 |
126 | component = React.cloneElement(regions[this.props.title].component, {
127 | region: regions[this.props.title],
128 | regions: regions
129 | });
130 | } else {
131 | component = null;
132 | }
133 |
134 | return component;
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/Regions.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import ComponentValidator from './ComponentValidator.jsx';
4 | import Region from './Region.jsx';
5 |
6 | export default class Regions extends React.Component {
7 |
8 | constructor() {
9 | super();
10 | this.state = {};
11 | this.regions = {};
12 | }
13 |
14 | static propTypes = {
15 | children: PropTypes.any,
16 | };
17 |
18 | getRegion(title) {
19 | if (this.regions[title])
20 | return this.regions[title];
21 | }
22 |
23 | static _splitPath( path ) {
24 | return path.replace(/\|\|/,'|').split('/').filter((frag) => frag !== '');
25 | }
26 |
27 | static _getMatchedRoute(location, route) {
28 | let params = {};
29 | let originalRoute = route;
30 | let original_location = location = Regions._splitPath(location);
31 |
32 | route = Regions._splitPath(route.props.path);
33 |
34 | let matchs = route.filter((fragment, index) => {
35 | const matched = (location[index] === fragment || fragment === '*' || fragment.indexOf(':') !== -1);
36 | if (fragment.indexOf(':') !== -1) {
37 | params[fragment.replace(':','')] = location[index];
38 | }
39 |
40 | return matched;
41 | });
42 |
43 | if (matchs.length === route.length) {
44 | return { originalRoute, params };
45 | }
46 | }
47 |
48 | static _findRoute(location, routes) {
49 | if (location) {
50 | if (!routes.filter) {
51 | routes = routes.props.children;
52 | }
53 |
54 | let finalRoute;
55 |
56 | for (let route of routes) {
57 | finalRoute = Regions._getMatchedRoute(location, route);
58 | if (finalRoute) { return finalRoute; }
59 | }
60 | }
61 | }
62 |
63 | static fetch(RegionsInstace, routesDeclaration) {
64 |
65 | if (this.prototype.cachedView === undefined) {
66 | this.prototype.cachedView = ReactDOM.render(RegionsInstace, document.createElement('div'));
67 | }
68 | this.prototype.cachedView.refresh(routesDeclaration);
69 |
70 | return this.prototype.cachedView;
71 | }
72 |
73 | _addRegionObject(region, skipPath) {
74 | const regionDefinition = {
75 | renderTo: region.props.title,
76 | path: skipPath === true ? undefined : Region.getMyPortion(this.requestedLocation, region.props.routeFragment),
77 | regionProps: region.props
78 | };
79 |
80 | let matchedRoute = Regions._findRoute(regionDefinition.path, this.routesDeclaration.props.children);
81 |
82 | if (matchedRoute === undefined) {
83 | console.warn(
84 | 'Route ' + regionDefinition.path + ' doesn\'t match any route.','You should try one of these :\n',
85 | React.Children.map(this.routesDeclaration.props.children.props.children, (child) => {
86 | return child.props.path;
87 | })
88 | );
89 | }
90 |
91 | let component = false;
92 |
93 | this.regions[regionDefinition.renderTo] = {};
94 | this.regions[regionDefinition.renderTo].regionProps = regionDefinition.regionProps;
95 | this.regions[regionDefinition.renderTo].location = this.requestedLocation;
96 |
97 | if (matchedRoute) {
98 | component = matchedRoute.originalRoute.props.component;
99 | }
100 |
101 | if (component) {
102 | component = React.createElement(component, { params : matchedRoute.params });
103 | }
104 |
105 | this.regions[regionDefinition.renderTo].component = component;
106 | }
107 |
108 | refresh(routesDeclaration) {
109 | this.regions = {};
110 | this.routesDeclaration = routesDeclaration;
111 | this.requestedLocation = ('/' + location.hash.replace('#','') + location.search).replace(/\/\//,'/');
112 |
113 | const mainRegion = this.props.children.find((child) => child.props.main);
114 |
115 | if (Region.checkMatch(this.requestedLocation, mainRegion.props.routeFragment)) {
116 | this.props.children.forEach(this._addRegionObject.bind(this));
117 | } else {
118 | this._addRegionObject(mainRegion);
119 | this.props.children.forEach((region) => {
120 | if (!region.props.main) {
121 | this._addRegionObject(region, true);
122 | }
123 | });
124 |
125 | }
126 | }
127 |
128 | render() {
129 | return null;
130 | }
131 | };
132 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export Application from './Application';
2 | export Hook from './Hook';
3 | export Region from './Region';
4 | export Regions from './Regions';
5 |
--------------------------------------------------------------------------------
/test-context.js:
--------------------------------------------------------------------------------
1 | var context = require.context('./spec', true, /spec\.jsx$/);
2 | context.keys().forEach(context);
3 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | entry: './examples/main.jsx',
3 | output: {
4 | filename: 'dist/bundle.js',
5 | sourceFilename: '[file].map'
6 | },
7 | devtool: 'source-map',
8 | resolve: {
9 | extensions: ['', '.js', '.jsx']
10 | },
11 | module: {
12 | preLoaders: [{
13 | test: /\.jsx?$/, loader: "eslint-loader",
14 | exclude: /(node_modules|bower_components)/
15 | }],
16 | loaders: [{
17 | test: /\.jsx?$/,
18 | exclude: /(node_modules|bower_components)/,
19 | loader: 'babel'
20 | }, {
21 | test: /\.less$/,
22 | loader: 'style!css!less'
23 | }]
24 | }
25 | };
26 |
--------------------------------------------------------------------------------