├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── docs
├── README.docs.md
├── README.md
├── assets
│ ├── art.cdr
│ ├── carousel.png
│ ├── docs.css
│ ├── logo.png
│ └── style.css
├── build.js
├── client.js
├── examples
│ ├── DataTreeViewBasicUsage.js
│ ├── EditorsTextBoxBasicUsage.js
│ ├── EditorsTextBoxDisabledState.js
│ ├── EditorsTextBoxPlaceholder.js
│ ├── EditorsTextBoxPrependAndAppend.js
│ └── NavigationVNavBasicUsage.js
├── md
│ └── building.md
├── server.js
└── src
│ ├── CodeMirror.client.js
│ ├── CodeMirror.css
│ ├── CodeMirror.js
│ ├── ComponentsPage.js
│ ├── GettingStartedPage.js
│ ├── HomePage.js
│ ├── NavMain.js
│ ├── NotFoundPage.js
│ ├── PageFooter.js
│ ├── PageHeader.js
│ ├── ReactPlayground.js
│ ├── Root.js
│ ├── Routes.js
│ └── Samples.js
├── gulpfile.js
├── karma.conf.js
├── package.json
├── readme.md
├── register-babel.js
├── run-babel
├── samples
├── codeSample.jsx
├── layout
│ ├── formGroup
│ │ ├── formGroupSample.html
│ │ └── formGroupSample.jsx
│ ├── stackPanel
│ │ ├── stackPanelSample.html
│ │ └── stackPanelSample.jsx
│ ├── tabControl
│ │ ├── tabControlSample.html
│ │ └── tabControlSample.jsx
│ └── treeView
│ │ ├── treeViewSample.html
│ │ └── treeViewSample.jsx
├── meta
│ └── componentBuilder
│ │ ├── componentBuilderSample.html
│ │ └── componentBuilderSample.jsx
├── navigation
│ ├── link
│ │ ├── about.html
│ │ ├── index.html
│ │ └── pages.jsx
│ └── pager
│ │ ├── pagerSample.html
│ │ └── pagerSample.jsx
└── samples.css
├── src
├── components
│ ├── data
│ │ └── treeView
│ │ │ ├── treeRow.jsx
│ │ │ └── treeView.jsx
│ ├── editors
│ │ └── textbox
│ │ │ └── textbox.js
│ ├── gearz.mixin.js
│ ├── layout
│ │ ├── formGroup
│ │ │ └── formGroup.jsx
│ │ ├── stackPanel
│ │ │ └── stackPanel.jsx
│ │ └── tabControl
│ │ │ ├── tab.jsx
│ │ │ ├── tabControl.css
│ │ │ ├── tabControl.jsx
│ │ │ └── tabHeader.jsx
│ ├── meta
│ │ └── componentBuilder
│ │ │ └── componentBuilder.jsx
│ └── navigation
│ │ ├── VNav
│ │ ├── VNav.jsx
│ │ ├── VNavGroup.jsx
│ │ └── VNavItem.jsx
│ │ ├── link
│ │ └── link.jsx
│ │ └── pager
│ │ ├── pager.css
│ │ └── pager.jsx
├── index.js
├── less
│ ├── data
│ │ └── treeView.less
│ └── navigation
│ │ └── VNav.less
└── lib
│ ├── componentFactory
│ ├── component-factory.jsx
│ └── templates
│ │ └── stringTemplate.jsx
│ ├── expression-evaluator.js
│ ├── gearz.routing.js
│ ├── gearz.routing.mixins.js
│ ├── rc.component.factory.js
│ └── text-expression-evaluator.js
├── test
├── .eslintrc
├── VNav-spec.js
├── expression-evaluator-spec.js
├── index.js
└── text-expression-evaluator-spec.js
├── tools
├── amd
│ ├── README.md
│ └── bower.json
└── build.js
├── webpack.config.js
├── webpack.docs.js
└── webpack
├── docs.config.js
├── strategies
├── development.js
├── docs.js
├── index.js
├── optimize.js
└── test.js
├── test.config.js
└── webpack.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | amd/**
2 | dist/**
3 | docs-built/**
4 | lib/**
5 | node_modules/**
6 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "node": true
5 | },
6 | "ecmaFeatures": {
7 | "jsx": true
8 | },
9 | "parser": "babel-eslint",
10 | "plugins": [
11 | "react"
12 | ],
13 | "rules": {
14 | "comma-spacing": 1,
15 | "key-spacing": 0,
16 | "no-underscore-dangle": 0,
17 | "no-unused-vars": [1, { "vars": "all", "args": "none" }],
18 | "no-undef": 1,
19 | "no-var": 2,
20 | "quotes": [1, "single", "avoid-escape"],
21 | "react/display-name": 0,
22 | "react/jsx-uses-react": 1,
23 | "react/no-did-mount-set-state": 1,
24 | "react/no-did-update-set-state": 1,
25 | "react/no-multi-comp": 1,
26 | "react/prop-types": [1, { ignore: [children, className] }],
27 | "react/react-in-jsx-scope": 1,
28 | "react/self-closing-comp": 1,
29 | "react/wrap-multilines": 1,
30 | "react/jsx-uses-vars": 1,
31 | "strict": 0
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *~
3 | .DS_Store
4 | npm-debug.log
5 | node_modules
6 | amd/
7 | !tools/amd/
8 | lib/
9 | !tools/lib/
10 | dist/
11 | !tools/dist/
12 | docs-built/
13 | tmp-bower-repo/
14 | tmp-docs-repo/
15 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | docs/
3 | docs-built/
4 | test-built/
5 | test/
6 | tools/
7 | .gitignore
8 | .travis.yml
9 | karma.conf.js
10 | tmp-docs-repo/
11 | tmp-bower-repo/
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - "0.10"
5 | - "0.12"
6 | - "iojs"
7 | webhooks:
8 | urls:
9 | - https://webhooks.gitter.im/e/0f4bbc965d7f5b1db630
10 | on_success: always
11 | on_failure: always
12 | on_start: false
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Andre Rodrigues Pena, Miguel Angelo dos Santos Bicudo
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
13 | all 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
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/docs/README.docs.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gearz-lab/react-ui/8bc4808092fd913ec6554f5b9f60d4a00f7bb7ef/docs/README.docs.md
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gearz-lab/react-ui/8bc4808092fd913ec6554f5b9f60d4a00f7bb7ef/docs/README.md
--------------------------------------------------------------------------------
/docs/assets/art.cdr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gearz-lab/react-ui/8bc4808092fd913ec6554f5b9f60d4a00f7bb7ef/docs/assets/art.cdr
--------------------------------------------------------------------------------
/docs/assets/carousel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gearz-lab/react-ui/8bc4808092fd913ec6554f5b9f60d4a00f7bb7ef/docs/assets/carousel.png
--------------------------------------------------------------------------------
/docs/assets/docs.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Docs (http://getbootstrap.com)
3 | * Copyright 2011-2014 Twitter, Inc.
4 | * Licensed under the Creative Commons Attribution 3.0 Unported License. For
5 | * details, see http://creativecommons.org/licenses/by/3.0/.
6 | */
7 |
8 |
9 | /*
10 | * Bootstrap Documentation
11 | * Special styles for presenting Bootstrap's documentation and code examples.
12 | *
13 | * Table of contents:
14 | *
15 | * Scaffolding
16 | * Main navigation
17 | * Footer
18 | * Social buttons
19 | * Homepage
20 | * Page headers
21 | * Old docs callout
22 | * Ads
23 | * Side navigation
24 | * Docs sections
25 | * Callouts
26 | * Grid styles
27 | * Examples
28 | * Code snippets (highlight)
29 | * Responsive tests
30 | * Glyphicons
31 | * Customizer
32 | * Miscellaneous
33 | */
34 |
35 |
36 | /*
37 | * Scaffolding
38 | *
39 | * Update the basics of our documents to prep for docs content.
40 | */
41 |
42 | body {
43 | position: relative; /* For scrollspy */
44 | }
45 |
46 | /* Keep code small in tables on account of limited space */
47 | .table code {
48 | font-size: 13px;
49 | font-weight: normal;
50 | }
51 |
52 | /* Outline button for use within the docs */
53 | .btn-outline {
54 | color: #563d7c;
55 | background-color: transparent;
56 | border-color: #563d7c;
57 | }
58 | .btn-outline:hover,
59 | .btn-outline:focus,
60 | .btn-outline:active {
61 | color: #fff;
62 | background-color: #563d7c;
63 | border-color: #563d7c;
64 | }
65 |
66 | /* Inverted outline button (white on dark) */
67 | .btn-outline-inverse {
68 | color: #fff;
69 | background-color: transparent;
70 | border-color: #cdbfe3;
71 | }
72 | .btn-outline-inverse:hover,
73 | .btn-outline-inverse:focus,
74 | .btn-outline-inverse:active {
75 | color: #563d7c;
76 | text-shadow: none;
77 | background-color: #fff;
78 | border-color: #fff;
79 | }
80 |
81 | /* Bootstrap "B" icon */
82 | .bs-docs-booticon {
83 | display: block;
84 | font-weight: 500;
85 | color: #fff;
86 | text-align: center;
87 | cursor: default;
88 | background-color: #563d7c;
89 | border-radius: 0;
90 | }
91 | .bs-docs-booticon-sm {
92 | width: 30px;
93 | height: 30px;
94 | font-size: 20px;
95 | line-height: 28px;
96 | }
97 | .bs-docs-booticon-lg {
98 | width: 144px;
99 | height: 144px;
100 | font-size: 108px;
101 | line-height: 140px;
102 | }
103 | .bs-docs-booticon-inverse {
104 | color: #563d7c;
105 | background-color: #fff;
106 | }
107 | .bs-docs-booticon-outline {
108 | background-color: transparent;
109 | border: 1px solid #cdbfe3;
110 | }
111 |
112 |
113 | /*
114 | * Main navigation
115 | *
116 | * Turn the `.navbar` at the top of the docs purple.
117 | */
118 |
119 | .bs-docs-nav {
120 | margin-bottom: 0;
121 | background-color: #fff;
122 | border-bottom: 0;
123 | }
124 | .bs-home-nav .bs-nav-b {
125 | display: none;
126 | }
127 | .bs-docs-nav .navbar-brand,
128 | .bs-docs-nav .navbar-nav > li > a {
129 | font-weight: 500;
130 | color: #563d7c;
131 | }
132 | .bs-docs-nav .navbar-nav > li > a:hover,
133 | .bs-docs-nav .navbar-nav > .active > a,
134 | .bs-docs-nav .navbar-nav > .active > a:hover {
135 | color: #463265;
136 | background-color: #f9f9f9;
137 | }
138 | .bs-docs-nav .navbar-toggle .icon-bar {
139 | background-color: #563d7c;
140 | }
141 | .bs-docs-nav .navbar-header .navbar-toggle {
142 | border-color: #fff;
143 | }
144 | .bs-docs-nav .navbar-header .navbar-toggle:hover,
145 | .bs-docs-nav .navbar-header .navbar-toggle:focus {
146 | background-color: #f9f9f9;
147 | border-color: #f9f9f9;
148 | }
149 |
150 |
151 | /*
152 | * Footer
153 | *
154 | * Separated section of content at the bottom of all pages, save the homepage.
155 | */
156 |
157 | .bs-docs-footer {
158 | padding-top: 40px;
159 | padding-bottom: 40px;
160 | margin-top: 100px;
161 | color: #777;
162 | text-align: center;
163 | border-top: 1px solid #e5e5e5;
164 | }
165 | .bs-docs-footer-links {
166 | padding-left: 0;
167 | margin-top: 20px;
168 | color: #999;
169 | }
170 | .bs-docs-footer-links li {
171 | display: inline;
172 | padding: 0 2px;
173 | }
174 | .bs-docs-footer-links li:first-child {
175 | padding-left: 0;
176 | }
177 |
178 | @media (min-width: 768px) {
179 | .bs-docs-footer p {
180 | margin-bottom: 0;
181 | }
182 | }
183 |
184 |
185 | /*
186 | * Social buttons
187 | *
188 | * Twitter and GitHub social action buttons (for homepage and footer).
189 | */
190 |
191 | .bs-docs-social {
192 | margin-bottom: 20px;
193 | text-align: center;
194 | }
195 | .bs-docs-social-buttons {
196 | display: inline-block;
197 | padding-left: 0;
198 | margin-bottom: 0;
199 | list-style: none;
200 | }
201 | .bs-docs-social-buttons li {
202 | display: inline-block;
203 | padding: 5px 8px;
204 | line-height: 1;
205 | }
206 | .bs-docs-social-buttons .twitter-follow-button {
207 | width: 225px !important;
208 | }
209 | .bs-docs-social-buttons .twitter-share-button {
210 | width: 98px !important;
211 | }
212 | /* Style the GitHub buttons via CSS instead of inline attributes */
213 | .github-btn {
214 | overflow: hidden;
215 | border: 0;
216 | }
217 |
218 |
219 | /*
220 | * Homepage
221 | *
222 | * Tweaks to the custom homepage and the masthead (main jumbotron).
223 | */
224 |
225 | /* Share masthead with page headers */
226 | .bs-docs-masthead,
227 | .bs-docs-header {
228 | position: relative;
229 | padding: 30px 15px;
230 | color: #cdbfe3;
231 | text-align: center;
232 | text-shadow: 0 1px 0 rgba(0,0,0,.1);
233 | background-color: #6f5499;
234 | background-image: -webkit-gradient(linear, left top, left bottom, from(#563d7c), to(#6f5499));
235 | background-image: -webkit-linear-gradient(top, #563d7c 0%, #6f5499 100%);
236 | background-image: -o-linear-gradient(top, #563d7c 0%, #6f5499 100%);
237 | background-image: linear-gradient(to bottom, #563d7c 0%, #6f5499 100%);
238 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#563d7c', endColorstr='#6F5499', GradientType=0);
239 | background-repeat: repeat-x;
240 | }
241 |
242 | /* Masthead (headings and download button) */
243 | .bs-docs-masthead .bs-docs-booticon {
244 | margin: 0 auto 30px;
245 | }
246 | .bs-docs-masthead h1 {
247 | font-weight: 300;
248 | line-height: 1;
249 | color: #fff;
250 | }
251 | .bs-docs-masthead .lead {
252 | margin: 0 auto 30px;
253 | font-size: 20px;
254 | color: #fff;
255 | }
256 | .bs-docs-masthead .version {
257 | margin-top: -15px;
258 | margin-bottom: 30px;
259 | color: #9783b9;
260 | }
261 | .bs-docs-masthead .btn {
262 | width: 100%;
263 | padding: 15px 30px;
264 | font-size: 20px;
265 | }
266 |
267 | @media (min-width: 480px) {
268 | .bs-docs-masthead .btn {
269 | width: auto;
270 | }
271 | }
272 |
273 | @media (min-width: 768px) {
274 | .bs-docs-masthead {
275 | padding: 80px 0;
276 | }
277 | .bs-docs-masthead h1 {
278 | font-size: 60px;
279 | }
280 | .bs-docs-masthead .lead {
281 | font-size: 24px;
282 | }
283 | }
284 |
285 | @media (min-width: 992px) {
286 | .bs-docs-masthead .lead {
287 | width: 80%;
288 | font-size: 30px;
289 | }
290 | }
291 |
292 |
293 | /*
294 | * Page headers
295 | *
296 | * Jumbotron-esque headers at the top of every page that's not the homepage.
297 | */
298 |
299 | /* Page headers */
300 | .bs-docs-header {
301 | margin-bottom: 40px;
302 | font-size: 20px;
303 | }
304 | .bs-docs-header h1 {
305 | margin-top: 0;
306 | color: #fff;
307 | }
308 | .bs-docs-header p {
309 | margin-bottom: 0;
310 | font-weight: 300;
311 | line-height: 1.4;
312 | }
313 | .bs-docs-header .container {
314 | position: relative;
315 | }
316 |
317 | @media (min-width: 768px) {
318 | .bs-docs-header {
319 | padding-top: 60px;
320 | padding-bottom: 60px;
321 | font-size: 24px;
322 | text-align: left;
323 | }
324 | .bs-docs-header h1 {
325 | font-size: 60px;
326 | line-height: 1;
327 | }
328 | }
329 |
330 | @media (min-width: 992px) {
331 | .bs-docs-header h1,
332 | .bs-docs-header p {
333 | margin-right: 380px;
334 | }
335 | }
336 |
337 |
338 | /*
339 | * Carbon ads
340 | *
341 | * Single display ad that shows on all pages (except homepage) in page headers.
342 | * The hella `!important` is required for any pre-set property.
343 | */
344 |
345 | .carbonad {
346 | width: auto !important;
347 | height: auto !important;
348 | padding: 20px !important;
349 | margin: 30px -30px -31px !important;
350 | overflow: hidden; /* clearfix */
351 | font-size: 13px !important;
352 | line-height: 16px !important;
353 | text-align: left;
354 | background: transparent !important;
355 | border: solid #866ab3 !important;
356 | border-width: 1px 0 !important;
357 | }
358 | .carbonad-img {
359 | margin: 0 !important;
360 | }
361 | .carbonad-text,
362 | .carbonad-tag {
363 | display: block !important;
364 | float: none !important;
365 | width: auto !important;
366 | height: auto !important;
367 | margin-left: 145px !important;
368 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important;
369 | }
370 | .carbonad-text {
371 | padding-top: 0 !important;
372 | }
373 | .carbonad-tag {
374 | color: inherit !important;
375 | text-align: left !important;
376 | }
377 | .carbonad-text a,
378 | .carbonad-tag a {
379 | color: #fff !important;
380 | }
381 | .carbonad #azcarbon > img {
382 | display: none; /* hide what I assume are tracking images */
383 | }
384 |
385 | @media (min-width: 480px) {
386 | .carbonad {
387 | width: 330px !important;
388 | margin: 20px auto !important;
389 | border-width: 1px !important;
390 | border-radius: 4px;
391 | }
392 | .bs-docs-masthead .carbonad {
393 | margin: 50px auto 0 !important;
394 | }
395 | }
396 |
397 | @media (min-width: 768px) {
398 | .carbonad {
399 | margin-right: 0 !important;
400 | margin-left: 0 !important;
401 | }
402 | }
403 |
404 | @media (min-width: 992px) {
405 | .carbonad {
406 | position: absolute;
407 | top: 0;
408 | right: 15px; /* 15px instead of 0 since box-sizing */
409 | width: 330px !important;
410 | padding: 15px !important;
411 | margin: 0 !important;
412 | }
413 | .bs-docs-masthead .carbonad {
414 | position: static;
415 | }
416 | }
417 |
418 |
419 | /*
420 | * Homepage featurettes
421 | *
422 | * Reasons to use Bootstrap, entries from the Expo, and more.
423 | */
424 |
425 | .bs-docs-featurette {
426 | padding-top: 40px;
427 | padding-bottom: 40px;
428 | font-size: 16px;
429 | line-height: 1.5;
430 | color: #555;
431 | text-align: center;
432 | background-color: #fff;
433 | border-bottom: 1px solid #e5e5e5;
434 | }
435 | .bs-docs-featurette + .bs-docs-footer {
436 | margin-top: 0;
437 | border-top: 0;
438 | }
439 |
440 | .bs-docs-featurette-title {
441 | margin-bottom: 5px;
442 | font-size: 30px;
443 | font-weight: normal;
444 | color: #333;
445 | }
446 | .half-rule {
447 | width: 100px;
448 | margin: 40px auto;
449 | }
450 | .bs-docs-featurette h3 {
451 | margin-bottom: 5px;
452 | font-weight: normal;
453 | color: #333;
454 | }
455 | .bs-docs-featurette-img {
456 | display: block;
457 | margin-bottom: 20px;
458 | color: #333;
459 | }
460 | .bs-docs-featurette-img:hover {
461 | color: #428bca;
462 | text-decoration: none;
463 | }
464 | .bs-docs-featurette-img img {
465 | display: block;
466 | margin-bottom: 15px;
467 | }
468 |
469 | @media (min-width: 480px) {
470 | .bs-docs-featurette .img-responsive {
471 | margin-top: 30px;
472 | }
473 | }
474 | @media (min-width: 768px) {
475 | .bs-docs-featurette {
476 | padding-top: 100px;
477 | padding-bottom: 100px;
478 | }
479 | .bs-docs-featurette-title {
480 | font-size: 40px;
481 | }
482 | .bs-docs-featurette .lead {
483 | max-width: 80%;
484 | margin-right: auto;
485 | margin-left: auto;
486 | }
487 | .bs-docs-featured-sites .col-sm-3:first-child img {
488 | border-top-left-radius: 4px;
489 | border-bottom-left-radius: 4px;
490 | }
491 | .bs-docs-featured-sites .col-sm-3:last-child img {
492 | border-top-right-radius: 4px;
493 | border-bottom-right-radius: 4px;
494 | }
495 |
496 | .bs-docs-featurette .img-responsive {
497 | margin-top: 0;
498 | }
499 | }
500 |
501 | /* Featured sites */
502 | .bs-docs-featured-sites {
503 | margin-right: -1px;
504 | margin-left: -1px;
505 | }
506 | .bs-docs-featured-sites .col-sm-3 {
507 | padding-right: 1px;
508 | padding-left: 1px;
509 | }
510 | .bs-docs-featured-sites .img-responsive {
511 | margin-bottom: 15px;
512 | }
513 | @media (min-width: 480px) {
514 | .bs-docs-featured-sites .img-responsive {
515 | margin-bottom: 0;
516 | }
517 | }
518 |
519 | /* Example thumbnails */
520 | @media (max-width: 480px) {
521 | .bs-examples {
522 | margin-right: -10px;
523 | margin-left: -10px;
524 | }
525 | .bs-examples > [class^="col-"] {
526 | padding-right: 10px;
527 | padding-left: 10px;
528 | }
529 | }
530 |
531 |
532 | /*
533 | * Side navigation
534 | *
535 | * Scrollspy and affixed enhanced navigation to highlight sections and secondary
536 | * sections of docs content.
537 | */
538 |
539 | /* By default it's not affixed in mobile views, so undo that */
540 | .bs-docs-sidebar.affix {
541 | position: static;
542 | }
543 | @media (min-width: 768px) {
544 | .bs-docs-sidebar {
545 | padding-left: 20px;
546 | }
547 | }
548 |
549 | /* First level of nav */
550 | .bs-docs-sidenav {
551 | margin-top: 20px;
552 | margin-bottom: 20px;
553 | }
554 |
555 | /* All levels of nav */
556 | .bs-docs-sidebar .nav > li > a {
557 | display: block;
558 | padding: 4px 20px;
559 | font-size: 13px;
560 | font-weight: 500;
561 | color: #999;
562 | }
563 | .bs-docs-sidebar .nav > li > a:hover,
564 | .bs-docs-sidebar .nav > li > a:focus {
565 | padding-left: 19px;
566 | color: #563d7c;
567 | text-decoration: none;
568 | background-color: transparent;
569 | border-left: 1px solid #563d7c;
570 | }
571 | .bs-docs-sidebar .nav > .active > a,
572 | .bs-docs-sidebar .nav > .active:hover > a,
573 | .bs-docs-sidebar .nav > .active:focus > a {
574 | padding-left: 18px;
575 | font-weight: bold;
576 | color: #563d7c;
577 | background-color: transparent;
578 | border-left: 2px solid #563d7c;
579 | }
580 |
581 | /* Nav: second level (shown on .active) */
582 | .bs-docs-sidebar .nav .nav {
583 | display: none; /* Hide by default, but at >768px, show it */
584 | padding-bottom: 10px;
585 | }
586 | .bs-docs-sidebar .nav .nav > li > a {
587 | padding-top: 1px;
588 | padding-bottom: 1px;
589 | padding-left: 30px;
590 | font-size: 12px;
591 | font-weight: normal;
592 | }
593 | .bs-docs-sidebar .nav .nav > li > a:hover,
594 | .bs-docs-sidebar .nav .nav > li > a:focus {
595 | padding-left: 29px;
596 | }
597 | .bs-docs-sidebar .nav .nav > .active > a,
598 | .bs-docs-sidebar .nav .nav > .active:hover > a,
599 | .bs-docs-sidebar .nav .nav > .active:focus > a {
600 | padding-left: 28px;
601 | font-weight: 500;
602 | }
603 |
604 | /* Back to top (hidden on mobile) */
605 | .back-to-top,
606 | .bs-docs-theme-toggle {
607 | display: none;
608 | padding: 4px 10px;
609 | margin-top: 10px;
610 | margin-left: 10px;
611 | font-size: 12px;
612 | font-weight: 500;
613 | color: #999;
614 | }
615 | .back-to-top:hover,
616 | .bs-docs-theme-toggle:hover {
617 | color: #563d7c;
618 | text-decoration: none;
619 | }
620 | .bs-docs-theme-toggle {
621 | margin-top: 0;
622 | }
623 |
624 | @media (min-width: 768px) {
625 | .back-to-top,
626 | .bs-docs-theme-toggle {
627 | display: block;
628 | }
629 | }
630 |
631 | /* Show and affix the side nav when space allows it */
632 | @media (min-width: 992px) {
633 | .bs-docs-sidebar .nav > .active > ul {
634 | display: block;
635 | }
636 | /* Widen the fixed sidebar */
637 | .bs-docs-sidebar.affix,
638 | .bs-docs-sidebar.affix-bottom {
639 | width: 213px;
640 | }
641 | .bs-docs-sidebar.affix {
642 | position: fixed; /* Undo the static from mobile first approach */
643 | top: 20px;
644 | }
645 | .bs-docs-sidebar.affix-bottom {
646 | position: absolute; /* Undo the static from mobile first approach */
647 | }
648 | .bs-docs-sidebar.affix-bottom .bs-docs-sidenav,
649 | .bs-docs-sidebar.affix .bs-docs-sidenav {
650 | margin-top: 0;
651 | margin-bottom: 0;
652 | }
653 | }
654 | @media (min-width: 1200px) {
655 | /* Widen the fixed sidebar again */
656 | .bs-docs-sidebar.affix-bottom,
657 | .bs-docs-sidebar.affix {
658 | width: 263px;
659 | }
660 | }
661 |
662 |
663 | /*
664 | * Docs sections
665 | *
666 | * Content blocks for each component or feature.
667 | */
668 |
669 | /* Space things out */
670 | .bs-docs-section {
671 | margin-bottom: 60px;
672 | }
673 | .bs-docs-section:last-child {
674 | margin-bottom: 0;
675 | }
676 |
677 | h1[id] {
678 | padding-top: 20px;
679 | margin-top: 0;
680 | }
681 |
682 |
683 | /*
684 | * Callouts
685 | *
686 | * Not quite alerts, but custom and helpful notes for folks reading the docs.
687 | * Requires a base and modifier class.
688 | */
689 |
690 | /* Common styles for all types */
691 | .bs-callout {
692 | padding: 20px;
693 | margin: 20px 0;
694 | border: 1px solid #eee;
695 | border-left-width: 5px;
696 | border-radius: 3px;
697 | }
698 | .bs-callout h4 {
699 | margin-top: 0;
700 | margin-bottom: 5px;
701 | }
702 | .bs-callout p:last-child {
703 | margin-bottom: 0;
704 | }
705 | .bs-callout code {
706 | border-radius: 3px;
707 | }
708 |
709 | /* Tighten up space between multiple callouts */
710 | .bs-callout + .bs-callout {
711 | margin-top: -5px;
712 | }
713 |
714 | /* Variations */
715 | .bs-callout-danger {
716 | border-left-color: #d9534f;
717 | }
718 | .bs-callout-danger h4 {
719 | color: #d9534f;
720 | }
721 | .bs-callout-warning {
722 | border-left-color: #f0ad4e;
723 | }
724 | .bs-callout-warning h4 {
725 | color: #f0ad4e;
726 | }
727 | .bs-callout-info {
728 | border-left-color: #5bc0de;
729 | }
730 | .bs-callout-info h4 {
731 | color: #5bc0de;
732 | }
733 |
734 |
735 | /*
736 | * Color swatches
737 | *
738 | * Color swatches and associated values for our grayscale and brand colors.
739 | */
740 |
741 | .color-swatches {
742 | margin: 0 -5px;
743 | overflow: hidden; /* clearfix */
744 | }
745 | .color-swatch {
746 | float: left;
747 | width: 60px;
748 | height: 60px;
749 | margin: 0 5px;
750 | border-radius: 3px;
751 | }
752 |
753 | @media (min-width: 768px) {
754 | .color-swatch {
755 | width: 100px;
756 | height: 100px;
757 | }
758 | }
759 |
760 | /* Framework colors */
761 | .color-swatches .gray-darker {
762 | background-color: #222;
763 | }
764 | .color-swatches .gray-dark {
765 | background-color: #333;
766 | }
767 | .color-swatches .gray {
768 | background-color: #555;
769 | }
770 | .color-swatches .gray-light {
771 | background-color: #999;
772 | }
773 | .color-swatches .gray-lighter {
774 | background-color: #eee;
775 | }
776 | .color-swatches .brand-primary {
777 | background-color: #428bca;
778 | }
779 | .color-swatches .brand-success {
780 | background-color: #5cb85c;
781 | }
782 | .color-swatches .brand-warning {
783 | background-color: #f0ad4e;
784 | }
785 | .color-swatches .brand-danger {
786 | background-color: #d9534f;
787 | }
788 | .color-swatches .brand-info {
789 | background-color: #5bc0de;
790 | }
791 |
792 | /* Docs colors */
793 | .color-swatches .bs-purple {
794 | background-color: #563d7c;
795 | }
796 | .color-swatches .bs-purple-light {
797 | background-color: #c7bfd3;
798 | }
799 | .color-swatches .bs-purple-lighter {
800 | background-color: #e5e1ea;
801 | }
802 | .color-swatches .bs-gray {
803 | background-color: #f9f9f9;
804 | }
805 |
806 |
807 | /*
808 | * Team members
809 | *
810 | * Avatars, names, and usernames for core team.
811 | */
812 |
813 | .bs-team .team-member {
814 | line-height: 32px;
815 | color: #555;
816 | }
817 | .bs-team .team-member:hover {
818 | color: #333;
819 | text-decoration: none;
820 | }
821 | .bs-team .github-btn {
822 | float: right;
823 | width: 180px;
824 | height: 20px;
825 | margin-top: 6px;
826 | }
827 | .bs-team img {
828 | float: left;
829 | width: 32px;
830 | margin-right: 10px;
831 | border-radius: 4px;
832 | }
833 |
834 |
835 | /*
836 | * Grid examples
837 | *
838 | * Highlight the grid columns within the docs so folks can see their padding,
839 | * alignment, sizing, etc.
840 | */
841 |
842 | .show-grid {
843 | margin-bottom: 15px;
844 | }
845 | .show-grid [class^="col-"] {
846 | padding-top: 10px;
847 | padding-bottom: 10px;
848 | background-color: #eee;
849 | background-color: rgba(86,61,124,.15);
850 | border: 1px solid #ddd;
851 | border: 1px solid rgba(86,61,124,.2);
852 | }
853 |
854 |
855 | /*
856 | * Examples
857 | *
858 | * Isolated sections of example content for each component or feature. Usually
859 | * followed by a code snippet.
860 | */
861 |
862 | .bs-example {
863 | position: relative;
864 | padding: 45px 15px 15px;
865 | margin: 0 -15px 15px;
866 | border-color: #e5e5e5 #eee #eee;
867 | border-style: solid;
868 | border-width: 1px 0;
869 | -webkit-box-shadow: inset 0 3px 6px rgba(0,0,0,.05);
870 | box-shadow: inset 0 3px 6px rgba(0,0,0,.05);
871 | }
872 | /* Echo out a label for the example */
873 | .bs-example:after {
874 | position: absolute;
875 | top: 15px;
876 | left: 15px;
877 | font-size: 12px;
878 | font-weight: bold;
879 | color: #959595;
880 | text-transform: uppercase;
881 | letter-spacing: 1px;
882 | content: "Example";
883 | }
884 |
885 | /* Tweak display of the code snippets when following an example */
886 | .bs-example + .highlight {
887 | margin: -15px -15px 15px;
888 | border-width: 0 0 1px;
889 | border-radius: 0;
890 | }
891 |
892 | /* Make the examples and snippets not full-width */
893 | @media (min-width: 768px) {
894 | .bs-example {
895 | margin-right: 0;
896 | margin-left: 0;
897 | background-color: #fff;
898 | border-color: #ddd;
899 | border-width: 1px;
900 | border-radius: 4px 4px 0 0;
901 | -webkit-box-shadow: none;
902 | box-shadow: none;
903 | }
904 | .bs-example + .highlight {
905 | margin-top: -16px;
906 | margin-right: 0;
907 | margin-left: 0;
908 | border-width: 1px;
909 | border-bottom-right-radius: 4px;
910 | border-bottom-left-radius: 4px;
911 | }
912 | }
913 |
914 | /* Undo width of container */
915 | .bs-example .container {
916 | width: auto;
917 | }
918 |
919 | /* Tweak content of examples for optimum awesome */
920 | .bs-example > p:last-child,
921 | .bs-example > ul:last-child,
922 | .bs-example > ol:last-child,
923 | .bs-example > blockquote:last-child,
924 | .bs-example > .form-control:last-child,
925 | .bs-example > .table:last-child,
926 | .bs-example > .navbar:last-child,
927 | .bs-example > .jumbotron:last-child,
928 | .bs-example > .alert:last-child,
929 | .bs-example > .panel:last-child,
930 | .bs-example > .list-group:last-child,
931 | .bs-example > .well:last-child,
932 | .bs-example > .progress:last-child,
933 | .bs-example > .table-responsive:last-child > .table {
934 | margin-bottom: 0;
935 | }
936 | .bs-example > p > .close {
937 | float: none;
938 | }
939 |
940 | /* Typography */
941 | .bs-example-type .table .type-info {
942 | color: #999;
943 | vertical-align: middle;
944 | }
945 | .bs-example-type .table td {
946 | padding: 15px 0;
947 | border-color: #eee;
948 | }
949 | .bs-example-type .table tr:first-child td {
950 | border-top: 0;
951 | }
952 | .bs-example-type h1,
953 | .bs-example-type h2,
954 | .bs-example-type h3,
955 | .bs-example-type h4,
956 | .bs-example-type h5,
957 | .bs-example-type h6 {
958 | margin: 0;
959 | }
960 |
961 | /* Contextual background colors */
962 | .bs-example-bg-classes p {
963 | padding: 15px;
964 | }
965 |
966 | /* Images */
967 | .bs-example > .img-circle,
968 | .bs-example > .img-rounded,
969 | .bs-example > .img-thumbnail {
970 | margin: 5px;
971 | }
972 |
973 | /* Tables */
974 | .bs-example > .table-responsive > .table {
975 | background-color: #fff;
976 | }
977 |
978 | /* Buttons */
979 | .bs-example > .btn,
980 | .bs-example > .btn-group {
981 | margin-top: 5px;
982 | margin-bottom: 5px;
983 | }
984 | .bs-example > .btn-toolbar + .btn-toolbar {
985 | margin-top: 10px;
986 | }
987 |
988 | /* Forms */
989 | .bs-example-control-sizing select,
990 | .bs-example-control-sizing input[type="text"] + input[type="text"] {
991 | margin-top: 10px;
992 | }
993 | .bs-example-form .input-group {
994 | margin-bottom: 10px;
995 | }
996 | .bs-example > textarea.form-control {
997 | resize: vertical;
998 | }
999 |
1000 | /* List groups */
1001 | .bs-example > .list-group {
1002 | max-width: 400px;
1003 | }
1004 |
1005 | /* Navbars */
1006 | .bs-example .navbar:last-child {
1007 | margin-bottom: 0;
1008 | }
1009 | .bs-navbar-top-example,
1010 | .bs-navbar-bottom-example {
1011 | z-index: 1;
1012 | padding: 0;
1013 | overflow: hidden; /* cut the drop shadows off */
1014 | }
1015 | .bs-navbar-top-example .navbar-header,
1016 | .bs-navbar-bottom-example .navbar-header {
1017 | margin-left: 0;
1018 | }
1019 | .bs-navbar-top-example .navbar-fixed-top,
1020 | .bs-navbar-bottom-example .navbar-fixed-bottom {
1021 | position: relative;
1022 | margin-right: 0;
1023 | margin-left: 0;
1024 | }
1025 | .bs-navbar-top-example {
1026 | padding-bottom: 45px;
1027 | }
1028 | .bs-navbar-top-example:after {
1029 | top: auto;
1030 | bottom: 15px;
1031 | }
1032 | .bs-navbar-top-example .navbar-fixed-top {
1033 | top: -1px;
1034 | }
1035 | .bs-navbar-bottom-example {
1036 | padding-top: 45px;
1037 | }
1038 | .bs-navbar-bottom-example .navbar-fixed-bottom {
1039 | bottom: -1px;
1040 | }
1041 | .bs-navbar-bottom-example .navbar {
1042 | margin-bottom: 0;
1043 | }
1044 | @media (min-width: 768px) {
1045 | .bs-navbar-top-example .navbar-fixed-top,
1046 | .bs-navbar-bottom-example .navbar-fixed-bottom {
1047 | position: absolute;
1048 | }
1049 | }
1050 |
1051 | /* Pagination */
1052 | .bs-example .pagination {
1053 | margin-top: 10px;
1054 | margin-bottom: 10px;
1055 | }
1056 |
1057 | /* Pager */
1058 | .bs-example > .pager {
1059 | margin-top: 0;
1060 | }
1061 |
1062 | /* Example modals */
1063 | .bs-example-modal {
1064 | background-color: #f5f5f5;
1065 | }
1066 | .bs-example-modal .modal {
1067 | position: relative;
1068 | top: auto;
1069 | right: auto;
1070 | bottom: auto;
1071 | left: auto;
1072 | z-index: 1;
1073 | display: block;
1074 | }
1075 | .bs-example-modal .modal-dialog {
1076 | left: auto;
1077 | margin-right: auto;
1078 | margin-left: auto;
1079 | }
1080 |
1081 | /* Example dropdowns */
1082 | .bs-example > .dropdown > .dropdown-toggle {
1083 | float: left;
1084 | }
1085 | .bs-example > .dropdown > .dropdown-menu {
1086 | position: static;
1087 | display: block;
1088 | margin-bottom: 5px;
1089 | clear: left;
1090 | }
1091 |
1092 | /* Example tabbable tabs */
1093 | .bs-example-tabs .nav-tabs {
1094 | margin-bottom: 15px;
1095 | }
1096 |
1097 | /* Tooltips */
1098 | .bs-example-tooltips {
1099 | text-align: center;
1100 | }
1101 | .bs-example-tooltips > .btn {
1102 | margin-top: 5px;
1103 | margin-bottom: 5px;
1104 | }
1105 |
1106 | /* Popovers */
1107 | .bs-example-popover {
1108 | padding-bottom: 24px;
1109 | background-color: #f9f9f9;
1110 | }
1111 | .bs-example-popover .popover {
1112 | position: relative;
1113 | display: block;
1114 | float: left;
1115 | width: 260px;
1116 | margin: 20px;
1117 | }
1118 |
1119 | /* Scrollspy demo on fixed height div */
1120 | .scrollspy-example {
1121 | position: relative;
1122 | height: 200px;
1123 | margin-top: 10px;
1124 | overflow: auto;
1125 | }
1126 |
1127 |
1128 | /*
1129 | * Code snippets
1130 | *
1131 | * Generated via Pygments and Jekyll, these are snippets of HTML, CSS, and JS.
1132 | */
1133 |
1134 | .highlight {
1135 | padding: 9px 14px;
1136 | margin-bottom: 14px;
1137 | background-color: #f7f7f9;
1138 | border: 1px solid #e1e1e8;
1139 | border-radius: 4px;
1140 | }
1141 | .highlight pre {
1142 | padding: 0;
1143 | margin-top: 0;
1144 | margin-bottom: 0;
1145 | word-break: normal;
1146 | word-wrap: nowrap;
1147 | white-space: nowrap;
1148 | background-color: transparent;
1149 | border: 0;
1150 | }
1151 | .highlight pre code {
1152 | font-size: inherit;
1153 | color: #333; /* Effectively the base text color */
1154 | }
1155 | .highlight pre code:first-child {
1156 | display: inline-block;
1157 | padding-right: 45px;
1158 | }
1159 |
1160 |
1161 | /*
1162 | * Responsive tests
1163 | *
1164 | * Generate a set of tests to show the responsive utilities in action.
1165 | */
1166 |
1167 | /* Responsive (scrollable) doc tables */
1168 | .table-responsive .highlight pre {
1169 | white-space: normal;
1170 | }
1171 |
1172 | /* Utility classes table */
1173 | .bs-table th small,
1174 | .responsive-utilities th small {
1175 | display: block;
1176 | font-weight: normal;
1177 | color: #999;
1178 | }
1179 | .responsive-utilities tbody th {
1180 | font-weight: normal;
1181 | }
1182 | .responsive-utilities td {
1183 | text-align: center;
1184 | }
1185 | .responsive-utilities td.is-visible {
1186 | color: #468847;
1187 | background-color: #dff0d8 !important;
1188 | }
1189 | .responsive-utilities td.is-hidden {
1190 | color: #ccc;
1191 | background-color: #f9f9f9 !important;
1192 | }
1193 |
1194 | /* Responsive tests */
1195 | .responsive-utilities-test {
1196 | margin-top: 5px;
1197 | }
1198 | .responsive-utilities-test .col-xs-6 {
1199 | margin-bottom: 10px;
1200 | }
1201 | .responsive-utilities-test span {
1202 | display: block;
1203 | padding: 15px 10px;
1204 | font-size: 14px;
1205 | font-weight: bold;
1206 | line-height: 1.1;
1207 | text-align: center;
1208 | border-radius: 4px;
1209 | }
1210 | .visible-on .col-xs-6 .hidden-xs,
1211 | .visible-on .col-xs-6 .hidden-sm,
1212 | .visible-on .col-xs-6 .hidden-md,
1213 | .visible-on .col-xs-6 .hidden-lg,
1214 | .hidden-on .col-xs-6 .hidden-xs,
1215 | .hidden-on .col-xs-6 .hidden-sm,
1216 | .hidden-on .col-xs-6 .hidden-md,
1217 | .hidden-on .col-xs-6 .hidden-lg {
1218 | color: #999;
1219 | border: 1px solid #ddd;
1220 | }
1221 | .visible-on .col-xs-6 .visible-xs-block,
1222 | .visible-on .col-xs-6 .visible-sm-block,
1223 | .visible-on .col-xs-6 .visible-md-block,
1224 | .visible-on .col-xs-6 .visible-lg-block,
1225 | .hidden-on .col-xs-6 .visible-xs-block,
1226 | .hidden-on .col-xs-6 .visible-sm-block,
1227 | .hidden-on .col-xs-6 .visible-md-block,
1228 | .hidden-on .col-xs-6 .visible-lg-block {
1229 | color: #468847;
1230 | background-color: #dff0d8;
1231 | border: 1px solid #d6e9c6;
1232 | }
1233 |
1234 |
1235 | /*
1236 | * Glyphicons
1237 | *
1238 | * Special styles for displaying the icons and their classes in the docs.
1239 | */
1240 |
1241 | .bs-glyphicons {
1242 | margin: 0 -10px 20px;
1243 | overflow: hidden;
1244 | }
1245 | .bs-glyphicons-list {
1246 | padding-left: 0;
1247 | list-style: none;
1248 | }
1249 | .bs-glyphicons li {
1250 | float: left;
1251 | width: 25%;
1252 | height: 115px;
1253 | padding: 10px;
1254 | font-size: 10px;
1255 | line-height: 1.4;
1256 | text-align: center;
1257 | background-color: #f9f9f9;
1258 | border: 1px solid #fff;
1259 | }
1260 | .bs-glyphicons .glyphicon {
1261 | margin-top: 5px;
1262 | margin-bottom: 10px;
1263 | font-size: 24px;
1264 | }
1265 | .bs-glyphicons .glyphicon-class {
1266 | display: block;
1267 | text-align: center;
1268 | word-wrap: break-word; /* Help out IE10+ with class names */
1269 | }
1270 | .bs-glyphicons li:hover {
1271 | color: #fff;
1272 | background-color: #563d7c;
1273 | }
1274 |
1275 | @media (min-width: 768px) {
1276 | .bs-glyphicons {
1277 | margin-right: 0;
1278 | margin-left: 0;
1279 | }
1280 | .bs-glyphicons li {
1281 | width: 12.5%;
1282 | font-size: 12px;
1283 | }
1284 | }
1285 |
1286 |
1287 | /*
1288 | * Customizer
1289 | *
1290 | * Since this is so form control heavy, we have quite a few styles to customize
1291 | * the display of inputs, headings, and more. Also included are all the download
1292 | * buttons and actions.
1293 | */
1294 |
1295 | .bs-customizer .toggle {
1296 | float: right;
1297 | margin-top: 25px;
1298 | }
1299 |
1300 | /* Headings and form contrls */
1301 | .bs-customizer label {
1302 | margin-top: 10px;
1303 | font-weight: 500;
1304 | color: #555;
1305 | }
1306 | .bs-customizer h2 {
1307 | padding-top: 30px;
1308 | margin-top: 0;
1309 | margin-bottom: 5px;
1310 | }
1311 | .bs-customizer h3 {
1312 | margin-bottom: 0;
1313 | }
1314 | .bs-customizer h4 {
1315 | margin-top: 15px;
1316 | margin-bottom: 0;
1317 | }
1318 | .bs-customizer .bs-callout h4 {
1319 | margin-top: 0; /* lame, but due to specificity we have to duplicate */
1320 | margin-bottom: 5px;
1321 | }
1322 | .bs-customizer input[type="text"] {
1323 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
1324 | background-color: #fafafa;
1325 | }
1326 | .bs-customizer .help-block {
1327 | margin-bottom: 5px;
1328 | font-size: 12px;
1329 | }
1330 |
1331 | /* For the variables, use regular weight */
1332 | #less-section label {
1333 | font-weight: normal;
1334 | }
1335 |
1336 | .bs-customizer-input {
1337 | float: left;
1338 | width: 33.333333%;
1339 | padding-right: 15px;
1340 | padding-left: 15px;
1341 | }
1342 |
1343 | /* Downloads */
1344 | .bs-customize-download .btn-outline {
1345 | padding: 20px;
1346 | }
1347 |
1348 | /* Error handling */
1349 | .bs-customizer-alert {
1350 | position: fixed;
1351 | top: 0;
1352 | right: 0;
1353 | left: 0;
1354 | z-index: 1030;
1355 | padding: 15px 0;
1356 | color: #fff;
1357 | background-color: #d9534f;
1358 | border-bottom: 1px solid #b94441;
1359 | -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.25);
1360 | box-shadow: inset 0 1px 0 rgba(255,255,255,.25);
1361 | }
1362 | .bs-customizer-alert .close {
1363 | margin-top: -4px;
1364 | font-size: 24px;
1365 | }
1366 | .bs-customizer-alert p {
1367 | margin-bottom: 0;
1368 | }
1369 | .bs-customizer-alert .glyphicon {
1370 | margin-right: 5px;
1371 | }
1372 | .bs-customizer-alert pre {
1373 | margin: 10px 0 0;
1374 | color: #fff;
1375 | background-color: #a83c3a;
1376 | border-color: #973634;
1377 | -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);
1378 | box-shadow: inset 0 2px 4px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);
1379 | }
1380 |
1381 |
1382 | /*
1383 | * Brand guidelines
1384 | *
1385 | * Extra styles for displaying wordmarks, logos, etc.
1386 | */
1387 |
1388 | /* Logo series wrapper */
1389 | .bs-brand-logos {
1390 | display: table;
1391 | width: 100%;
1392 | margin-bottom: 15px;
1393 | overflow: hidden;
1394 | color: #563d7c;
1395 | background-color: #f9f9f9;
1396 | border-radius: 4px;
1397 | }
1398 |
1399 | /* Individual items */
1400 | .bs-brand-item {
1401 | padding: 60px 0;
1402 | text-align: center;
1403 | }
1404 | .bs-brand-item + .bs-brand-item {
1405 | border-top: 1px solid #fff;
1406 | }
1407 | .bs-brand-logos .inverse {
1408 | color: #fff;
1409 | background-color: #563d7c;
1410 | }
1411 | .bs-brand-item .svg {
1412 | width: 144px;
1413 | height: 144px;
1414 | }
1415 |
1416 | /* Heading content within */
1417 | .bs-brand-item h1,
1418 | .bs-brand-item h3 {
1419 | margin-top: 0;
1420 | margin-bottom: 0;
1421 | }
1422 | .bs-brand-item .bs-docs-booticon {
1423 | margin-right: auto;
1424 | margin-left: auto;
1425 | }
1426 |
1427 | /* Make the icons stand out on what is/isn't okay */
1428 | .bs-brand-item .glyphicon {
1429 | width: 30px;
1430 | height: 30px;
1431 | margin: 10px auto -10px;
1432 | line-height: 30px;
1433 | color: #fff;
1434 | border-radius: 50%;
1435 | }
1436 | .bs-brand-item .glyphicon-ok {
1437 | background-color: #5cb85c;
1438 | }
1439 | .bs-brand-item .glyphicon-remove {
1440 | background-color: #d9534f;
1441 | }
1442 |
1443 | @media (min-width: 768px) {
1444 | .bs-brand-item {
1445 | display: table-cell;
1446 | width: 1%;
1447 | }
1448 | .bs-brand-item + .bs-brand-item {
1449 | border-top: 0;
1450 | border-left: 1px solid #fff;
1451 | }
1452 | .bs-brand-item h1 {
1453 | font-size: 60px;
1454 | }
1455 | }
1456 |
1457 |
1458 | /*
1459 | * Miscellaneous
1460 | *
1461 | * Odds and ends for optimum docs display.
1462 | */
1463 |
1464 | /* Examples gallery: space out content better */
1465 | .bs-examples .thumbnail {
1466 | margin-bottom: 10px;
1467 | }
1468 | .bs-examples h4 {
1469 | margin-bottom: 5px;
1470 | }
1471 | .bs-examples p {
1472 | margin-bottom: 20px;
1473 | }
1474 |
1475 | /* Pseudo :focus state for showing how it looks in the docs */
1476 | #focusedInput {
1477 | border-color: rgb(204,204,204); /* Restate unfocused value to make CSSLint happy that there's a pre-CSS3 fallback*/
1478 | border-color: rgba(82,168,236,.8);
1479 | outline: 0;
1480 | outline: thin dotted \9; /* IE6-9 */
1481 | -webkit-box-shadow: 0 0 8px rgba(82,168,236,.6);
1482 | box-shadow: 0 0 8px rgba(82,168,236,.6);
1483 | }
1484 |
1485 |
1486 | /*
1487 | * ZeroClipboard styles
1488 | */
1489 |
1490 | .zero-clipboard {
1491 | position: relative;
1492 | display: none;
1493 | }
1494 | .btn-clipboard {
1495 | position: absolute;
1496 | top: 0;
1497 | right: 0;
1498 | z-index: 10;
1499 | display: block;
1500 | padding: 5px 8px;
1501 | font-size: 12px;
1502 | color: #777;
1503 | cursor: pointer;
1504 | background-color: #fff;
1505 | border: 1px solid #e1e1e8;
1506 | border-radius: 0 4px 0 4px;
1507 | }
1508 | .btn-clipboard-hover {
1509 | color: #fff;
1510 | background-color: #563d7c;
1511 | border-color: #563d7c;
1512 | }
1513 |
1514 | @media (min-width: 768px) {
1515 | .zero-clipboard {
1516 | display: block;
1517 | }
1518 | }
1519 |
--------------------------------------------------------------------------------
/docs/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gearz-lab/react-ui/8bc4808092fd913ec6554f5b9f60d4a00f7bb7ef/docs/assets/logo.png
--------------------------------------------------------------------------------
/docs/assets/style.css:
--------------------------------------------------------------------------------
1 | /*
2 | * React Bootstrap Documentation
3 | * Special styles for presenting react-bootstrap's documentation and code examples.
4 | * Based on the Bootstrap Documentation styles and overridden as necessary.
5 | */
6 |
7 | body {
8 | background-color: #f9f9f9;
9 | }
10 |
11 | .bs-docs-nav {
12 | background-color: #222;
13 | }
14 |
15 | .bs-docs-nav .navbar-nav>li>a {
16 | color: #aaa;
17 | font-weight: normal;
18 | }
19 |
20 | .bs-docs-nav .navbar-nav>li>a:hover,.bs-docs-nav .navbar-nav>.active>a, .bs-docs-nav .navbar-nav>.active>a:hover {
21 | background: #333;
22 | color: #fafafa;
23 | }
24 |
25 | .bs-docs-nav .navbar-collapse {
26 | overflow: hidden;
27 | }
28 |
29 | @media (min-width: 768px) {
30 |
31 | .bs-docs-nav .navbar-nav>li>a {
32 | border-bottom: 3px solid #222;
33 | }
34 | .bs-docs-nav .navbar-nav>li>a:hover,.bs-docs-nav .navbar-nav>.active>a, .bs-docs-nav .navbar-nav>.active>a:hover {
35 | border-bottom: 3px solid #cc7a6f;
36 | }
37 | }
38 |
39 | .navbar>.container .navbar-brand, .navbar>.container-fluid .navbar-brand {
40 | color: white;
41 | }
42 |
43 | .bs-docs-masthead, .bs-docs-header {
44 | background: #79255E;
45 | filter: none;
46 | color: #79255E;
47 | }
48 |
49 | .bs-docs-header h1 {
50 | color: #e9e9e9;
51 | }
52 |
53 | .bs-docs-header p {
54 | color: #e9e9e9;
55 | }
56 |
57 | .bs-docs-sidebar .nav>li>a {
58 | color: #666;
59 | }
60 |
61 | .bs-docs-sidebar .nav>li>a:hover, .bs-docs-sidebar .nav>li>a:focus {
62 | color: #cc7a6f;
63 | border-left: 1px solid #cc7a6f;
64 | }
65 |
66 | .back-to-top:hover {
67 | color: #cc7a6f;
68 | }
69 |
70 |
71 | .CodeMirror, .CodeMirror-scroll {
72 | height: auto;
73 | }
74 |
75 | .bs-example .btn-toolbar + .btn-toolbar {
76 | margin-top: 10px;
77 | }
78 |
79 | .bs-example .static-modal .modal {
80 | position: relative;
81 | top: auto;
82 | right: auto;
83 | left: auto;
84 | bottom: auto;
85 | z-index: 1;
86 | display: block;
87 | }
88 |
89 | .bs-docs-booticon {
90 | background: url('./logo.png') 0 0 no-repeat;
91 | background-size: contain;
92 | border: 0;
93 | width: 345px;
94 | }
95 |
96 | .bs-example-scroll {
97 | overflow: scroll;
98 | height: 200px;
99 | }
100 |
101 | .bs-example-scroll > div {
102 | position: relative;
103 | padding: 100px 0;
104 | }
105 |
106 | .playground {
107 | margin-bottom: 36px;
108 | }
109 |
110 | .bs-example {
111 | margin-bottom: 0;
112 | }
113 |
114 | .bs-example + .highlight {
115 | margin-top: 0;
116 | margin-bottom: 0;
117 | border-top: none;
118 | border-bottom-right-radius: 0;
119 | }
120 |
121 | .code-toggle {
122 | float: right;
123 | display: inline-block;
124 | position: relative;
125 | top: -1px;
126 | background: #fafafa;
127 | border-bottom-left-radius: 4px;
128 | border-bottom-right-radius: 4px;
129 | border: 1px solid #e1e1e8;
130 | border-top: none;
131 | padding: 4px 8px;
132 | }
133 |
134 | @media (min-width: 768px) {
135 | .code-toggle {
136 | background: #fff;
137 | }
138 | }
139 |
140 | .code-toggle.open {
141 | background: #f8f5ec;
142 | }
143 |
144 | // Minimal CSS Needed for contained modals
145 | .modal-container {
146 | position: relative;
147 | }
148 | .modal-container .modal, .modal-container .modal-backdrop {
149 | position: absolute;
150 | }
151 |
152 |
--------------------------------------------------------------------------------
/docs/build.js:
--------------------------------------------------------------------------------
1 | import from 'colors';
2 | import React from 'react';
3 | import path from 'path';
4 | import Router from 'react-router';
5 | import routes from './src/Routes';
6 | import Root from './src/Root';
7 | import fsep from 'fs-extra-promise';
8 | import { exec } from 'child-process-promise';
9 | import rimraf from 'rimraf-promise';
10 |
11 | const repoRoot = path.resolve(__dirname, '../');
12 | const docsBuilt = path.join(repoRoot, 'docs-built');
13 |
14 | const licenseSrc = path.join(repoRoot, 'LICENSE');
15 | const licenseDest = path.join(docsBuilt, 'LICENSE');
16 | const readmeSrc = path.join(__dirname, 'README.docs.md');
17 | const readmeDest = path.join(docsBuilt, 'README.md');
18 |
19 | export default function BuildDocs() {
20 | console.log('Building: '.cyan + 'docs'.green);
21 |
22 | return rimraf(docsBuilt)
23 | .then(() => fsep.mkdir(docsBuilt))
24 | .then(() => {
25 | let writes = Root
26 | .getPages()
27 | .map(fileName => new Promise((resolve, reject) => {
28 | Router.run(routes, '/' + fileName, Handler => {
29 | let RootHTML = React.renderToString(React.createElement(Handler));
30 | return fsep.writeFile(path.join(docsBuilt, fileName), RootHTML)
31 | .then(write => resolve(write));
32 | });
33 | }));
34 |
35 | return Promise.all(writes.concat([
36 | exec(`webpack --config webpack.docs.js -p --bail`),
37 | fsep.copy(licenseSrc, licenseDest),
38 | fsep.copy(readmeSrc, readmeDest)
39 | ]));
40 | })
41 | .then(() => console.log('Built: '.cyan + 'docs'.green));
42 | }
43 |
--------------------------------------------------------------------------------
/docs/client.js:
--------------------------------------------------------------------------------
1 | import from 'bootstrap/less/bootstrap.less';
2 | import from './assets/docs.css';
3 | import from './assets/style.css';
4 |
5 | import from './assets/carousel.png';
6 | import from './assets/logo.png';
7 |
8 | // React-UI styles
9 | import from '../src/less/data/treeView.less';
10 | import from '../src/less/navigation/VNav.less';
11 |
12 | import React from 'react';
13 | import Router from 'react-router';
14 | import routes from './src/Routes';
15 |
16 | // TODO: Move this to Webpack
17 | // For React devtools
18 | window.React = React;
19 |
20 | Router.run(routes, Router.RefreshLocation, Handler => {
21 | React.render(
22 | React.createElement(Handler, window.INITIAL_PROPS), document);
23 | });
24 |
--------------------------------------------------------------------------------
/docs/examples/DataTreeViewBasicUsage.js:
--------------------------------------------------------------------------------
1 | const treeNodes = {
2 | page: {
3 | display: "PageControl",
4 | nodes: {
5 | editPanel: {
6 | display: "StackPanelControl",
7 | nodes: {
8 | panel: {
9 | display: "Panel (Main)",
10 | nodes: {
11 | name: {
12 | display: "TexboxControl (Name)"
13 | },
14 | dateOfBirth: {
15 | display: "DatePickerControl (Date of Birth)"
16 | },
17 | gender: {
18 | display: "ToggleButtonControl (Gender)"
19 | }
20 | }
21 | },
22 | panel2: {
23 | display: "Panel (Additional Info)",
24 | nodes: {
25 | isResponsible: {
26 | display: "ToogleButtonControl (Is Responsible)"
27 | }
28 | }
29 | }
30 | }
31 | }
32 | }
33 | }
34 | };
35 |
36 | const LayoutTreeViewBasicUsage = (
37 |
38 | );
39 |
40 | React.render(LayoutTreeViewBasicUsage, mountNode);
41 |
--------------------------------------------------------------------------------
/docs/examples/EditorsTextBoxBasicUsage.js:
--------------------------------------------------------------------------------
1 | const EditorsTextBoxBasicUsage = (
2 |
6 | );
7 |
8 | React.render(EditorsTextBoxBasicUsage, mountNode);
9 |
--------------------------------------------------------------------------------
/docs/examples/EditorsTextBoxDisabledState.js:
--------------------------------------------------------------------------------
1 | const EditorsTextBoxDisabledState = (
2 |
7 | );
8 |
9 | React.render(EditorsTextBoxDisabledState, mountNode);
10 |
--------------------------------------------------------------------------------
/docs/examples/EditorsTextBoxPlaceholder.js:
--------------------------------------------------------------------------------
1 | const EditorsTextBoxPrependAndAppend = (
2 |
7 | );
8 |
9 | React.render(EditorsTextBoxPrependAndAppend, mountNode);
10 |
--------------------------------------------------------------------------------
/docs/examples/EditorsTextBoxPrependAndAppend.js:
--------------------------------------------------------------------------------
1 | const EditorsTextBoxPlaceholder = (
2 |
8 | );
9 |
10 | React.render(EditorsTextBoxPlaceholder, mountNode);
11 |
--------------------------------------------------------------------------------
/docs/examples/NavigationVNavBasicUsage.js:
--------------------------------------------------------------------------------
1 | const selectHandler = (name) => alert(name);
2 |
3 | const NavigationSideNavBasicUsage = (
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | );
14 |
15 | React.render(NavigationSideNavBasicUsage, mountNode);
16 |
--------------------------------------------------------------------------------
/docs/md/building.md:
--------------------------------------------------------------------------------
1 | Building react-ui
2 | ===
3 |
4 | Building CSS
5 | ---
6 |
7 | CSS is build through `gulp`. Run:
8 |
9 | $ gulp styles
--------------------------------------------------------------------------------
/docs/server.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import express from 'express';
3 | import path from 'path';
4 | import webpack from 'webpack';
5 | import webpackMiddleware from 'webpack-dev-middleware';
6 | import webpackConfigBuilder from '../webpack/webpack.config';
7 | import Router from 'react-router';
8 | import routes from './src/Routes';
9 |
10 | const development = process.env.NODE_ENV !== 'production';
11 | let app = express();
12 |
13 | if (development) {
14 | let webpackConfig = webpackConfigBuilder({
15 | development: development,
16 | docs: true
17 | });
18 |
19 |
20 |
21 | let publicPath = webpackConfig.output.publicPath;
22 |
23 | webpackConfig.output.path = '/';
24 | webpackConfig.output.publicPath = undefined;
25 |
26 | app = app
27 | .use(webpackMiddleware(webpack(webpackConfig), {
28 | noInfo: false,
29 | publicPath: publicPath,
30 | stats: {
31 | colors: true
32 | }
33 | }))
34 | .use(function renderApp(req, res) {
35 | Router.run(routes, req.url, Handler => {
36 | let html = React.renderToString( );
37 | res.send(html);
38 | });
39 | });
40 | } else {
41 | app = app
42 | .use(express.static(path.join(__dirname, '../docs-built')));
43 | }
44 |
45 | app
46 | .listen(4000, function () {
47 | console.log('Server started at http://localhost:4000');
48 | });
49 |
--------------------------------------------------------------------------------
/docs/src/CodeMirror.client.js:
--------------------------------------------------------------------------------
1 | import CodeMirror from 'codemirror';
2 | import 'codemirror/mode/javascript/javascript';
3 |
4 | import 'codemirror/theme/solarized.css';
5 | import 'codemirror/lib/codemirror.css';
6 | import './CodeMirror.css';
7 |
8 | export default {
9 | IS_NODE: false,
10 | CodeMirror
11 | };
12 |
--------------------------------------------------------------------------------
/docs/src/CodeMirror.css:
--------------------------------------------------------------------------------
1 | .cm-s-solarized.CodeMirror {
2 | -moz-box-shadow: none;
3 | -webkit-box-shadow: none;
4 | box-shadow: none;
5 | }
6 |
7 | .highlight, .code-toggle.open {
8 | background-color: #fdf6e3;
9 | }
10 |
11 | .cm-s-solarized .cm-comment { color: #93a1a1; }
12 |
13 | .CodeMirror, .CodeMirror-scroll {
14 | height: auto;
15 | }
16 |
--------------------------------------------------------------------------------
/docs/src/CodeMirror.js:
--------------------------------------------------------------------------------
1 | export default {
2 | IS_NODE: true,
3 | CodeMirror: {}
4 | };
5 |
--------------------------------------------------------------------------------
/docs/src/ComponentsPage.js:
--------------------------------------------------------------------------------
1 | /* eslint no-path-concat: 0 */
2 |
3 | import React from 'react';
4 |
5 | import Affix from 'react-bootstrap/lib/Affix';
6 | import Nav from 'react-bootstrap/lib/Nav';
7 | import SubNav from 'react-bootstrap/lib/SubNav';
8 | import NavItem from 'react-bootstrap/lib/NavItem';
9 |
10 | import NavMain from './NavMain';
11 | import PageHeader from './PageHeader';
12 | import PageFooter from './PageFooter';
13 | import ReactPlayground from './ReactPlayground';
14 | import Samples from './Samples';
15 | import Textbox from '../../src/components/editors/textbox/textbox';
16 |
17 | const ComponentsPage = React.createClass({
18 | getInitialState() {
19 | return {
20 | activeNavItemHref: null,
21 | navOffsetTop: null
22 | };
23 | },
24 |
25 | handleNavItemSelect(key, href) {
26 | this.setState({
27 | activeNavItemHref: href
28 | });
29 |
30 | window.location = href;
31 | },
32 |
33 | componentDidMount() {
34 | let elem = this.refs.sideNav.getDOMNode(),
35 | domUtils = Affix.domUtils,
36 | sideNavOffsetTop = domUtils.getOffset(elem).top,
37 | sideNavMarginTop = parseInt(domUtils.getComputedStyles(elem.firstChild).marginTop, 10),
38 | topNavHeight = this.refs.topNav.getDOMNode().offsetHeight;
39 |
40 | this.setState({
41 | navOffsetTop: sideNavOffsetTop - topNavHeight - sideNavMarginTop,
42 | navOffsetBottom: this.refs.footer.getDOMNode().offsetHeight
43 | });
44 | },
45 |
46 | render() {
47 | return (
48 |
49 |
50 |
51 |
54 |
55 |
56 |
57 |
58 |
59 | {/* General */}
60 |
61 |
General
62 |
ReactUI uses Statefy to control state
and props
.
63 | We recommend reading it's documentation .
64 |
65 |
66 |
Auto generated events
67 |
Statefy adds auto-generated change events to any props
. So, for instance, if a component has
68 | a url
prop, then, there's also a onUrlChange
event. When this event is handled, if
69 | event.preventDefault()
is called, then the component will not update it's internal state and it's
70 | up the the caller to render the component again.
71 |
72 |
73 |
74 | {/* Editors */}
75 |
76 |
Editors
77 |
78 |
Textbox
79 |
Basic usage
80 |
81 |
82 |
Prepending and appending text
83 |
84 |
85 |
Placeholder
86 |
87 |
88 |
Disabled state
89 |
90 |
91 |
92 |
93 | {/* Data */}
94 |
95 |
Data
96 |
TreeView
97 |
Basic usage
98 |
99 |
100 |
101 | {/* Navigation */}
102 |
103 |
Navigation
104 |
VNav
105 |
Basic usage
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
119 |
124 |
125 | TextBox
126 |
127 |
128 | TreeView
129 |
130 |
131 | VNav
132 |
133 |
134 |
135 | Back to top
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 | );
145 | }
146 | });
147 |
148 | export default ComponentsPage;
149 |
--------------------------------------------------------------------------------
/docs/src/GettingStartedPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import NavMain from './NavMain';
4 | import PageHeader from './PageHeader';
5 | import PageFooter from './PageFooter';
6 |
7 | const Page = React.createClass({
8 | render() {
9 | return (
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
Setup
22 |
You can import the lib as AMD modules, CommonJS modules, or as a global JS script.
23 |
24 |
First add the Bootstrap CSS to your project; check here if you have not already done that. Then:
25 |
26 |
CommonJS
27 |
28 |
Installing:
29 |
30 |
31 |
{`
32 | $ npm install react
33 | $ npm install reactui
34 | `}
35 |
36 |
Using:
37 |
38 |
{`
39 | var Textbox = require('reactui/lib/Textbox');
40 | // or
41 | var Textbox = require('reactui').Textbox;
42 | `}
43 |
44 |
45 |
AMD
46 |
47 |
Installing:
48 |
49 |
50 |
{`
51 | $ bower install react
52 | $ bower install reactui
53 | `}
54 |
55 |
Using:
56 |
57 |
{`
58 | define(['reactui/lib/Textbox'], function(Alert) { ... });
59 | // or
60 | define(['reactui'], function(ReactUI) { var Textbox = ReactUI.Textbox; ... });
61 | `}
62 |
63 |
64 |
Browser globals
65 |
66 |
Installing:
67 |
68 |
The bower repo contains react-ui.js
and react-ui.min.js
with all components exported in the window.ReactUI
object.
69 |
70 |
Using:
71 |
72 |
73 |
{`
74 |
75 |
76 |
79 | `}
80 |
81 |
82 |
83 |
Browser support
84 |
We aim to support all browsers supported by both React and Bootstrap .
85 |
86 |
React requires polyfills for non-ES5 capable browsers.
87 |
88 |
jQuery is currently required only for IE8 support for components which require reading element positions from the DOM: Popover
and Tooltip
when launched with OverlayTrigger
. We would like to remove this dependency in future versions but for now, including the following snippet in your page should have you covered:
89 |
90 |
91 |
{`
92 |
105 | `}
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | );
115 | },
116 |
117 | shouldComponentUpdate() {
118 | return false;
119 | }
120 | });
121 |
122 | export default Page;
123 |
--------------------------------------------------------------------------------
/docs/src/HomePage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import NavMain from './NavMain';
4 | import PageFooter from './PageFooter';
5 |
6 | const HomePage = React.createClass({
7 | render: function () {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Bootstrap based data components for React
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 | });
24 |
25 | export default HomePage;
26 |
--------------------------------------------------------------------------------
/docs/src/NavMain.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Router, { Link } from 'react-router';
3 | import Navbar from 'react-bootstrap/lib/Navbar';
4 | import Nav from 'react-bootstrap/lib/Nav';
5 |
6 | const NAV_LINKS = {
7 | 'getting-started': {
8 | link: 'getting-started',
9 | title: 'Getting started'
10 | },
11 | 'components': {
12 | link: 'components',
13 | title: 'Components'
14 | }
15 | };
16 |
17 | const NavMain = React.createClass({
18 | propTypes: {
19 | activePage: React.PropTypes.string
20 | },
21 |
22 | render() {
23 | let brand = React-UI;
24 | let links = Object.keys(NAV_LINKS).map(this.renderNavItem).concat([
25 |
26 | GitHub
27 |
28 | ]);
29 |
30 | return (
31 |
32 |
33 | {links}
34 |
35 |
36 | );
37 | },
38 |
39 | renderNavItem(linkName) {
40 | let link = NAV_LINKS[linkName];
41 |
42 | return (
43 |
44 | {link.title}
45 |
46 | );
47 | }
48 | });
49 |
50 | export default NavMain;
51 |
--------------------------------------------------------------------------------
/docs/src/NotFoundPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import NavMain from './NavMain';
4 | import PageHeader from './PageHeader';
5 | import PageFooter from './PageFooter';
6 |
7 | const NotFoundPage = React.createClass({
8 | render() {
9 | return (
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 | );
20 | }
21 | });
22 |
23 | export default NotFoundPage;
24 |
--------------------------------------------------------------------------------
/docs/src/PageFooter.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import packageJSON from '../../package.json';
3 |
4 | const PageHeader = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
24 |
25 |
26 |
34 |
35 |
36 |
37 |
Code licensed under MIT .
38 |
39 | Currently v{packageJSON.version}
40 | ·
41 | GitHub
42 | ·
43 | Issues
44 |
45 |
46 |
47 | );
48 | }
49 | });
50 |
51 | export default PageHeader;
52 |
--------------------------------------------------------------------------------
/docs/src/PageHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const PageHeader = React.createClass({
4 | propTypes: {
5 | title: React.PropTypes.string,
6 | subTitle: React.PropTypes.string
7 | },
8 | render() {
9 | return (
10 |
11 |
12 |
{this.props.title}
13 |
{this.props.subTitle}
14 |
15 |
16 | );
17 | }
18 | });
19 |
20 | export default PageHeader;
21 |
--------------------------------------------------------------------------------
/docs/src/ReactPlayground.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import React from 'react';
3 | import classNames from 'classnames';
4 |
5 | // React-UI
6 | import FormGroup from '../../src/components/layout/formGroup/formGroup.jsx';
7 | import Textbox from '../../src/components/editors/textbox/textbox';
8 | import TreeView from '../../src/components/data/treeView/treeView.jsx';
9 | import VNav from '../../src/components/navigation/VNav/VNav.jsx';
10 | import VNavGroup from '../../src/components/navigation/VNav/VNavGroup.jsx';
11 | import VNavItem from '../../src/components/navigation/VNav/VNavItem.jsx';
12 |
13 | // React-Bootstrap
14 | import Accordion from 'react-bootstrap/lib/Accordion';
15 | import Alert from 'react-bootstrap/lib/Alert';
16 | import Badge from 'react-bootstrap/lib/Badge';
17 | import Button from 'react-bootstrap/lib/Button';
18 | import ButtonGroup from 'react-bootstrap/lib/ButtonGroup';
19 | import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar';
20 | import CollapsableNav from 'react-bootstrap/lib/CollapsableNav';
21 | import CollapsableMixin from 'react-bootstrap/lib/CollapsableMixin';
22 | import Carousel from 'react-bootstrap/lib/Carousel';
23 | import CarouselItem from 'react-bootstrap/lib/CarouselItem';
24 | import Col from 'react-bootstrap/lib/Col';
25 | import DropdownButton from 'react-bootstrap/lib/DropdownButton';
26 | import Glyphicon from 'react-bootstrap/lib/Glyphicon';
27 | import Grid from 'react-bootstrap/lib/Grid';
28 | import Input from 'react-bootstrap/lib/Input';
29 | import Jumbotron from 'react-bootstrap/lib/Jumbotron';
30 | import Label from 'react-bootstrap/lib/Label';
31 | import ListGroup from 'react-bootstrap/lib/ListGroup';
32 | import ListGroupItem from 'react-bootstrap/lib/ListGroupItem';
33 | import Nav from 'react-bootstrap/lib/Nav';
34 | import Navbar from 'react-bootstrap/lib/Navbar';
35 | import NavItem from 'react-bootstrap/lib/NavItem';
36 | import MenuItem from 'react-bootstrap/lib/MenuItem';
37 | import Modal from 'react-bootstrap/lib/Modal';
38 | import ModalTrigger from 'react-bootstrap/lib/ModalTrigger';
39 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
40 | import OverlayMixin from 'react-bootstrap/lib/OverlayMixin';
41 | import PageHeader from 'react-bootstrap/lib/PageHeader';
42 | import PageItem from 'react-bootstrap/lib/PageItem';
43 | import Pager from 'react-bootstrap/lib/Pager';
44 | import Panel from 'react-bootstrap/lib/Panel';
45 | import PanelGroup from 'react-bootstrap/lib/PanelGroup';
46 | import Popover from 'react-bootstrap/lib/Popover';
47 | import ProgressBar from 'react-bootstrap/lib/ProgressBar';
48 | import Row from 'react-bootstrap/lib/Row';
49 | import SplitButton from 'react-bootstrap/lib/SplitButton';
50 | import TabbedArea from 'react-bootstrap/lib/TabbedArea';
51 | import Table from 'react-bootstrap/lib/Table';
52 | import TabPane from 'react-bootstrap/lib/TabPane';
53 | import Tooltip from 'react-bootstrap/lib/Tooltip';
54 | import Well from 'react-bootstrap/lib/Well';
55 | /* eslint-enable */
56 |
57 | import {CodeMirror, IS_NODE} from './CodeMirror';
58 | import babel from 'babel/browser';
59 |
60 | const IS_MOBILE = typeof navigator !== 'undefined' && (
61 | navigator.userAgent.match(/Android/i)
62 | || navigator.userAgent.match(/webOS/i)
63 | || navigator.userAgent.match(/iPhone/i)
64 | || navigator.userAgent.match(/iPad/i)
65 | || navigator.userAgent.match(/iPod/i)
66 | || navigator.userAgent.match(/BlackBerry/i)
67 | || navigator.userAgent.match(/Windows Phone/i)
68 | );
69 |
70 | const CodeMirrorEditor = React.createClass({
71 | componentDidMount() {
72 | if (IS_MOBILE || IS_NODE) {
73 | return;
74 | }
75 |
76 | this.editor = CodeMirror.fromTextArea(this.refs.editor.getDOMNode(), {
77 | mode: 'javascript',
78 | lineNumbers: false,
79 | lineWrapping: false,
80 | matchBrackets: true,
81 | tabSize: 2,
82 | theme: 'solarized light',
83 | readOnly: this.props.readOnly
84 | });
85 | this.editor.on('change', this.handleChange);
86 | },
87 |
88 | componentDidUpdate() {
89 | if (this.props.readOnly) {
90 | this.editor.setValue(this.props.codeText);
91 | }
92 | },
93 |
94 | handleChange() {
95 | if (!this.props.readOnly && this.props.onChange) {
96 | this.props.onChange(this.editor.getValue());
97 | }
98 | },
99 |
100 | render() {
101 | // wrap in a div to fully contain CodeMirror
102 | let editor;
103 |
104 | if (IS_MOBILE) {
105 | let preStyles = {overflow: 'scroll'};
106 | editor = {this.props.codeText} ;
107 | } else {
108 | editor = ;
109 | }
110 |
111 | return (
112 |
113 | {editor}
114 |
115 | );
116 | }
117 | });
118 |
119 | const selfCleaningTimeout = {
120 | componentDidUpdate() {
121 | clearTimeout(this.timeoutID);
122 | },
123 |
124 | setTimeout() {
125 | clearTimeout(this.timeoutID);
126 | this.timeoutID = setTimeout.apply(null, arguments);
127 | }
128 | };
129 |
130 | const ReactPlayground = React.createClass({
131 | mixins: [selfCleaningTimeout],
132 |
133 | MODES: {JSX: 'JSX', JS: 'JS', NONE: null},
134 |
135 | propTypes: {
136 | codeText: React.PropTypes.string.isRequired,
137 | transformer: React.PropTypes.func,
138 | renderCode: React.PropTypes.bool
139 | },
140 |
141 | getDefaultProps() {
142 | return {
143 | transformer(code) {
144 | return babel.transform(code).code;
145 | }
146 | };
147 | },
148 |
149 | getInitialState() {
150 | return {
151 | mode: this.MODES.NONE,
152 | code: this.props.codeText
153 | };
154 | },
155 |
156 | handleCodeChange(value) {
157 | this.setState({code: value});
158 | this.executeCode();
159 | },
160 |
161 | handleCodeModeSwitch(mode) {
162 | this.setState({mode: mode});
163 | },
164 |
165 | handleCodeModeToggle(e) {
166 | let mode;
167 |
168 | e.preventDefault();
169 |
170 | switch (this.state.mode) {
171 | case this.MODES.NONE:
172 | mode = this.MODES.JSX;
173 | break;
174 | case this.MODES.JSX:
175 | default:
176 | mode = this.MODES.NONE;
177 | }
178 |
179 | this.setState({mode: mode});
180 | },
181 |
182 | compileCode() {
183 | return this.props.transformer(this.state.code);
184 | },
185 |
186 | render() {
187 | let classes = {
188 | 'bs-example': true
189 | };
190 | let toggleClasses = {
191 | 'code-toggle': true
192 | };
193 | let editor;
194 |
195 | if (this.props.exampleClassName){
196 | classes[this.props.exampleClassName] = true;
197 | }
198 |
199 | if (this.state.mode !== this.MODES.NONE) {
200 | editor = (
201 |
206 | );
207 | toggleClasses.open = true;
208 | }
209 | return (
210 |
217 | );
218 | },
219 |
220 | componentDidMount() {
221 | this.executeCode();
222 | },
223 |
224 | componentWillUpdate(nextProps, nextState) {
225 | // execute code only when the state's not being updated by switching tab
226 | // this avoids re-displaying the error, which comes after a certain delay
227 | if (this.state.code !== nextState.code) {
228 | this.executeCode();
229 | }
230 | },
231 |
232 | componentWillUnmount() {
233 | let mountNode = this.refs.mount.getDOMNode();
234 | try {
235 | React.unmountComponentAtNode(mountNode);
236 | } catch (e) {
237 | console.error(e);
238 | }
239 | },
240 |
241 | executeCode() {
242 | let mountNode = this.refs.mount.getDOMNode();
243 |
244 | try {
245 | React.unmountComponentAtNode(mountNode);
246 | } catch (e) {
247 | console.error(e);
248 | }
249 |
250 | try {
251 | let compiledCode = this.compileCode();
252 | if (this.props.renderCode) {
253 | React.render(
254 | ,
255 | mountNode
256 | );
257 | } else {
258 | /* eslint-disable */
259 | eval(compiledCode);
260 | /* eslint-enable */
261 | }
262 | } catch (err) {
263 | this.setTimeout(() => {
264 | React.render(
265 | {err.toString()} ,
266 | mountNode
267 | );
268 | }, 500);
269 | }
270 | }
271 | });
272 |
273 | export default ReactPlayground;
274 |
--------------------------------------------------------------------------------
/docs/src/Root.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Router from 'react-router';
3 |
4 | const Root = React.createClass({
5 | statics: {
6 |
7 | /**
8 | * Get the doctype the page expects to be rendered with
9 | *
10 | * @returns {string}
11 | */
12 | getDoctype() {
13 | return '';
14 | },
15 |
16 | /**
17 | * Get the list of pages that are renderable
18 | *
19 | * @returns {Array}
20 | */
21 | getPages() {
22 | return [
23 | 'index.html',
24 | 'getting-started.html',
25 | 'components.html'
26 | ];
27 | },
28 |
29 | renderToString(props) {
30 | return Root.getDoctype() +
31 | React.renderToString( );
32 | },
33 |
34 | /**
35 | * Get the Base url this app sits at
36 | * This url is appended to all app urls to make absolute url's within the app.
37 | *
38 | * @returns {string}
39 | */
40 | getBaseUrl() {
41 | return '/';
42 | }
43 | },
44 |
45 | render() {
46 | // Dump out our current props to a global object via a script tag so
47 | // when initialising the browser environment we can bootstrap from the
48 | // same props as what each page was rendered with.
49 | let browserInitScriptObj = {
50 | __html:
51 | `window.INITIAL_PROPS = ${JSON.stringify(this.props)};
52 | // console noop shim for IE8/9
53 | (function (w) {
54 | var noop = function () {};
55 | if (!w.console) {
56 | w.console = {};
57 | ['log', 'info', 'warn', 'error'].forEach(function (method) {
58 | w.console[method] = noop;
59 | });
60 | }
61 | }(window));`
62 | };
63 |
64 | let head = {
65 | __html: `ReactUI - Bootstrap based data components for React
66 |
67 |
68 |
69 | `
76 | };
77 |
78 | return (
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | );
90 | }
91 | });
92 |
93 |
94 | module.exports = Root;
95 |
--------------------------------------------------------------------------------
/docs/src/Routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Root from './Root';
4 | import HomePage from './HomePage';
5 | import GettingStartedPage from './GettingStartedPage';
6 | import ComponentsPage from './ComponentsPage';
7 | import NotFoundPage from './NotFoundPage';
8 |
9 | import {Route, DefaultRoute, NotFoundRoute} from 'react-router';
10 |
11 | export default (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | )
21 |
--------------------------------------------------------------------------------
/docs/src/Samples.js:
--------------------------------------------------------------------------------
1 | /* eslint no-path-concat: 0, no-var: 0 */
2 |
3 | export default {
4 | /* Editors */
5 | EditorsTextBoxBasicUsage: require('fs').readFileSync(__dirname + '/../examples/EditorsTextBoxBasicUsage.js', 'utf8'),
6 | EditorsTextBoxPrependAndAppend: require('fs').readFileSync(__dirname + '/../examples/EditorsTextBoxPrependAndAppend.js', 'utf8'),
7 | EditorsTextBoxPlaceholder: require('fs').readFileSync(__dirname + '/../examples/EditorsTextBoxPlaceholder.js', 'utf8'),
8 | EditorsTextBoxDisabledState: require('fs').readFileSync(__dirname + '/../examples/EditorsTextBoxDisabledState.js', 'utf8'),
9 |
10 | /* Data */
11 | DataTreeViewBasicUsage: require('fs').readFileSync(__dirname + '/../examples/DataTreeViewBasicUsage.js', 'utf8'),
12 |
13 | /* Navigation */
14 | NavigationVNavBasicUsage: require('fs').readFileSync(__dirname + '/../examples/NavigationVNavBasicUsage.js', 'utf8')
15 | };
16 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var $ = require('gulp-load-plugins')({lazy: true});
3 | var colors = require('colors');
4 | var del = require('del');
5 | var rimraf = require('rimraf-promise');
6 | var exec = require('child-process-promise').exec;
7 | var path = require('path');
8 | var fsp = require('fs-promise');
9 | var fsep = require('fs-extra-promise');
10 | var React = require('react');
11 | var Router = require('react-router');
12 | var path = require('path');
13 | var _ = require('lodash');
14 |
15 | gulp.task('build-css', function () {
16 | del(['./css/*.css', './css/*.map']);
17 | return gulp.src('src/less/*.less')
18 | .pipe($.plumber())
19 | .pipe($.sourcemaps.init())
20 | .pipe($.less())
21 | .pipe($.minifyCss())
22 | .pipe($.concat('react-ui.css'))
23 | .pipe($.sourcemaps.write('.'))
24 | .pipe(gulp.dest('css'));
25 | });
26 |
27 | gulp.task('build-lib', function () {
28 | return rimraf('./lib')
29 | .then(function (error) {
30 | var babelCli = 'babel --optional es7.objectRestSpread ./src --out-dir ./lib';
31 | return exec(babelCli).fail(function (error) {
32 | console.log(colors.red(error))
33 | });
34 | });
35 | });
36 |
37 | gulp.task('build-dist', function () {
38 | return rimraf('./dist').then(function (error) {
39 | var webpackCli = 'webpack --bail';
40 | var webpackCliProduction = 'webpack --bail -p';
41 | return exec(webpackCli).fail(function (error) {
42 | console.log(colors.red(error))
43 | })
44 | .then(function () {
45 | exec(webpackCliProduction).fail(function (error) {
46 | console.log(colors.red(error));
47 | });
48 | });
49 | });
50 | });
51 |
52 | gulp.task('build-amd', function () {
53 | function bowerConfig() {
54 | return Promise.all([
55 | fsp.readFile('./package.json')
56 | .then(function (json) {
57 | return JSON.parse(json);
58 | }),
59 | fsp.readFile('./tools/amd/bower.json')
60 | .then(function (template) {
61 | return _.template(template);
62 | })
63 | ])
64 | .then(function (args) {
65 | var package = args[0];
66 | var template = args[1];
67 | return template({pkg: package});
68 | })
69 | .then(function (config) {
70 | return fsp.writeFile('./amd/bower.json', config);
71 | });
72 | }
73 |
74 | return rimraf('./amd').then(function (error) {
75 | return fsp.mkdir('./amd').then(function () {
76 | return Promise.all([
77 | bowerConfig(),
78 | exec('babel --modules amd --optional es7.objectRestSpread ./src --out-dir ./amd/lib'),
79 | fsep.copy('./tools/amd/README.md', './amd/README.md'),
80 | fsep.copy('./LICENSE', './amd/LICENSE')
81 | ]);
82 | });
83 | });
84 | });
85 |
86 | gulp.task('build-docs', function () {
87 | // building the docs require React compilation, which is made automatically
88 | // by Babel, so it makes more sense to just run a node script.
89 | // Running it through Gulp it not easy.
90 | return exec('node run-babel ./tools/build.js');
91 | });
92 |
93 | gulp.task('build', ['build-css', 'build-docs', 'build-lib', 'build-dist', 'build-amd']);
94 |
95 | gulp.task('watch-css', function () {
96 | // TODO: This is not deterministic... Sometimes it doesn't work. For precision, use 'build-css'
97 | gulp.watch(['./src/less/*.less'], ['build-css']);
98 | });
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | /* eslint no-var: 0 */
2 | require('./register-babel');
3 |
4 | var webpackConfig = require('./webpack/test.config.js');
5 | var isCI = process.env.CONTINUOUS_INTEGRATION === 'true';
6 |
7 | module.exports = function (config) {
8 | config.set({
9 |
10 | basePath: '',
11 |
12 | frameworks: [
13 | 'mocha',
14 | 'chai',
15 | 'sinon'
16 | ],
17 |
18 | files: [
19 | 'test/index.js'
20 | ],
21 |
22 | preprocessors: {
23 | 'test/index.js': ['webpack', 'sourcemap']
24 | },
25 |
26 | webpack: webpackConfig,
27 |
28 | webpackMiddleware: {
29 | noInfo: isCI
30 | },
31 |
32 | reporters: ['mocha'],
33 |
34 | mochaReporter: {
35 | output: 'autowatch'
36 | },
37 |
38 | port: 9876,
39 |
40 | colors: true,
41 |
42 | logLevel: config.LOG_INFO,
43 |
44 | autoWatch: true,
45 |
46 | browsers: [ isCI ? 'PhantomJS' : 'Chrome' ],
47 |
48 | captureTimeout: 60000,
49 | browserNoActivityTimeout: 30000,
50 |
51 | singleRun: isCI
52 | });
53 | };
54 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "docs": "node run-babel docs/server.js",
4 | "test": "karma start --single-run",
5 | "test-watch": "karma start",
6 | "lint": "eslint src test docs tools webpack karma.conf.js webpack.config.js webpack.docs.js"
7 | },
8 | "name": "reactui",
9 | "description": "A components library for ReactJS. This is part of the Gearz project",
10 | "version": "0.0.7",
11 | "main": "./lib/index.js",
12 | "directories": {
13 | "test": "test"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/gearz-lab/react-ui.git"
18 | },
19 | "keywords": [
20 | "reactui",
21 | "react-ui",
22 | "react",
23 | "reactjs",
24 | "javascript"
25 | ],
26 | "authors": [
27 | "Andre Pena",
28 | "Miguel Angelo"
29 | ],
30 | "license": "MIT",
31 | "bugs": {
32 | "url": "https://github.com/gearz-lab/react-ui/issues"
33 | },
34 | "homepage": "https://github.com/gearz-lab/react-ui",
35 | "devDependencies": {
36 | "autoprefixer-core": "^5.1.11",
37 | "babel": "^4.7.0",
38 | "babel-core": "^4.7.4",
39 | "babel-eslint": "^2.0.2",
40 | "babel-loader": "^4.1.0",
41 | "bootstrap": "^3.3.4",
42 | "brfs": "^1.4.0",
43 | "chai": "^2.2.0",
44 | "child-process-promise": "^1.0.1",
45 | "client-loader": "0.0.1",
46 | "codemirror": "^5.0.0",
47 | "colors": "^1.0.3",
48 | "css-loader": "^0.9.1",
49 | "css-mqpacker": "^3.1.0",
50 | "cssgrace": "^2.0.2",
51 | "cssnext": "^1.3.0",
52 | "csswring": "^3.0.3",
53 | "del": "^1.1.1",
54 | "es5-shim": "^4.1.0",
55 | "eslint": "^0.18.0",
56 | "eslint-plugin-react": "^2.0.2",
57 | "express": "^4.12.3",
58 | "extract-text-webpack-plugin": "^0.3.8",
59 | "file-loader": "^0.8.1",
60 | "fs-extra": "^0.18.0",
61 | "fs-extra-promise": "^0.1.0",
62 | "fs-promise": "^0.3.1",
63 | "gulp": "^3.8.10",
64 | "gulp-concat": "^2.4.3",
65 | "gulp-less": "^3.0.3",
66 | "gulp-load-plugins": "^0.10.0",
67 | "gulp-minify-css": "^1.1.0",
68 | "gulp-plumber": "^1.0.0",
69 | "gulp-postcss": "^4.0.1",
70 | "gulp-rename": "^1.2.0",
71 | "gulp-sourcemaps": "^1.3.0",
72 | "js-beautify": "^1.5.5",
73 | "json-loader": "^0.5.1",
74 | "karma": "~0.12.32",
75 | "karma-chai": "^0.1.0",
76 | "karma-chrome-launcher": "~0.1.2",
77 | "karma-cli": "0.0.4",
78 | "karma-firefox-launcher": "~0.1.3",
79 | "karma-mocha": "~0.1.1",
80 | "karma-mocha-reporter": "^1.0.2",
81 | "karma-phantomjs-launcher": "~0.1.1",
82 | "karma-sinon": "^1.0.3",
83 | "karma-sourcemap-loader": "^0.3.4",
84 | "karma-webpack": "^1.5.0",
85 | "less": "^2.4.0",
86 | "less-loader": "^2.1.0",
87 | "lodash": "^3.5.0",
88 | "mocha": "^2.2.1",
89 | "postcss": "^4.1.5",
90 | "react": "^0.13.1",
91 | "react-bootstrap": "^0.20.3",
92 | "react-router": "^0.13.1",
93 | "rf-changelog": "^0.4.0",
94 | "rimraf": "^2.3.2",
95 | "rimraf-promise": "^1.0.0",
96 | "semver": "^4.3.1",
97 | "sinon": "^1.10.3",
98 | "style-loader": "^0.9.0",
99 | "transform-loader": "^0.2.1",
100 | "webpack": "^1.7.2",
101 | "webpack-dev-middleware": "^1.0.11",
102 | "yargs": "^3.5.4"
103 | },
104 | "dependencies": {
105 | "classnames": "^1.1.4"
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | React-UI
2 | ===
3 |
4 | [](https://gitter.im/gearz-lab/react-ui?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5 |
6 | 
7 |
8 | Bootstrap based data components for React. Check out the [website](http://reactui.com).
9 |
10 | Installing
11 | ---
12 |
13 | You can import the lib as AMD modules, CommonJS modules, or as a global JS script.
14 |
15 | First add the Bootstrap CSS to your project; check [here](http://getbootstrap.com/getting-started/) if you have not already done that. Then:
16 |
17 | **CommonJS**
18 |
19 | Installing:
20 |
21 | $ npm install react
22 | $ npm install reactui
23 |
24 | Using:
25 |
26 | var Textbox = require('reactui/lib/Textbox');
27 | // or
28 | var Textbox = require('reactui').Textbox;
29 |
30 | **AMD**
31 |
32 | Installing:
33 |
34 | $ bower install react
35 | $ bower install reactui
36 |
37 | Using:
38 |
39 | define(['reactui/lib/Textbox'], function(Alert) { ... });
40 | // or
41 | define(['reactui'], function(ReactUI) { var Textbox = ReactUI.Textbox; ... });
42 |
43 | **Browser globals**
44 |
45 | Installing:
46 |
47 | Include the file `react-ui.js` or `react-ui.min.js` from the `dist` folder into your app. All components are exported
48 | to the `window.ReactUI` global.
49 |
50 | Using:
51 |
52 | var Textbox = ReactUI.Textbox;
53 |
54 | Building
55 | ---
56 |
57 | Make sure you have `Git` and `Node.js` installed.
58 |
59 | Clone the rep:
60 |
61 | $ git clone https://github.com/gearz-lab/react-ui.git
62 |
63 | Install Webpack:
64 |
65 | $ npm install -g webpack
66 |
67 | Install local dependencies:
68 |
69 | $ npm install
70 |
71 | Build:
72 |
73 | $ npm run build
74 |
75 | Build docs only
76 |
77 | $ npm run docs-build
78 |
79 | Run the docs:
80 |
81 | $ npm run docs
82 |
--------------------------------------------------------------------------------
/register-babel.js:
--------------------------------------------------------------------------------
1 | require('babel/register')({
2 | ignore: /node_modules/,
3 | optional: ['es7.objectRestSpread']
4 | });
5 |
--------------------------------------------------------------------------------
/run-babel:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /* eslint no-var: 0 */
3 | var path = require('path');
4 | require('./register-babel');
5 | var mod = require(path.join(__dirname, process.argv[2]));
6 |
7 | if (typeof mod === 'function') {
8 | mod();
9 | }
10 |
--------------------------------------------------------------------------------
/samples/codeSample.jsx:
--------------------------------------------------------------------------------
1 | var CodeSample = React.createClass({
2 | render: function () {
3 | console.log(this.props.sourceCode.__html);
4 | return (
5 |
6 |
7 | { this.props.component }
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 | });
--------------------------------------------------------------------------------
/samples/layout/formGroup/formGroupSample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FormGroup
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/samples/layout/formGroup/formGroupSample.jsx:
--------------------------------------------------------------------------------
1 | var FormGroupSample = React.createClass({
2 | getInitialState: function () {
3 | return {
4 | };
5 | },
6 | render: function () {
7 |
8 | return (
9 |
10 |
11 |
FormGroup
12 |
13 |
Wraps a label and a component. The label automatically points to the child component using the for
attribute.
14 |
15 | { /* BASICS */ }
16 |
Basic usage
17 |
The FormGroup component renders a label and the component passed in as a child.
18 |
19 |
22 |
26 |
27 | }
28 | sourceCode= {{
29 | __html: "\n\
30 | \n\
31 | "
32 | }}
33 | />
34 |
35 | { /* INVALID STATE */ }
36 | Invalid state
37 | If the child component is invalid, the FormGroup adds a has-error
class, highlighting both the label
and the child component.
38 |
39 |
42 |
47 |
48 | }
49 | sourceCode= {{
50 | __html: "\n\
51 | \n\
52 | "
53 | }}
54 | />
55 |
56 |
57 |
58 | );
59 | }
60 | });
61 |
--------------------------------------------------------------------------------
/samples/layout/stackPanel/stackPanelSample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | StackPanel
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/samples/layout/stackPanel/stackPanelSample.jsx:
--------------------------------------------------------------------------------
1 | var StackPanelSample = React.createClass({
2 | getInitialState: function () {
3 | return {
4 | };
5 | },
6 | render: function () {
7 |
8 | return (
9 |
10 |
11 |
StackPanel
12 |
13 |
Organizes it's content in a stack. Currently only supports vertical orientation.
14 |
15 | { /* BASICS */ }
16 |
Basic usage
17 |
The StackPanel component renders it's children vertically stacked
18 |
19 |
22 |
23 |
27 |
28 |
29 |
33 |
34 |
35 | }
36 | sourceCode= {{
37 | __html: "\n\
38 | \n\
39 | \n\
43 | \n\
44 | \n\
45 | \n\
49 | \n\
50 | "
51 | }}
52 | />
53 |
54 |
55 |
56 | );
57 | }
58 | });
--------------------------------------------------------------------------------
/samples/layout/tabControl/tabControlSample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TabControl
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/samples/layout/tabControl/tabControlSample.jsx:
--------------------------------------------------------------------------------
1 | var TabControlSample = React.createClass({
2 | render: function () {
3 |
4 | return (
5 |
6 |
7 |
TabControl
8 |
9 |
Displays data in tabs
10 |
11 | { /* BASICS */ }
12 |
Basic usage
13 |
Example of a tab
14 |
15 |
18 |
19 | First tab content
20 |
21 |
22 |
23 | Second tab content
24 |
25 |
26 |
27 | }
28 | sourceCode= {{
29 | __html: ""
30 | }}
31 | />
32 |
33 | );
34 | }
35 | });
--------------------------------------------------------------------------------
/samples/layout/treeView/treeViewSample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TreeView
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/samples/layout/treeView/treeViewSample.jsx:
--------------------------------------------------------------------------------
1 | var TreeViewSample = React.createClass({
2 | getInitialState: function () {
3 | return {
4 |
5 | treeView1: {
6 | app: {
7 | display: "ApplicationControl",
8 | nodes: {
9 | page: {
10 | display: "PageControl",
11 | nodes: {
12 | editPanel: {
13 | display: "StackPanelControl",
14 | nodes: {
15 | panel: {
16 | display: "Panel (Main)",
17 | nodes: {
18 | name: {
19 | display: "TexboxControl (Name)"
20 | },
21 | dateOfBirth: {
22 | display: "DatePickerControl (Date of Birth)"
23 | },
24 | gender: {
25 | display: "ToggleButtonControl (Gender)"
26 | }
27 | }
28 | },
29 | panel2: {
30 | display: "Panel (Additional Info)",
31 | nodes: {
32 | isResponsible: {
33 | display: "ToogleButtonControl (Is Responsible)"
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 | }
42 | }
43 | },
44 |
45 | treeView2: [
46 | {
47 | display: "ApplicationControl",
48 | nodes: [
49 | {
50 | display: "PageControl",
51 | nodes: [
52 | {
53 | display: "StackPanelControl",
54 | nodes: [
55 | {
56 | display: "Panel (Main)",
57 | nodes: [
58 | {
59 | display: "TexboxControl (Name)"
60 | },
61 | {
62 | display: "DatePickerControl (Date of Birth)"
63 | },
64 | {
65 | display: "ToggleButtonControl (Gender)"
66 | }
67 | ]
68 | },
69 | {
70 | display: "Panel (Additional Info)",
71 | nodes: [
72 | {
73 | display: "ToogleButtonControl (Is Responsible)"
74 | }
75 | ]
76 | }
77 | ]
78 | }
79 | ]
80 | }
81 | ]
82 | }
83 | ]
84 | };
85 | },
86 | render: function () {
87 |
88 | return (
89 |
90 |
91 |
TreeView
92 |
93 |
Displays hierarchical data
94 |
95 | { /* BASICS */ }
96 |
Basic usage
97 |
Example of hierarchical data
98 |
99 |
102 | }
103 | sourceCode= {{
104 | __html: ""
105 | }}
106 | />
107 |
108 | Notes: when using internal state to store node attributes,
109 | the values outlive the nodes sent through the nodes
prop.
110 | This means that even if a collapsed node is removed from the tree,
111 | it is still marked as being collapsed, so that if the node is reinserted in the tree
112 | it appears collapsed again.
113 |
114 |
115 | );
116 | }
117 | });
--------------------------------------------------------------------------------
/samples/meta/componentBuilder/componentBuilderSample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Textbox
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/samples/meta/componentBuilder/componentBuilderSample.jsx:
--------------------------------------------------------------------------------
1 | var ComponentBuilderSample = React.createClass({
2 | render: function () {
3 |
4 | return (
5 |
6 |
7 |
ComponentBuilder
8 |
9 | { /* EXTERNAL PROPS */ }
10 |
Using a JSON like object
11 |
The component builder can be used to render a JSON representation of controls.
12 |
In fact it is a live Javascript object, so you can pass in event hanlers to the components if you wish to.
13 |
14 |
43 | }
44 | sourceCode= {{
45 | __html: "\
46 |
\
73 | "
74 | }}
75 | />
76 |
77 |
78 | );
79 | }
80 | });
--------------------------------------------------------------------------------
/samples/navigation/link/about.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Link Component Experiment
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/samples/navigation/link/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Link Component Experiment
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/samples/navigation/link/pages.jsx:
--------------------------------------------------------------------------------
1 | function getRouter(){
2 | var router =
3 | new Router({
4 | basePath: "gearz-components/samples/navigation",
5 |
6 | // To support areas, we need to indicate a global default value for the `area` attribute.
7 | // But don't think that `area` is a special key-word, it isn't. The `globals` definitions
8 | // just takes name-value pairs, and make these pairs globally available to all routes.
9 | // This means that, by adding `area: ""` to the list,
10 | globals: {area: ""},
11 | mixins: [routerMixins.location]
12 | })
13 | .addRoute("with-id", {
14 | UriPattern: "{controller}/{action}/{id}.html",
15 | Constraints: {id: "^\\d+$"}
16 | })
17 | .addRoute("about", {
18 | UriPattern: "{controller}/about.html",
19 | Defaults: { action: "about", pageComponent: "AboutPage", isClient: true }
20 | })
21 | .addRoute("index", {
22 | UriPattern: "{controller}/index.html",
23 | Defaults: { action: "index", pageComponent: "IndexPage", isClient: true }
24 | })
25 | .addRoute("normal", {
26 | UriPattern: "{controller}/{action}.html"
27 | })
28 | .addRoute("no-action", {
29 | UriPattern: "{controller}.html"
30 | });
31 |
32 | router.setCurrentLocation(document.location.pathname);
33 |
34 | return router;
35 | }
36 |
37 |
38 | var IndexPage = React.createClass({
39 | render: function () {
40 | var router = getRouter();
41 | return (
42 |
43 |
link.jsx
44 | Index
45 | using external router (request using a router)
46 |
51 | about page
52 |
53 | using internal router (normal browser request)
54 |
55 |
56 | );
57 | }
58 | });
59 |
60 |
61 | var AboutPage = React.createClass({
62 | render: function () {
63 | var router = getRouter();
64 | return (
65 |
66 |
link.jsx
67 | About
68 | using external router (request using a router)
69 |
74 | about page
75 |
76 | using internal router (normal browser request)
77 |
78 |
79 | );
80 | }
81 | });
82 |
--------------------------------------------------------------------------------
/samples/navigation/pager/pagerSample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Pager component
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
17 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
40 |
41 |
--------------------------------------------------------------------------------
/samples/navigation/pager/pagerSample.jsx:
--------------------------------------------------------------------------------
1 | var PagerSample = React.createClass({
2 | getInitialState: function () {
3 | return {
4 | pager1: {
5 | page: 1,
6 | pageSize: 10,
7 | count: 45
8 | },
9 | pager2: {
10 | page: 1,
11 | pageSize: 10,
12 | count: 88
13 | }
14 | };
15 | },
16 | render: function () {
17 |
18 | return (
19 |
20 |
21 |
pager.jsx
22 |
23 |
using external props (generic event)
24 |
38 |
39 |
using external props (specific event)
40 |
54 |
55 |
using internal state (no events at all)
56 |
60 |
61 |
62 | );
63 | }
64 | });
--------------------------------------------------------------------------------
/samples/samples.css:
--------------------------------------------------------------------------------
1 | .gearz-example {
2 | position: relative;
3 | padding: 45px 15px 15px;
4 | margin: 0 0 15px;
5 | border-color: #e5e5e5 #eee #eee;
6 | border-style: solid;
7 | border-width: 1px 0;
8 | border-width: 1px;
9 | border-radius: 4px 4px 0 0;
10 | }
11 |
12 | .gearz-example:after {
13 | position: absolute;
14 | top: 15px;
15 | left: 15px;
16 | font-size: 12px;
17 | font-weight: 700;
18 | color: #959595;
19 | text-transform: uppercase;
20 | letter-spacing: 1px;
21 | content: "Example";
22 | }
23 |
24 | .gearz-example + .highlight, .gearz-example + .zero-clipboard + .highlight {
25 | margin: -15px -15px 15px;
26 | border-width: 0 0 1px;
27 | border-radius: 0;
28 | }
29 |
30 | .gearz-example + .highlight, .gearz-example + .zero-clipboard + .highlight {
31 | margin-top: -16px;
32 | margin-right: 0;
33 | margin-left: 0;
34 | border-width: 1px;
35 | border-bottom-right-radius: 4px;
36 | border-bottom-left-radius: 4px;
37 | }
38 |
39 | .highlight {
40 | padding: 9px 14px;
41 | margin-bottom: 14px;
42 | background-color: #f7f7f9;
43 | border: 1px solid #e1e1e8;
44 | border-radius: 4px;
45 | }
46 |
47 | .highlight pre {
48 | padding: 0;
49 | margin-top: 0;
50 | margin-bottom: 0;
51 | word-break: normal;
52 | white-space: nowrap;
53 | background-color: transparent;
54 | border: 0;
55 | }
56 |
57 | .highlight pre code:first-child {
58 | display: inline-block;
59 | padding-right: 45px;
60 | }
61 |
62 | .highlight pre code {
63 | font-size: inherit;
64 | color: #333;
65 | }
--------------------------------------------------------------------------------
/src/components/data/treeView/treeRow.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var gearzMixin = require("../../gearz.mixin.js");
3 |
4 | var TreeRow = React.createClass({
5 | mixins: [gearzMixin],
6 | propTypes: {
7 | onAnyChange: React.PropTypes.func,
8 | onCollapsedChange: React.PropTypes.func,
9 | path: React.PropTypes.array.isRequired
10 | },
11 | hasChildren: function(nodes) {
12 | if (Array.isArray(nodes))
13 | return nodes.length>0;
14 | if (typeof nodes == 'object')
15 | return Object.keys(nodes).length>0;
16 | return !!nodes;
17 | },
18 | cardinality: function(nodes) {
19 | if (Array.isArray(nodes))
20 | return nodes.length;
21 | if (typeof nodes == 'object')
22 | return Object.keys(nodes).length;
23 | return null;
24 | },
25 | render: function () {
26 |
27 | var nodes = this.get("nodes");
28 | var collapsed = !!this.get("collapsed");
29 | var display = this.get("display");
30 | var path = this.get("path");
31 |
32 | var hasChildren = this.hasChildren(nodes);
33 | var cardinality = this.cardinality(nodes);
34 |
35 | var indentation = 10 + path.length * 15 + "px";
36 |
37 | return (
38 |
39 |
46 |
47 |
48 | { display }
49 |
50 | { hasChildren && cardinality !== null ? { cardinality } : null }
51 |
52 | );
53 | }
54 | });
55 |
56 | module.exports = TreeRow;
--------------------------------------------------------------------------------
/src/components/data/treeView/treeView.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 | var TreeRow = require("./treeRow.jsx");
3 | var gearzMixin = require("../../gearz.mixin.js");
4 |
5 | var TreeView = React.createClass({
6 | mixins: [gearzMixin],
7 |
8 | propTypes: {
9 | nodes: React.PropTypes.object.isRequired
10 | },
11 |
12 | /**
13 | * ReactJS function to get the component initial state.
14 | * @returns {{nodesCache: {}}}
15 | */
16 | getInitialState: function () {
17 | return {
18 | nodesCache: {}
19 | };
20 | },
21 |
22 | /**
23 | * Returns a list containing information about all nodes in a nodes collection.
24 | * @param nodes {Object | Array}
25 | * An object or array representing a nodes collection.
26 | * Each key of this object or array, contains a child node.
27 | * A child node may have an arbitrary set of properties, but children must be in a property called 'nodes'.
28 | * @param path {Array=}
29 | * Path to the node to which the passed nodes collection belongs.
30 | * @param output {Array=}
31 | * Optional array to which items will be pushed, and then returned.
32 | * If none is passed, then a new array is created, and returned.
33 | * @returns {Array}
34 | * An array containing information about all nodes in the tree analysed in pre-order.
35 | * Take a look at http://en.wikipedia.org/wiki/Tree_traversal#Pre-order
36 | */
37 | flattenNodes: function (nodes, path, output) {
38 | var flatData = output || [];
39 |
40 | for (var key in nodes)
41 | if (nodes.hasOwnProperty(key)) {
42 | var info = {
43 | node: nodes[key],
44 | path: (path || []).concat(key)
45 | };
46 | flatData.push(info);
47 | this.flattenNodes(info.node.nodes, info.path, flatData);
48 | }
49 |
50 | return flatData;
51 | },
52 |
53 | /**
54 | * Gets the nodes corresponding to each key in a `path`.
55 | * E.g.:
56 | * `nodes` = {A: {B: {C: {}}}}
57 | * `path` = ["A", "B", "C"]
58 | * `return` => [{B: {C: {}}}]
59 | * @param nodes {Object | Array}
60 | * An object or array representing a nodes collection.
61 | * Each key of this object or array, contains a child node.
62 | * A child node may have an arbitrary set of properties, but children must be in a property called 'nodes'.
63 | * @param path {Array}
64 | * Path to get the nodes from. Each `path` key will be mapped to the corresponding node in the tree.
65 | * @param output {Array=}
66 | * Optional array to which items will be pushed, and then returned.
67 | * If none is passed, then a new array is created, and returned.
68 | * @returns {Array}
69 | * An array containing the nodes that correspond to each `path` key.
70 | */
71 | getPathNodes: function (nodes, path, output) {
72 | var sequence = output || [];
73 |
74 | for (var it = 0; it < path.length; it++) {
75 | var key = path[it];
76 | if (!nodes || !nodes.hasOwnProperty(key))
77 | break;
78 | var node = nodes[key];
79 | sequence.push(node);
80 | nodes = node.nodes;
81 | }
82 |
83 | return sequence;
84 | },
85 |
86 | /**
87 | * Merges two nodes, the main node with all needed descendants and values,
88 | * and another source with default descendants and values.
89 | * @param main {Object|Array}
90 | * An object or array containing the main descendants and values,
91 | * that have precedence over the descendants and values from the default node.
92 | * @param defaults {Object|Array}
93 | * An object or array containing default descendants and values,
94 | * that are alternatives for elements missing from the main node.
95 | * @returns {Object|Array}
96 | * An object or array containing the merged descendants and values from the main node and the default node.
97 | */
98 | mergeNodeValues: function (main, defaults) {
99 | var result = typeof main == 'object' ? {} : Array.isArray(main) ? [] : null;
100 |
101 | if (!result)
102 | throw new Error("Argument `main` must be an object or an array.");
103 |
104 | if (typeof defaults != 'object' && Array.isArray(defaults))
105 | throw new Error("Argument `defaults` must be an object or an array.");
106 |
107 | for (var key2 in defaults)
108 | if (defaults.hasOwnProperty(key2) && key2 != "nodes")
109 | result[key2] = defaults[key2];
110 |
111 | for (var key3 in main)
112 | if (main.hasOwnProperty(key3))
113 | if (defaults.hasOwnProperty(key3) && key3 == "nodes")
114 | result[key3] = this.mergeNodeCollections(main[key3], defaults[key3]);
115 | else
116 | result[key3] = main[key3];
117 |
118 | return result;
119 | },
120 |
121 | /**
122 | * Merges two node-collections.
123 | * @param main {Object}
124 | * @param defaults {Object}
125 | * @returns {Object}
126 | */
127 | mergeNodeCollections: function (main, defaults) {
128 | var result = {};
129 |
130 | for (var key1 in main)
131 | if (main.hasOwnProperty(key1))
132 | if (defaults.hasOwnProperty(key1))
133 | result[key1] = this.mergeNodeValues(main[key1], defaults[key1]);
134 | else
135 | result[key1] = main[key1];
136 |
137 | return result;
138 | },
139 |
140 | /**
141 | * Handles all node changes and triggers events indicating what node changed,
142 | * and what value of the node changed.
143 | * @param eventObject {Object}
144 | * An object containing information about the event.
145 | */
146 | onNodeChange: function (eventObject) {
147 | function mergeOrCreate(previousValue) {
148 | return function (value) {
149 | return React.addons.update(typeof value != 'object' ? {} : value, previousValue);
150 | };
151 | }
152 |
153 | // Input:
154 | // eventObject.path = ["app","page","editPanel"]
155 | // eventObject.key = "collapsed"
156 | // eventObject.value = true
157 | // Output:
158 | // merger = {nodes: {$mergeOrCreate: {app: {$mergeOrCreate: {page: {$mergeOrCreate: {editPanel: {$mergeOrCreate: {collapsed: {$set: true}}}}}}}}}}
159 | let setter = {};
160 | setter[eventObject.key] = {$set: eventObject.value};
161 | let merger = eventObject.path.reduceRight((innerMerger, currentPathItem) => {
162 | let itemMerger = {};
163 | itemMerger[currentPathItem] = {$apply: mergeOrCreate(innerMerger)};
164 | let nodesMerger = {nodes: {$apply: mergeOrCreate(itemMerger)}};
165 | return nodesMerger;
166 | }, setter);
167 |
168 | // determining what is the new state
169 | let newState = React.addons.update(this.state, {nodesCache: merger.nodes});
170 | this.setState(newState);
171 |
172 | // calling external event handlers
173 | if (eventObject.trigger(eventObject.genericEventName) || eventObject.trigger(eventObject.specificEventName)) {
174 | return;
175 | }
176 | },
177 |
178 | /**
179 | * Determined whether a node visible or not.
180 | * A node is invisible when any ancestor node is collapsed.
181 | * @param nodes {Object|Array}
182 | * An object or array that represents the root node, from which path nodes will be taken.
183 | * @param path {Array}
184 | * An array containing the path components into the passed node.
185 | * @returns {boolean}
186 | * True if the node is hidden; otherwise false.
187 | */
188 | isNodeHidden: function (nodes, path) {
189 | var ancestors = this.getPathNodes(nodes, path);
190 | ancestors.pop();
191 | return ancestors
192 | .map(x => x.collapsed)
193 | .reduce((a, b) => a || b, false);
194 | },
195 |
196 | /**
197 | * ReactJS rendering function.
198 | * @returns {XML}
199 | */
200 | render: function () {
201 | var nodes = this.get("nodes");
202 |
203 | var mergedNodes = this.mergeNodeCollections(nodes, this.state.nodesCache);
204 | var flattenNodes = this.flattenNodes(mergedNodes);
205 |
206 | var children = flattenNodes.map(info => (
207 | this.isNodeHidden(mergedNodes, info.path) ?
208 | null :
209 | {
215 | var newPath = Object.freeze([].concat(info.path));
216 | var eventData = e.merge({
217 | target: this,
218 | path: newPath,
219 | specificEventName: "Node" + e.specificEventName
220 | });
221 | this.onNodeChange(eventData);
222 | }}
223 | />
224 | ));
225 |
226 | return (
227 |
230 | );
231 | }
232 | });
233 |
234 | module.exports = TreeView;
--------------------------------------------------------------------------------
/src/components/editors/textbox/textbox.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import gearzMixin from '../../gearz.mixin';
3 |
4 | const Textbox = React.createClass({
5 | mixins: [gearzMixin],
6 | propTypes: {
7 | // The input id
8 | id: React.PropTypes.string.isRequired,
9 |
10 | // The textbox value
11 | value: React.PropTypes.string,
12 |
13 | // Event raised when the textbox value is changed by the user
14 | onChange: React.PropTypes.func,
15 |
16 | // Text to be prepended to the component
17 | prependText: React.PropTypes.string,
18 |
19 | // Text to be appended to the component
20 | appendText: React.PropTypes.string,
21 |
22 | // Placeholder
23 | placeholder: React.PropTypes.string,
24 |
25 | // Whether or not the component is disabled
26 | disabled: React.PropTypes.bool,
27 |
28 | // Whether or not the component is invalid
29 | invalid: React.PropTypes.bool,
30 |
31 | // Whether or not the component is required
32 | required: React.PropTypes.bool,
33 |
34 | // Event raised when anything is changed in the component
35 | onAnyChange: React.PropTypes.func
36 | },
37 | render: function () {
38 |
39 | let id = this.get('id');
40 | let value = this.get('value');
41 | let prependText = this.get('prependText');
42 | let appendText = this.get('appendText');
43 | let placeholder = this.get('placeholder');
44 | let disabled = this.get('disabled');
45 |
46 | let input = ( );
59 |
60 | // if there's any add-on
61 | if (prependText || appendText) {
62 | return (
63 |
64 | { prependText ?
{ prependText }
: null }
65 | { input }
66 | { appendText ?
{ appendText }
: null }
67 |
68 | );
69 | }
70 | else {
71 | return input;
72 | }
73 | }
74 | });
75 |
76 | export default Textbox;
77 |
--------------------------------------------------------------------------------
/src/components/gearz.mixin.js:
--------------------------------------------------------------------------------
1 | var gearz = {
2 | getInitialState: function () {
3 | return {};
4 | },
5 | // 'get' is used to get properties that may be stored in 'state' or in 'props'
6 | // this happens when a value is defined throw a 'setter'
7 | get: function (propName) {
8 | return this.state.hasOwnProperty(propName)
9 | ? this.state[propName]
10 | : this.props[propName];
11 | },
12 | /**
13 | * 'set' is used to define the value of a property, given the name of the
14 | * property and the new value to assign. It can also receive a third parameter,
15 | * representing the context of the change. For example: you can pass the
16 | * event data when the change is caused by a DOM event.
17 | * This will raise events that can be listened by parent components,
18 | * so that they know when the child component changes.
19 | * @param propName {String}
20 | * Name of the property that is being changed.
21 | * This is usually the name of a `prop` of the component.
22 | * @param newValue {*}
23 | * @param context {Object}
24 | */
25 | set: function (propName, newValue, context) {
26 | var prevDef = false, isOriginalNewValue = true;
27 |
28 | var name = propName == "value" ? "" : propName[0].toUpperCase() + propName.substr(1);
29 | var specificEventName = name + "Change";
30 |
31 | // properties of an event object that cannot be overridden
32 | var defaultProps = [
33 | "previous",
34 | "context",
35 | "originalNewValue",
36 | "genericEventName",
37 | "value",
38 | "isOriginalNewValue",
39 | "preventDefault",
40 | "merge",
41 | "trigger"];
42 |
43 | var eventObject = {
44 | target: this,
45 | key: propName,
46 | previous: this.props[propName],
47 | context: context,
48 | originalNewValue: newValue,
49 | specificEventName: specificEventName,
50 | genericEventName: "AnyChange",
51 | get value() {
52 | return newValue;
53 | },
54 | set value(value) {
55 | newValue = value;
56 | isOriginalNewValue = this.originalNewValue === newValue;
57 | },
58 | get isOriginalNewValue() {
59 | return isOriginalNewValue;
60 | },
61 |
62 | /**
63 | * Prevents the default behavior of storing the data internally in the state of the component.
64 | * This is generally used to indicate that the state is going to be stored externally from the component.
65 | */
66 | preventDefault: function () {
67 | prevDef = true;
68 | },
69 |
70 | /**
71 | * Merges this event object, with another object, in order to include additional data to this event object.
72 | * You cannot override the default properties.
73 | * @param other {object} Containing properties to merged in a new event object.
74 | * @returns {object} The merging result between this event object and another object.
75 | */
76 | merge: function (other) {
77 | var result = Object.create(this);
78 |
79 | for (var key in other)
80 | if (other.hasOwnProperty(key) && defaultProps.indexOf(key) < 0)
81 | Object.defineProperty(result, key, {value: other[key]});
82 |
83 | return Object.freeze(result);
84 | },
85 |
86 | /**
87 | * Triggers an event handler (a function), if preventDefault was not called yet,
88 | * and returning whether the handler called preventDefault itself.
89 | * @param eventHandler {Function|String}
90 | * Function representing an event handler that will receive the current event object;
91 | * Or a string representing a named event, that may be present in a `prop`.
92 | * @returns {Boolean}
93 | * Indicates whether preventDefault was called.
94 | */
95 | trigger: function (eventHandler) {
96 | if (typeof eventHandler == 'string')
97 | eventHandler = this.target.props["on" + eventHandler];
98 |
99 | if (!prevDef && typeof eventHandler == 'function')
100 | eventHandler(this);
101 |
102 | return prevDef;
103 | }
104 | };
105 |
106 | Object.freeze(eventObject);
107 |
108 | if (eventObject.trigger(specificEventName))
109 | return;
110 |
111 | if (eventObject.trigger(this.props.onAnyChange))
112 | return;
113 |
114 | var newState = {};
115 | newState[propName] = newValue;
116 | this.setState(newState);
117 | },
118 | // 'setter' is used to create a function that changes the value of an
119 | // attribute used by this component in response to a DOM event,
120 | // raising other events to notify parent components,
121 | // and with a default behaviour of storing changes
122 | // in the component internal 'state'
123 | setter: function (propName, newValue) {
124 | return (function (e) {
125 | return this.set(propName, newValue, {domEvent: e});
126 | }).bind(this);
127 | }
128 | };
129 |
130 | module.exports = gearz;
--------------------------------------------------------------------------------
/src/components/layout/formGroup/formGroup.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var gearzMixin = require("../../gearz.mixin");
3 |
4 | var FormGroup = React.createClass({
5 | mixins: [gearzMixin],
6 | propTypes: {
7 | // The display name
8 | displayName: React.PropTypes.string.isRequired,
9 | // The child (single one) that represents the inner-control to be rendered inside the FormGroup
10 | children: React.PropTypes.element.isRequired
11 | },
12 | render: function () {
13 | var displayName = this.get("displayName");
14 | var childId = this.props.children.props.id;
15 | var childInvalid = this.props.children.props.invalid;
16 | return (
17 |
18 | {displayName}
19 | { this.props.children }
20 |
21 | );
22 | }
23 | });
24 |
25 | module.exports = FormGroup;
--------------------------------------------------------------------------------
/src/components/layout/stackPanel/stackPanel.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var gearzMixin = require("../../gearz.mixin");
3 |
4 | var StackPanel = React.createClass({
5 | mixins: [gearzMixin],
6 | propTypes: {
7 | },
8 | render: function () {
9 | return (
10 |
11 | { this.props.children }
12 |
13 | );
14 | }
15 | });
16 |
17 | module.exports = StackPanel;
--------------------------------------------------------------------------------
/src/components/layout/tabControl/tab.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var gearzMixin = require("../../gearz.mixin");
3 |
4 | var Tab = React.createClass({
5 | mixins: [gearzMixin],
6 | propTypes: {
7 | // the tab name
8 | name: React.PropTypes.string.isRequired,
9 | // the tab display name
10 | displayName: React.PropTypes.string.isRequired,
11 | },
12 |
13 | render: function () {
14 | return
15 | { this.props.children }
16 |
17 | }
18 | });
19 |
20 | module.exports = Tab;
--------------------------------------------------------------------------------
/src/components/layout/tabControl/tabControl.css:
--------------------------------------------------------------------------------
1 | .tabControl a {
2 | cursor: pointer;
3 | }
4 |
5 | .tabControl-content {
6 | padding: 10px;
7 | }
8 |
9 | .tabControl .tab {
10 | display: none;
11 | }
12 |
13 | .tabControl .tab.activeTab {
14 | display: block;
15 | }
--------------------------------------------------------------------------------
/src/components/layout/tabControl/tabControl.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var TabHeader = require("./tabHeader.jsx");
3 | var gearzMixin = require("../../gearz.mixin");
4 |
5 | var TabControl = React.createClass({
6 | mixins: [gearzMixin],
7 | propTypes: {
8 | // current tab
9 | activeTab: React.PropTypes.string.isRequired,
10 | // A collection of Tab controls
11 | children: React.PropTypes.array.isRequired
12 | },
13 | getInitialState: function () {
14 | return {}
15 | },
16 | activeTabChanged: function(e) {
17 | this.set("activeTab", e.value);
18 | },
19 | render: function() {
20 | var tabs = this.props.children;
21 | var activeTab = this.get("activeTab");
22 | var tabHeaderItems = [];
23 | for(var i = 0; i < tabs.length; i++)
24 | {
25 | var tab = tabs[i];
26 | tabHeaderItems.push({name: tab.props.name, displayName: tab.props.displayName })
27 | }
28 |
29 | return
30 |
31 |
32 | { this.props.children.map(function(item) {
33 | var tabName = item.props.name;
34 | return
35 | {item}
36 |
;
37 | }) }
38 |
39 |
;
40 | }
41 | });
42 |
43 | module.exports = TabControl;
--------------------------------------------------------------------------------
/src/components/layout/tabControl/tabHeader.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var gearzMixin = require("../../gearz.mixin");
3 |
4 | var TabHeader = React.createClass({
5 | mixins: [gearzMixin],
6 | propTypes: {
7 | // Active tab name
8 | activeTab: React.PropTypes.string.isRequired,
9 | // tab array. Each element is of type { name (string), displayName (string) }
10 | tabs: React.PropTypes.array.isRequired
11 | },
12 |
13 | render: function () {
14 | var that = this;
15 | var activeTab = this.get("activeTab");
16 | var tabs = this.get("tabs");
17 |
18 | return
25 | }
26 | });
27 |
28 | module.exports = TabHeader;
--------------------------------------------------------------------------------
/src/components/meta/componentBuilder/componentBuilder.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var gearzMixin = require("../../gearz.mixin");
3 |
4 | var ComponentBuilder = React.createClass({
5 | mixins: [gearzMixin],
6 | propTypes: {},
7 | isUpper: function (x) {
8 | var u = x.toUpperCase(),
9 | l = x.toLowerCase();
10 | return x == u && x != l;
11 | },
12 | isLower: function (x) {
13 | var u = x.toUpperCase(),
14 | l = x.toLowerCase();
15 | return x == l && x != u;
16 | },
17 | getComponents: function (data) {
18 | if (!data)
19 | return [];
20 | var array = Array.isArray(data) ? data : [data];
21 | return array.map(function (item) {
22 | if (!item)
23 | return null;
24 |
25 | if (typeof item == 'string')
26 | return item;
27 |
28 | var args = [];
29 |
30 | // React convention:
31 | // first letter lower-case named components are HTML tags
32 | // first letter upper-case named components are custom React components
33 | if (this.isUpper(item.type[0]) && window[item.type])
34 | args.push(window[item.type]);
35 | else if (this.isLower(item.type[0]))
36 | args.push(item.type);
37 | else
38 | return null;
39 |
40 | args.push(item.props);
41 | args.push.apply(args, this.getComponents(item.children));
42 |
43 | return React.createElement.apply(React, args);
44 | }.bind(this));
45 | },
46 | render: function () {
47 | var data = this.get("data");
48 | if (!data)
49 | return (No components to render
);
50 | var components = this.getComponents(data);
51 | return (
52 | {components}
53 | );
54 | }
55 | });
56 |
57 | module.exports = ComponentBuilder;
--------------------------------------------------------------------------------
/src/components/navigation/VNav/VNav.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react/addons';
2 | import VNagGroup from './VNavGroup.jsx';
3 |
4 | let VNav = React.createClass({
5 |
6 | propTypes: {
7 | },
8 |
9 | render: function () {
10 | return (
11 |
12 | { this.props.children }
13 |
);
14 | }
15 | });
16 |
17 | export default VNav;
18 |
--------------------------------------------------------------------------------
/src/components/navigation/VNav/VNavGroup.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react/addons';
2 | import VNavItem from './VNavItem.jsx';
3 |
4 |
5 | let VNavGroup = React.createClass({
6 |
7 | propTypes: {
8 | title: React.PropTypes.string.isRequired,
9 | name: React.PropTypes.string,
10 | onSelect: React.PropTypes.func,
11 | href: React.PropTypes.string
12 | },
13 |
14 | renderGroupHeader: function () {
15 | return (
16 | );
24 | },
25 |
26 | handleSelect: function() {
27 | if(this.props.onSelect) {
28 | this.props.onSelect(this.props.name);
29 | return false;
30 | }
31 | },
32 |
33 | render: function () {
34 | return (
35 |
36 | { this.renderGroupHeader() }
37 |
38 |
39 |
40 | { this.props.children }
41 |
42 |
43 |
44 |
);
45 | }
46 |
47 | });
48 |
49 | export default VNavGroup;
50 |
--------------------------------------------------------------------------------
/src/components/navigation/VNav/VNavItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react/addons';
2 |
3 |
4 | let VNavItem = React.createClass({
5 |
6 | propTypes: {
7 | title: React.PropTypes.string.isRequired,
8 | name: React.PropTypes.string,
9 | onSelect: React.PropTypes.func,
10 | href: React.PropTypes.string
11 | },
12 |
13 | handleSelect: function() {
14 | if(this.props.onSelect) {
15 | this.props.onSelect(this.props.name);
16 | return false;
17 | }
18 | },
19 |
20 | render: function () {
21 | return (
22 |
23 |
24 | { this.props.title }
25 |
26 | );
27 | }
28 |
29 | });
30 |
31 | export default VNavItem;
32 |
--------------------------------------------------------------------------------
/src/components/navigation/link/link.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var gearzMixin = require("../../gearz.mixin");
3 |
4 | var Link = React.createClass({
5 | mixins: [gearzMixin],
6 | processAjaxData: function (response, urlPath) {
7 | document.getElementById("content").innerHTML = response.html;
8 | document.title = response.pageTitle;
9 | window.history.pushState({"html": response.html, "pageTitle": response.pageTitle}, "", urlPath);
10 | },
11 | navigator: function (routeInfo) {
12 | return function (e) {
13 | var onNavigate = this.props.onNavigate;
14 | onNavigate && onNavigate(e);
15 | if (routeInfo.routeData.isClient) {
16 | //var currentInfo = this.props.router.getCurrentLocationInfo();
17 | var Component = window[routeInfo.routeData.pageComponent];
18 | var targetElement = document.getElementById(this.props.target);
19 | if (Component && targetElement) {
20 | React.render( , targetElement);
21 | window.history.pushState(
22 | {
23 | pageComponent: routeInfo.routeData.pageComponent,
24 | viewData: {},
25 | "pageTitle": "TITLE"
26 | },
27 | null,
28 | routeInfo.uri);
29 | }
30 | }
31 |
32 | e.preventDefault();
33 | }.bind(this);
34 | },
35 | render: function () {
36 | var href = this.props.href,
37 | router = this.props.router,
38 | onNavigate = this.props.onNavigate; // triggered when navigating through this link
39 |
40 | var routeInfo = router.getInfo(href);
41 |
42 | return (
43 |
44 | {routeInfo.uri}
45 |
46 | );
47 | }
48 | });
49 |
50 | module.exports = Link;
--------------------------------------------------------------------------------
/src/components/navigation/pager/pager.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gearz-lab/react-ui/8bc4808092fd913ec6554f5b9f60d4a00f7bb7ef/src/components/navigation/pager/pager.css
--------------------------------------------------------------------------------
/src/components/navigation/pager/pager.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var gearzMixin = require("../../gearz.mixin");
3 |
4 | var Pager = React.createClass({
5 | mixins: [gearzMixin],
6 | render: function() {
7 | var page = this.get("page");
8 | var pageCount = this.props.count / this.props.pageSize,
9 | children = [];
10 |
11 | for (var it = 0; it < pageCount; it++) {
12 | var setter = this.setter("page", it+1);
13 | children.push(
14 |
18 |
19 | {it}
20 |
21 | );
22 | }
23 |
24 | return (
25 |
26 |
29 |
30 | );
31 | }
32 | });
33 |
34 | module.exports = Pager;
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Textbox from './components/editors/textbox/textbox.js';
2 | import FormGroup from './components/layout/formGroup/formGroup.jsx';
3 | import StackPanel from './components/layout/stackPanel/stackPanel.jsx';
4 | import TreeView from 'components/data/treeView/treeView.jsx';
5 | import TreeRow from 'components/data/treeView/treeRow.jsx';
6 | import Tab from './components/layout/tabControl/tab.jsx';
7 | import TabControl from './components/layout/tabControl/tabControl.jsx';
8 | import TabHeader from './components/layout/tabControl/tabHeader.jsx';
9 | import ComponentBuilder from './components/meta/componentBuilder/componentBuilder.jsx';
10 | import Link from './components/navigation/link/link.jsx';
11 | import Pager from './components/navigation/pager/pager.jsx';
12 | // Navigation
13 | import VNav from './components/navigation/VNav/VNav.jsx';
14 | import VNavGroup from './components/navigation/VNav/VNavGroup.jsx';
15 | import VNavItem from './components/navigation/VNav/VNavItem.jsx.jsx';
16 |
17 | export default {
18 | Textbox,
19 | FormGroup,
20 | StackPanel,
21 | TreeView,
22 | TreeRow,
23 | Tab,
24 | TabControl,
25 | TabHeader,
26 | ComponentBuilder,
27 | Link,
28 | Pager,
29 | // Navigation
30 | VNav,
31 | VNavGroup,
32 | VNavItem
33 | }
--------------------------------------------------------------------------------
/src/less/data/treeView.less:
--------------------------------------------------------------------------------
1 | .rui-treeView-content {
2 | margin-left: 5px;
3 | }
4 |
5 | .rui-treeView-toggle-button {
6 | cursor: pointer;
7 | }
8 |
9 | .rui-noselect {
10 | -webkit-touch-callout: none;
11 | -webkit-user-select: none;
12 | -khtml-user-select: none;
13 | -moz-user-select: none;
14 | -ms-user-select: none;
15 | user-select: none;
16 | }
--------------------------------------------------------------------------------
/src/less/navigation/VNav.less:
--------------------------------------------------------------------------------
1 | .rui-vnav { }
2 | .rui-vnav a { text-decoration: none}
3 | .rui-vnav .glyphicon { margin-right: 8px; }
4 | .rui-vnav .panel-body { padding:0px; }
5 | .rui-vnav .panel-body table tr td { padding-left: 15px }
6 | .rui-vnav .panel-body .table {margin-bottom: 0px; }
--------------------------------------------------------------------------------
/src/lib/componentFactory/component-factory.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var StringTemplate = require("./templates/stringTemplate.jsx");
3 |
4 | var ComponentFactory = {
5 |
6 | /**
7 | * Builds the component template templateMap
8 | */
9 | buildTemplateCache: function() {
10 | if(!this.templaceMap)
11 | this.templateMap = {};
12 | this.templateMap["string"] = StringTemplate;
13 | },
14 |
15 | /**
16 | * Builds a component based on the given metadata
17 | * @param meta: metadata
18 | * @returns the React component based on the given metadata
19 | */
20 | build: function(meta) {
21 | this.buildTemplateCache();
22 | var template = this.templateMap["string"];
23 | return template.build(meta);
24 | }
25 | };
26 |
27 | module.exports = ComponentFactory;
--------------------------------------------------------------------------------
/src/lib/componentFactory/templates/stringTemplate.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 |
3 | var StringTemplate = {
4 | build: function(meta) {
5 | return ;
6 | }
7 | };
8 |
9 | module.exports = StringTemplate;
--------------------------------------------------------------------------------
/src/lib/expression-evaluator.js:
--------------------------------------------------------------------------------
1 | import textExpressionEvaluator from './text-expression-evaluator.js';
2 |
3 | /**
4 | * Evaluates expressions
5 | */
6 | export default {
7 | /**
8 | * Evaluates the given expression
9 | * @param expression - the expression to be evaluated. This can be either a constant, a function or a text expression
10 | * @param data - the data scope in which the expression will be executed
11 | * @returns {Object}
12 | */
13 | evaluate: function (expression, data) {
14 | switch (typeof expression) {
15 | case 'function':
16 | return expression(data);
17 | case 'string':
18 | return textExpressionEvaluator.evaluate(expression, data);
19 | }
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/src/lib/gearz.routing.js:
--------------------------------------------------------------------------------
1 | /// Gearz Routing v1.0.0
2 | ///
3 | /// This is responsible for routing, that is,
4 | /// extracting information from an URI so that
5 | /// an external agent can determine what to
6 | /// render next.
7 | ///
8 | /// This will mimic the behaviour of ASP.NET routing with the following exceptions:
9 | /// 1) when building patterns:
10 | /// - none yet
11 | /// 2) when matching URI's:
12 | /// - pattern "{x}-{y}" matches '~/x-y-z/' as:
13 | /// Here: x -> 'x'; y -> 'y-z'
14 | /// ASP.NET: x -> 'x-y'; y -> 'z'
15 | /// - pattern "{x?}-{y}" matches '~/---/' as:
16 | /// Here: x -> no match (x = '' and y = '--', but x is a middle place-holder)
17 | /// ASP.NET: x -> '-'; y -> '-'
18 | ///
19 | /// This will not:
20 | /// - change URI for single-page apps
21 | /// - request server data in any way
22 | ///
23 | /// This router support mix-in functions, that can be used to add plug-ins into it.
24 | /// With plug-ins, anything is possible, including the above mentioned behaviours.
25 |
26 | (function() {
27 |
28 |
29 |
30 |
31 | /*****************************************************************************************
32 | ** **
33 | ** PROTOTYPES. **
34 | ** **
35 | *****************************************************************************************/
36 |
37 | function RouteError(type, message) {
38 | if (window.chrome) {
39 | var err = new Error();
40 | this.__defineGetter__('stack', function(){return remLine(err.stack, 1);});
41 | this.__defineSetter__('stack', function(value){err.stack=value;});
42 | }
43 | this.message = message;
44 | this.type = type;
45 | }
46 | var freeze = Object.freeze;
47 | var types;
48 | RouteError.prototype = extend(Object.create(Error.prototype), {
49 | message: 'Route error.',
50 | name: 'RouteError',
51 | constructor: RouteError,
52 | types: types = {
53 | SYNTAX_ERROR: 'SYNTAX_ERROR',
54 | EMPTY_SEGMENT: 'EMPTY_SEGMENT',
55 | ADJACENT_PLACEHOLDERS: 'ADJACENT_PLACEHOLDERS',
56 | DUPLICATE_PLACEHOLDER: 'DUPLICATE_PLACEHOLDER',
57 | UNNAMED_PLACEHOLDER: 'UNNAMED_PLACEHOLDER'
58 | }
59 | });
60 |
61 | function Literal(value) {
62 | this.value = value.replace(/\{\{/g, "{").replace(/\}\}/g, "}");
63 | this.regexp = escapeRegExp(this.value);
64 | freeze(this);
65 | }
66 | Literal.prototype = {
67 | toString: function() {
68 | return "Literal: " + JSON.stringify(this.value);
69 | }
70 | };
71 |
72 | function PlaceHolderBase(name) {
73 | this.name = name;
74 | freeze(this);
75 | }
76 | PlaceHolderBase.prototype = {
77 | toString: function() {
78 | return "Name: " + this.name;
79 | }
80 | };
81 |
82 |
83 |
84 |
85 | /*****************************************************************************************
86 | ** **
87 | ** PRIVATE METHODS. **
88 | ** **
89 | *****************************************************************************************/
90 |
91 | function extend(target, source) {
92 | for (var k in source)
93 | if (source.hasOwnProperty(k))
94 | target[k] = source[k];
95 | return target;
96 | }
97 |
98 | function remLine(str, num) {
99 | var lines = str.split('\n');
100 | lines.splice(num-1, 1);
101 | return lines.join('\n');
102 | }
103 |
104 | function getSegments(uriPattern, LiteralClass, PlaceHolderClass) {
105 | var segments = uriPattern && uriPattern.split('/').map(function (seg) {
106 | var ss = seg.split(/(?:((?:[^\{\}]|\{\{|\}\})+)|\{([^\{\}]*)(?!\}\})\})/g),
107 | items = [];
108 |
109 | for (var itSs = 0; itSs < ss.length; itSs += 3) {
110 | var empty = ss[itSs],
111 | literal = ss[itSs + 1],
112 | name = ss[itSs + 2];
113 |
114 | if (empty) throw new RouteError(types.SYNTAX_ERROR, "Invalid route pattern: near '" + empty + "'");
115 |
116 | if (itSs == ss.length - 1) break;
117 |
118 | if (typeof literal == 'string') items.push(new LiteralClass(literal));
119 | else if (typeof name == 'string') items.push(new PlaceHolderClass(name));
120 | }
121 | return items;
122 | });
123 |
124 | // validating:
125 | // - Names of place-holders cannot be repeated
126 | // - Adjacent place-holders
127 | var usedNames = {},
128 | prevName = '';
129 | for (var itSeg = 0; itSeg < segments.length; itSeg++) {
130 | var subSegs = segments[itSeg];
131 | if (itSeg < segments.length - 1 && subSegs.length == 0)
132 | throw new RouteError(types.EMPTY_SEGMENT, "Invalid route pattern: empty segment #" + itSeg);
133 | for (var itSub = 0; itSub < subSegs.length; itSub++) {
134 | var item = subSegs[itSub];
135 | if (item instanceof PlaceHolderBase) {
136 | if (prevName !== '') throw new RouteError(types.ADJACENT_PLACEHOLDERS, "Invalid route pattern: '{" + prevName + "}' and '{" + item.name + "}' cannot be adjacent");
137 | if (usedNames[item.name]) throw new RouteError(types.DUPLICATE_PLACEHOLDER, "Invalid route pattern: '{" + item.name + "}' used multiple times");
138 | if (!item.name) throw new RouteError(types.UNNAMED_PLACEHOLDER, "Invalid route pattern: found '{}'");
139 | usedNames[item.name] = true;
140 | }
141 | prevName = item instanceof PlaceHolderBase ? item.name : '';
142 | }
143 | prevName = '';
144 | }
145 |
146 | return segments;
147 | }
148 |
149 | function escapeRegExp(str) {
150 | // http://stackoverflow.com/a/6969486/195417
151 | return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
152 | }
153 |
154 | function matchConstraint(constraint, value) {
155 | // constraint fail
156 | value = value==null ? "" : ""+value;
157 | if (typeof constraint == 'string') {
158 | var regex = new RegExp(constraint, 'g');
159 | if (!regex.test(value))
160 | return false;
161 | }
162 | else if (typeof constraint == 'function') {
163 | if (!constraint(value))
164 | return false;
165 | }
166 | return true;
167 | }
168 |
169 | function validateSegmentValues(segments, route, segValues) {
170 | var segIdx = 0,
171 | glbFilled = 0,
172 | glbMissing = 0;
173 |
174 | for (var itSeg = 0; itSeg < segments.length; itSeg++) {
175 |
176 | var subSegs = segments[itSeg],
177 | missing = 0,
178 | filled = 0,
179 | literals = 0;
180 |
181 | for (var itSub = 0; itSub < subSegs.length; itSub++) {
182 | var item = subSegs[itSub],
183 | value = segValues[segIdx++];
184 |
185 | if (item instanceof PlaceHolderBase) {
186 | var name = item.name,
187 | constraint = route.Constraints && route.Constraints[name],
188 | def = route.Defaults && route.Defaults[name];
189 |
190 | if (!matchConstraint(constraint, value))
191 | return "Match failed: constraint of '{" + name + "}' did not match";
192 |
193 | // no value and no default
194 | if (!value && isUndefined(def))
195 | return "Match failed: no value and no default for '{" + name + "}'";
196 | }
197 | else if (item instanceof Literal) {
198 | // ASP.NET: literal can never be missing
199 | if (value !== item.value)
200 | return "Match failed: literal cannot be missing '" + item.value + "'";
201 | literals++;
202 | }
203 |
204 | if (!value) missing++;
205 | else filled++;
206 | }
207 |
208 | // ASP.NET: segment is partially filled
209 | if (literals && missing)
210 | return "Match failed: segment is partially filled";
211 |
212 | // ASP.NET: missing segments may only appear at end
213 | if (!filled) glbMissing++;
214 | else if (glbMissing) {
215 | return "Match failed: missing segments may only appear at end";
216 | }
217 | }
218 | return null;
219 | }
220 |
221 | function getRouteValues(route, segments, segValues) {
222 | var segIdx = 0,
223 | r = {};
224 | for (var itSeg = 0; itSeg < segments.length; itSeg++) {
225 | var subSegs = segments[itSeg];
226 | for (var itSub = 0; itSub < subSegs.length; itSub++) {
227 | var item = subSegs[itSub];
228 | if (item instanceof PlaceHolderBase)
229 | r[item.name] = segValues[segIdx];
230 | segIdx++;
231 | }
232 | }
233 | return r;
234 | }
235 |
236 | function getSegmentsMatcher(segments) {
237 | var rgxStr = "^";
238 | for (var itSeg = 0; itSeg < segments.length; itSeg++) {
239 | var subSegs = segments[itSeg], cntUnclosed = 0;
240 | rgxStr += "(?:" + (itSeg ? "\\/" : "\\~\\/");
241 | for (var itSub = 0; itSub < subSegs.length; itSub++) {
242 | var item = subSegs[itSub];
243 | if (item instanceof PlaceHolderBase) {
244 | var adjLit = subSegs[itSub + 1],
245 | adjLitRgx = adjLit instanceof Literal ? "|" + adjLit.regexp : "",
246 | op = item.isOptional ? '*' : '+';
247 |
248 | rgxStr += "((?:(?!\\/" + adjLitRgx + ").)" + op + ")?";
249 | } else if (item instanceof Literal) {
250 | rgxStr += "(?:" + "(" + item.regexp + ")";
251 | cntUnclosed++;
252 | }
253 | }
254 | for (var itU = 0; itU < cntUnclosed; itU++)
255 | rgxStr += ")?";
256 | }
257 | for (var itP = 0; itP < segments.length; itP++)
258 | rgxStr += ")?";
259 | rgxStr += "$";
260 |
261 | var regex = new RegExp(rgxStr, 'g');
262 |
263 | SegmentsMatcher.regex = regex;
264 | function SegmentsMatcher(uri) {
265 | regex.lastIndex = 0;
266 | var result = regex.exec(uri);
267 | if (!result) return null;
268 | result.shift();
269 | return result.map(function(s){return s||undefined;});
270 | }
271 | return SegmentsMatcher;
272 | }
273 |
274 | function Route(route) {
275 | if (!route || typeof route != 'object')
276 | throw new Error("Invalid route information: route argument cannot be missing");
277 |
278 | var constraints = route.Constraints ? extend({}, route.Constraints) : {};
279 |
280 | function PlaceHolder(name) {
281 | this.name = name;
282 | this.isOptional = !!route.Defaults
283 | && route.Defaults.hasOwnProperty(name)
284 | && !isUndefined(route.Defaults[name]);
285 | if (this.isOptional)
286 | this.defaultValue = route.Defaults[name];
287 | this.isConstrained = constraints.hasOwnProperty(name);
288 | if (this.isConstrained) {
289 | this.constraint = constraints[name];
290 | delete constraints[name];
291 | }
292 | freeze(this);
293 | }
294 | PlaceHolder.prototype = Object.create(PlaceHolderBase.prototype);
295 |
296 | var segments = getSegments.call(this, route.UriPattern, Literal, PlaceHolder);
297 | var segmentsMatcher = getSegmentsMatcher.call(this, segments);
298 |
299 | // properties with extracted information from the route object
300 | this.segments = segments;
301 | this.match = segmentsMatcher;
302 | this.contextChecks = constraints;
303 |
304 | // source object properties
305 | this.UriPattern = route.UriPattern;
306 | this.DataTokens = route.DataTokens;
307 | this.Defaults = route.Defaults;
308 | this.Constraints = route.Constraints;
309 |
310 | freeze(this);
311 | }
312 |
313 | function freezeAny(o) {
314 | if (typeof o == 'object' && o != null)
315 | return freeze(o);
316 | return o;
317 | }
318 |
319 | function RouteMatch(data, tokens, error, details) {
320 | if (!(this instanceof RouteMatch))
321 | throw new Error("Call with 'new' operator.");
322 | this.data = freezeAny(data);
323 | this.tokens = freezeAny(tokens);
324 | this.error = freezeAny(error);
325 | this.details = freezeAny(details);
326 | freeze(this);
327 | }
328 | this.RouteMatch = RouteMatch;
329 |
330 | function addParams(p2, values) {
331 | for (var p1 in values) {
332 | if (!this[p1]) this[p1] = {};
333 | this[p1][p2] = values[p1];
334 | }
335 | }
336 |
337 | function ifUndef(f,t) {
338 | return isUndefined(f) ? t : f;
339 | }
340 |
341 | function isNullOrEmpty(x) {
342 | return x===null||x==="";
343 | }
344 |
345 | function isUndefined(x) {
346 | return typeof x == 'undefined';
347 | }
348 |
349 | function ensureStringLimits(start, end, str) {
350 | str = ""+str;
351 | end = ""+end;
352 |
353 | str = str == "" ? start
354 | : str[0] != start ? (start+str)
355 | : str;
356 |
357 | str = str == "" ? end
358 | : str[str.length-1] != end ? (str+end)
359 | : str;
360 |
361 | return str;
362 | }
363 |
364 | function bindUriValues(route, currentRouteData, targetRouteData, globalValues) {
365 | var params = {};
366 | var add = addParams.bind(params);
367 | add('current', currentRouteData);
368 | add('target', targetRouteData);
369 | add('default', route.Defaults);
370 | add('constraint', route.Constraints);
371 | add('global', globalValues);
372 |
373 | // Getting values that will be used.
374 | var result = { uriValues: {}, dataTokens: {} }, allowCurrent = true;
375 | var fnc = false;
376 | for (var itS = 0; itS < route.segments.length; itS++) {
377 | var seg = route.segments[itS];
378 | for (var itSS = 0; itSS < seg.length; itSS++) {
379 | var item = seg[itSS];
380 | if (item instanceof PlaceHolderBase) {
381 | var name = item.name;
382 | if (!params.hasOwnProperty(name))
383 | return null;
384 | var param = params[name];
385 | var c = ifUndef(param.current, g);
386 | var t = param.target;
387 | var d = ifUndef(param.default, g);
388 |
389 | // c t d | r action
390 | // -----------+------------
391 | // - - - | stop
392 | // a - - | a
393 | // - a - | a
394 | // - - a | a
395 | // a a - | a
396 | // a b - | b clear c
397 | // a - a | a
398 | // a - b | a
399 | // - a a | a
400 | // - a b | a
401 | // a a a | a
402 | // a a b | a
403 | // a b a | b
404 | // b a a | a
405 | // a b c | b
406 |
407 | var nc = !c || fnc,
408 | nt = !t,
409 | nd = isUndefined(d),
410 | ect = c == t || nc && nt,
411 | etd = t == d || nt && nd,
412 | edc = d == c || nd && nc;
413 | var r0;
414 | if (nc && nt && nd ) return null;
415 | else if (!nc && nt && nd ) r0 = c;
416 | else if (nc && !nt && nd ) r0 = t;
417 | else if (nc && nt && !nd) r0 = d;
418 | else if (!nc && ect && nd ) r0 = t;
419 | else if (!nc && !nt && nd ) { r0 = t; fnc = true; }
420 | else if (edc && nt && !nd) r0 = c;
421 | else if (!nc && nt && !nd) r0 = c;
422 | else if (nc && !nt && etd) r0 = t;
423 | else if (nc && !nt && !nd) r0 = t;
424 | else if (edc && ect && !nd) r0 = t;
425 | else if (!nc && ect && !nd) r0 = t;
426 | else if (edc && !nt && !nd) r0 = t;
427 | else if (!nc && !nt && etd) r0 = t;
428 | else if (!nc && !nt && !nd) r0 = t;
429 |
430 | param.used = true;
431 | result.uriValues[name] = r0;
432 | r0 = undefined;
433 | }
434 | }
435 | }
436 |
437 | // checking remaining parameters
438 | for (var name in params) {
439 | var param = params[name];
440 | if (!param.used) {
441 | var g = param.global;
442 | var c = ifUndef(param.current, g);
443 | var t = param.target;
444 | var d = ifUndef(param.default, g);
445 |
446 | // c t d | r action
447 | // -----------+------------
448 | // - - - | -
449 | // a - - | -
450 | // - a - | a
451 | // - - a | stop
452 | // a a - | a
453 | // a b - | b
454 | // a - a | - data-token
455 | // a - b | stop
456 | // - a a | - data-token
457 | // - a b | stop
458 | // a a a | - data-token
459 | // a a b | stop
460 | // a b a | stop
461 | // b a a | - data-token
462 | // a b c | stop
463 |
464 | var nc = isUndefined(c),
465 | nt = isUndefined(t),
466 | nd = isUndefined(d),
467 | ect = c == t || nc && nt || isNullOrEmpty(c) && isNullOrEmpty(t),
468 | etd = t == d || nt && nd || isNullOrEmpty(t) && isNullOrEmpty(d),
469 | edc = d == c || nd && nc || isNullOrEmpty(d) && isNullOrEmpty(c);
470 | var r1;
471 | if (nc && nt && nd ) r1 = undefined;
472 | else if (!nc && nt && nd ) r1 = undefined;
473 | else if (nc && !nt && nd ) r1 = t;
474 | else if (nc && nt && !nd) return null;
475 | else if (!nc && ect && nd ) r1 = t;
476 | else if (!nc && !nt && nd ) r1 = t;
477 | else if (edc && nt && !nd) { r1=undefined; result.dataTokens[name]=d; }
478 | else if (!nc && nt && !nd) return null;
479 | else if (nc && !nt && etd) { r1=undefined; result.dataTokens[name]=d; }
480 | else if (nc && !nt && !nd) return null;
481 | else if (edc && ect && !nd) { r1=undefined; result.dataTokens[name]=d; }
482 | else if (!nc && ect && !nd) return null;
483 | else if (edc && !nt && !nd) return null;
484 | else if (!nc && !nt && etd) { r1=undefined; result.dataTokens[name]=d; }
485 | else if (!nc && !nt && !nd) return null;
486 |
487 | if (typeof r1 != 'undefined')
488 | {
489 | param.used = true;
490 | result.uriValues[name] = r1;
491 | r1 = undefined;
492 | }
493 | }
494 | }
495 |
496 | return result;
497 | }
498 |
499 | function buildUri(route, data, basePath) {
500 | var uri = basePath;
501 | var tempUri = uri;
502 | for (var itS = 0; itS < route.segments.length; itS++) {
503 | var seg = route.segments[itS];
504 | tempUri += tempUri[tempUri.length-1] != "/" ? "/" : "";
505 | var segmentRequired = false;
506 | for (var itSS = 0; itSS < seg.length; itSS++) {
507 | var item = seg[itSS];
508 | if (item instanceof PlaceHolderBase) {
509 | var name = item.name,
510 | constraint = route.Constraints && route.Constraints[name],
511 | def = route.Defaults && route.Defaults[name],
512 | value = data.uriValues[name];
513 |
514 | // !(A || B) <=> !A && !B
515 | // !(A && B) <=> !A || !B
516 | if (typeof def != 'undefined')
517 | if (def != null && def != "" || value != null && value != "")
518 | if (def != value)
519 | segmentRequired = true;
520 |
521 | if (!matchConstraint(constraint, value))
522 | return null;
523 |
524 | if (value) tempUri += encodeURIComponent(value);
525 | delete data.uriValues[item.name];
526 | }
527 | else if (item instanceof Literal) {
528 | segmentRequired = true;
529 | tempUri += encodeURIComponent(item.value);
530 | }
531 | }
532 |
533 | if (segmentRequired)
534 | uri = tempUri;
535 | }
536 |
537 | var sep = '?';
538 | for (var name in data.uriValues) {
539 | var value = data.uriValues[name];
540 | delete data.uriValues[name];
541 | uri += uri[uri.length-1] != sep ? sep : "";
542 | sep = '&';
543 | uri += encodeURIComponent(name) + '=' + encodeURIComponent(value);
544 | }
545 |
546 | return uri;
547 | }
548 |
549 | function mergeMixin(n, o) {
550 | if (typeof n == 'function') {
551 | return n;
552 | }
553 | }
554 |
555 | function appendParam(v0, v1) {
556 | return isUndefined(v0) ? (v1||"")
557 | : isNullOrEmpty(v0) ? ";"+(v1||"")
558 | : v0+";"+(v1||"");
559 | }
560 |
561 | function getQueryValues(uri) {
562 | uri = isNullOrEmpty(uri) || isUndefined(uri) ? "" : ""+uri;
563 | uri = uri.replace(new RegExp("^"+escapeRegExp(this.basePath), "g"), "~/");
564 | var qs = uri.split(/[?&]/g);
565 | uri = qs.splice(0,1)[0];
566 | qs = qs.map(function(x){
567 | return decodeURIComponent(x)
568 | .replace(/\+/g, " ")
569 | .replace('=', '&')
570 | .split('&');
571 | });
572 | var qv = {};
573 | for (var it = 0; it < qs.length; it++) {
574 | var kv = qs[it],
575 | name = kv[0];
576 | qv[name] = appendParam(qv[name], kv[1]);
577 | }
578 |
579 | return { queryValues: qv, path: uri };
580 | }
581 |
582 | function toVirtualPath(path) {
583 | path=""+path;
584 | if (path.indexOf(this.basePath) == 0)
585 | return path.substr(this.basePath.length);
586 | return null;
587 | }
588 |
589 | function toAppPath(virtualPath) {
590 | virtualPath=""+virtualPath;
591 | if (virtualPath.indexOf("~/") == 0)
592 | return this.basePath + virtualPath.substr(2);
593 | return null;
594 | }
595 |
596 |
597 |
598 |
599 | /*****************************************************************************************
600 | ** **
601 | ** DEFINITION OF THE ROUTER OBJECT. **
602 | ** **
603 | *****************************************************************************************/
604 |
605 | function Router(opts) {
606 | if (!(this instanceof Router))
607 | throw new Error("Must call 'Router' with 'new' operator.");
608 |
609 | // enclosed values for the following functions
610 | var _routes = [];
611 |
612 | // private function definitions, that depend on the above enclosed values
613 | function makeURI(currentRouteData, targetRouteData, opts) {
614 | opts = opts || {};
615 | for (var itR = 0; itR < _routes.length; itR++) {
616 | var route = _routes[itR];
617 |
618 | // getting data to use in the URI
619 | var data = bindUriValues.call(
620 | this, route, currentRouteData, targetRouteData, this.globalValues);
621 |
622 | // building URI with the data
623 | var uri = null;
624 | if (data) uri = buildUri.call(
625 | this, route, data, opts.virtual ? "~/" : this.basePath);
626 |
627 | if (uri) return uri;
628 | }
629 |
630 | throw new Error("No matching route to build the URI");
631 | }
632 |
633 | function matchRoute(uri, opts) {
634 | var details = opts && opts.verbose ? [] : null;
635 | var parts = getQueryValues.call(this, uri);
636 | // ASP.NET routing code (for reference):
637 | // http://referencesource.microsoft.com/#System.Web/Routing/ParsedRoute.cs
638 | for (var itR = 0; itR < _routes.length; itR++) {
639 | var route = _routes[itR];
640 |
641 | // Trying to match the route information with the given URI.
642 | // Convert the URI pattern to a RegExp that can
643 | // extract information from a real URI.
644 | var segments = route.segments;
645 |
646 | var segValues = route.match(parts.path);
647 | if (!segValues) {
648 | if (details) details.push("Match failed: URI does not match");
649 | continue;
650 | }
651 |
652 | var validation = validateSegmentValues(segments, route, segValues);
653 | if (validation) {
654 | if (details) details.push(validation);
655 | continue;
656 | }
657 |
658 | var values = getRouteValues(route, segments, segValues);
659 | var r = {}, t = {};
660 |
661 | // Copy route data to the resulting object.
662 | // Copying `DataTokens` to the tokens variable.
663 | if (route.DataTokens)
664 | for (var kt in route.DataTokens)
665 | t[kt] = route.DataTokens[kt];
666 |
667 | // Copying the default values to the used values,
668 | // then overriding them with query values,
669 | // and finally overriding them with route data.
670 | if (route.Defaults)
671 | for (var kd in route.Defaults)
672 | r[kd] = route.Defaults[kd];
673 |
674 | if (parts.queryValues)
675 | for (var kt in parts.queryValues)
676 | r[kt] = parts.queryValues[kt];
677 |
678 | for (var kv in values)
679 | r[kv] = values[kv];
680 |
681 | return new RouteMatch(r, t, null, null);
682 | }
683 |
684 | return new RouteMatch(null, null, "No routes matched the URI.", details);
685 | }
686 | function addRoute(name, route) {
687 | if (typeof name !== 'string'
688 | && name != null || name === "" || /^\d+$/.test(name))
689 | throw new Error("Invalid argument: route name is invalid");
690 | _routes.push(new Route(route));
691 | if (name)
692 | _routes[name] = route;
693 | // allow fluent route definitions
694 | return this;
695 | }
696 | function getRoute(idOrName) {
697 | return _routes[idOrName];
698 | }
699 | // values that will be used
700 | var routes = opts.routes,
701 | globalValues = opts.globals,
702 | basePath = opts.basePath,
703 | mixins = opts.mixins || [];
704 |
705 | // adding routes
706 | if (routes instanceof Array)
707 | for (var itR = 0; itR < routes.length; itR++)
708 | addRoute.call(this, routes[itR]);
709 |
710 |
711 |
712 |
713 | /*****************************************************************************************
714 | ** **
715 | ** PUBLIC API OF THE ROUTER OBJECT. **
716 | ** **
717 | *****************************************************************************************/
718 |
719 | // METHODS
720 | this.addRoute = addRoute.bind(this);
721 | this.getRoute = getRoute.bind(this);
722 | this.matchRoute = matchRoute.bind(this);
723 | this.makeURI = makeURI.bind(this);
724 | this.toVirtualPath = toVirtualPath.bind(this);
725 | this.toAppPath = toAppPath.bind(this);
726 |
727 | // PROPERTIES
728 | this.globalValues = globalValues || {};
729 | this.basePath =
730 | isUndefined(basePath) ? "~/" :
731 | isNullOrEmpty(basePath) ? "/" :
732 | ensureStringLimits('/', '/', basePath);
733 | this.mixins = mixins;
734 |
735 | // APPLYING THE MIX-INS
736 | // Must be the last thing done before freezing.
737 | for (var it = 0; it < mixins.length; it++)
738 | mixins[it].call(this);
739 |
740 | freeze(this);
741 | }
742 |
743 |
744 |
745 |
746 | /*****************************************************************************************
747 | ** **
748 | ** PUBLIC GLOBAL DEFINITIONS. **
749 | ** **
750 | *****************************************************************************************/
751 |
752 | this.RouteError = RouteError;
753 | this.Router = Router;
754 |
755 | return Router;
756 | })();
--------------------------------------------------------------------------------
/src/lib/gearz.routing.mixins.js:
--------------------------------------------------------------------------------
1 | // Gearz Router Mixins v1.0.0
2 | //
3 | // This is responsible for extending the router,
4 | // to make it easier to use. The following mix-ins
5 | // are available in this file:
6 | //
7 | // - location: allows the router to store the current URI,
8 | // and use it to get the current route data when needed.
9 | //
10 |
11 | (function() {
12 |
13 | function getInfo(value) {
14 | if (typeof value == 'string') {
15 | var routeMatch = this.matchRoute(value);
16 | var uri = this.makeURI(routeMatch.data);
17 | return Object.freeze({
18 | routeData: routeMatch.data,
19 | dataTokens: routeMatch.tokens,
20 | uri: uri });
21 | }
22 | else if (typeof value == 'object') {
23 | var uri = this.makeURI(value);
24 | var routeMatch = this.matchRoute(uri);
25 | return Object.freeze({
26 | routeData: routeMatch.data,
27 | dataTokens: routeMatch.tokens,
28 | uri: uri });
29 | }
30 | throw new Error("Invalid value passed to 'getInfo'");
31 | }
32 |
33 | var mixins = Object.freeze({
34 | location: function() {
35 | var current = {};
36 |
37 | this.getInfo = getInfo.bind(this);
38 |
39 | this.setCurrentLocation = (function(value) {
40 | current = this.getInfo(value);
41 | }).bind(this);
42 |
43 | this.getCurrentLocationInfo = (function() {
44 | return current;
45 | }).bind(this);
46 |
47 | var base = this.makeURI;
48 | this.makeURI = (function(target, opts) {
49 | if (arguments.length == 2 && typeof target == 'object' && typeof opts == 'object')
50 | return base.call(this, current.routeData, target, opts);
51 | else if (arguments.length == 1 && typeof target == 'object')
52 | return base.call(this, current.routeData, target);
53 | return base.apply(this, arguments);
54 | }).bind(this);
55 | }
56 | });
57 |
58 | window.routerMixins = window.routerMixins || {};
59 | for (var k in mixins)
60 | window.routerMixins[k] = mixins[k];
61 |
62 | return mixins;
63 | })();
--------------------------------------------------------------------------------
/src/lib/rc.component.factory.js:
--------------------------------------------------------------------------------
1 | // defining the ReactComponents global namespace
2 | if(!window.ReactComponents) window.ReactComponents = {};
3 |
4 |
5 |
6 | window.ReactComponents.ComponentFactory = {
7 | createComponent: function(componentMetadata) {
8 |
9 | }
10 | };
11 |
12 |
--------------------------------------------------------------------------------
/src/lib/text-expression-evaluator.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Evaluates text expressions
3 | */
4 | export default {
5 | /**
6 | * Evaluates the given text expression
7 | * @param expression - the text expression
8 | * @param data - the data scope in which the expression will be executed
9 | * @returns {Object}
10 | */
11 | evaluate: function (expression, data) {
12 | // contains all variable statements. Each variable in 'data' will result in a declaraction statement.
13 | // example: if, data is { x : 1 }, variableDeclarations will contain: 'var x = 1'
14 | // I didn't necessarily have to use these declarations, because the scope of 'eval' is the current scope,
15 | // that is, I could just put the 'data' first level variables in scope inside this function and it would work,
16 | // but it would be EXTREMELY unsafe.
17 | // However, eval will not use the outside scope if it's in 'use strict' mode.
18 | let variableDeclarations = [];
19 | let finalExpression = '\'use strict\';\n';
20 | if (data) {
21 | for (let prop in data) {
22 | let declaration = `var ${prop} = ${JSON.stringify(data[prop])};\n`;
23 | variableDeclarations.push(declaration);
24 | }
25 | }
26 | for (let i = 0; i < variableDeclarations.length; i++) {
27 | finalExpression += variableDeclarations[i];
28 | }
29 | finalExpression += expression;
30 | /* eslint-disable */
31 | return eval(finalExpression);
32 | /* eslint-enable */
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "globals": {
6 | "assert": true,
7 | "sinon": true
8 | },
9 | "rules": {
10 | "no-script-url": 0,
11 | "no-unused-expressions": 0,
12 | "react/no-multi-comp": 0,
13 | "react/prop-types": 0
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/VNav-spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import expressionEvaluator from '../src/lib/expression-evaluator.js';
3 | import ReactTestUtils from 'react/lib/ReactTestUtils';
4 | import VNav from '../src/components/navigation/VNav/VNav.jsx';
5 | import VNavGroup from '../src/components/navigation/VNav/VNavGroup.jsx';
6 | import VNavItem from '../src/components/navigation/VNav/VNavItem.jsx';
7 |
8 | import { html } from 'js-beautify';
9 |
10 | describe('Expression evaluator', function () {
11 | describe('SideNav', function () {
12 |
13 | it('Should have proper class', function () {
14 |
15 | let instance = ReactTestUtils.renderIntoDocument(
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | let node = React.findDOMNode(instance);
27 | });
28 |
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/expression-evaluator-spec.js:
--------------------------------------------------------------------------------
1 | import expressionEvaluator from '../src/lib/expression-evaluator.js';
2 |
3 | describe('Expression evaluator', function () {
4 | describe('Literals', function () {
5 | it('Should evaluate literals', function () {
6 | assert.strictEqual(expressionEvaluator.evaluate('1 + 1'), 2);
7 | });
8 | it('Should evaluate boolean operations', function () {
9 | assert.strictEqual(expressionEvaluator.evaluate('true'), true);
10 | });
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | import 'es5-shim';
2 |
3 | beforeEach(function() {
4 | sinon.stub(console, 'warn');
5 | });
6 |
7 | afterEach(function() {
8 | if (typeof console.warn.restore === 'function') {
9 | assert(!console.warn.called, () => console.warn.getCall(0).args[0]);
10 | console.warn.restore();
11 | }
12 | });
13 |
14 | describe('Process environment for tests', function () {
15 | it('Should be development for React console warnings', function () {
16 | assert.equal(process.env.NODE_ENV, 'development');
17 | });
18 | });
19 |
20 | const testsContext = require.context('.', true, /spec$/);
21 | testsContext.keys().forEach(testsContext);
22 |
--------------------------------------------------------------------------------
/test/text-expression-evaluator-spec.js:
--------------------------------------------------------------------------------
1 | import textExpressionEvaluator from '../src/lib/text-expression-evaluator.js';
2 |
3 | describe('Text expression evaluator', function() {
4 | describe('Literals', function() {
5 | it('Should evaluate number operations', function() {
6 | assert.strictEqual(textExpressionEvaluator.evaluate('1 + 1'), 2);
7 | });
8 | it('Should evaluate boolean operations', function() {
9 | assert.strictEqual(textExpressionEvaluator.evaluate('true'), true);
10 | });
11 | });
12 | describe('Variables', function() {
13 | it('Should evaluate first level variables', function() {
14 | assert.strictEqual(textExpressionEvaluator.evaluate('x + 1', { x : 1 }), 2 );
15 | });
16 | it('Should evaluate deep variables', function() {
17 | assert.strictEqual(textExpressionEvaluator.evaluate('x.y.z + 1', { x: { y: { z: 1 } } }), 2);
18 | });
19 | it('Should scape strings correctly', function() {
20 | assert.strictEqual(textExpressionEvaluator.evaluate('x.y.z + \'ny\'', { x: { y: { z: 'john' } } }), 'johnny');
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/tools/amd/README.md:
--------------------------------------------------------------------------------
1 | # react-bootstrap-bower
2 |
3 | [Bootstrap 3](http://getbootstrap.com) components built with [React](http://facebook.github.io/react/)
4 |
5 | This repo contains built AMD modules and standalone browser globals.
6 |
7 | There is a separate [source repo](https://github.com/react-bootstrap/react-bootstrap).
8 |
9 | A [docs site](http://react-bootstrap.github.io) with live editable examples.
10 |
11 | [](https://travis-ci.org/react-bootstrap/react-bootstrap) [](http://badge.fury.io/bo/react-bootstrap)
12 |
--------------------------------------------------------------------------------
/tools/amd/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= pkg.name %>",
3 | "version": "<%= pkg.version %>",
4 | "homepage": "<%= pkg.homepage %>",
5 | "author": "<%= pkg.author %>",
6 | "license": "<%= pkg.license %>",
7 | "main": [
8 | "react-bootstrap.js"
9 | ],
10 | "keywords": [
11 | "react",
12 | "react-component",
13 | "bootstrap"
14 | ],
15 | "ignore": [
16 | "**/.*"
17 | ],
18 | "dependencies": {
19 | "react": ">= 0.13.0",
20 | "classnames": "1.1.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tools/build.js:
--------------------------------------------------------------------------------
1 | /* eslint no-process-exit: 0 */
2 |
3 | import path from 'path';
4 | import docs from '../docs/build';
5 |
6 | export default function Build(noExitOnFailure) {
7 | return docs();
8 | };
9 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* eslint no-var: 0 */
2 | require('./register-babel');
3 | var config = require('./webpack/webpack.config');
4 | var result = config();
5 | module.exports = result;
6 |
--------------------------------------------------------------------------------
/webpack.docs.js:
--------------------------------------------------------------------------------
1 | /* eslint no-var: 0 */
2 | require('./register-babel');
3 | var config = require('./webpack/docs.config');
4 | module.exports = config;
5 |
--------------------------------------------------------------------------------
/webpack/docs.config.js:
--------------------------------------------------------------------------------
1 | import config from './webpack.config';
2 | export default config({docs: true});
3 |
--------------------------------------------------------------------------------
/webpack/strategies/development.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export default (config, options) => {
4 | if (options.development) {
5 | config = _.extend({}, config, {
6 | devtool: 'sourcemap'
7 | });
8 |
9 | return config;
10 | }
11 |
12 | return config;
13 | }
14 |
--------------------------------------------------------------------------------
/webpack/strategies/docs.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import ExtractTextPlugin from 'extract-text-webpack-plugin';
3 |
4 | export default (config, options) => {
5 | if (options.docs) {
6 | let jsLoader = '';
7 | let cssSourceMap = options.development ? '?sourceMap' : '';
8 |
9 | config = _.extend({}, config, {
10 | entry: {
11 | bundle: './docs/client.js'
12 | },
13 | output: {
14 | filename: '[name].js',
15 | path: './docs-built/assets',
16 | publicPath: '/assets/'
17 | },
18 | externals: undefined,
19 | resolve: {
20 | extensions: ['', '.js', '.json']
21 | },
22 | module: {
23 | loaders: config.module.loaders
24 | .map(value => {
25 | if (/\.js\/$/.test(value.test.toString())) {
26 | jsLoader = value.loader;
27 |
28 | return _.extend({}, value, {
29 | loader: jsLoader + '!client',
30 | exclude: /node_modules|Samples\.js/
31 | });
32 | }
33 |
34 | return value;
35 | })
36 | .concat([
37 | { test: /Samples.js/, loader: 'transform?brfs!' + jsLoader },
38 | { test: /\.css/, loader: ExtractTextPlugin.extract('style', `css${cssSourceMap}`) },
39 | { test: /\.less$/, loader: ExtractTextPlugin.extract('style', `css${cssSourceMap}!less${cssSourceMap}`) },
40 | { test: /\.json$/, loader: 'json' },
41 |
42 | { test: /\.jpe?g$|\.gif$|\.png$/, loader: 'file?name=[name].[ext]' },
43 | { test: /\.eot$|\.ttf$|\.svg$|\.woff2?$/, loader: 'file?name=[name].[ext]' }
44 | ])
45 | },
46 | plugins: config.plugins.concat([
47 | new ExtractTextPlugin('[name].css')
48 | ])
49 | });
50 |
51 | return config;
52 | }
53 |
54 | return config;
55 | }
56 |
--------------------------------------------------------------------------------
/webpack/strategies/index.js:
--------------------------------------------------------------------------------
1 | import development from './development';
2 | import docs from './docs';
3 | import optimize from './optimize';
4 | import test from './test';
5 |
6 | export default [
7 | optimize,
8 | docs,
9 | development,
10 | test
11 | ];
12 |
--------------------------------------------------------------------------------
/webpack/strategies/optimize.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export default (config, options) => {
4 | if (options.optimize) {
5 | config = _.extend({}, config, {
6 | output: _.extend({}, config.output, {
7 | filename: '[name].min.js'
8 | })
9 | });
10 |
11 | return config;
12 | }
13 |
14 | return config;
15 | }
16 |
--------------------------------------------------------------------------------
/webpack/strategies/test.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export default (config, options) => {
4 | if (options.test) {
5 | config = _.extend({}, config, {
6 | devtool: 'inline-source-map',
7 | entry: undefined,
8 | output: {
9 | pathinfo: true
10 | },
11 | externals: undefined
12 | });
13 |
14 | return config;
15 | }
16 |
17 | return config;
18 | }
19 |
--------------------------------------------------------------------------------
/webpack/test.config.js:
--------------------------------------------------------------------------------
1 | import config from './webpack.config';
2 |
3 | export default config({test: true});
4 |
--------------------------------------------------------------------------------
/webpack/webpack.config.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import webpack from 'webpack';
3 | import strategies from './strategies';
4 | import yargs from 'yargs';
5 |
6 | const argv = yargs
7 | .alias('p', 'optimize-minimize')
8 | .alias('d', 'debug')
9 | .argv;
10 |
11 | const defaultOptions = {
12 | development: argv.debug,
13 | docs: false,
14 | test: false,
15 | optimize: argv.optimizeMinimize
16 | };
17 |
18 | export default (options) => {
19 | options = _.merge({}, defaultOptions, options);
20 | const environment = options.test || options.development ?
21 | 'development' : 'production';
22 |
23 | const config = {
24 | entry: {
25 | 'react-ui': './src/index.js'
26 | },
27 |
28 | output: {
29 | path: './dist',
30 | filename: '[name].js',
31 | library: 'ReactUI',
32 | libraryTarget: 'umd'
33 | },
34 |
35 | externals: [
36 | {
37 | 'react': {
38 | root: 'React',
39 | commonjs2: 'react',
40 | commonjs: 'react',
41 | amd: 'react'
42 | }
43 | }
44 | ],
45 |
46 | module: {
47 | loaders: [
48 | { test: /\.js/, loader: 'babel?optional=es7.objectRestSpread', exclude: /node_modules/ }
49 | ]
50 | },
51 |
52 | plugins: [
53 | new webpack.DefinePlugin({
54 | 'process.env': {
55 | 'NODE_ENV': JSON.stringify(environment)
56 | }
57 | })
58 | ]
59 | };
60 |
61 | return strategies.reduce((conf, strategy) => {
62 | return strategy(conf, options);
63 | }, config);
64 | }
65 |
--------------------------------------------------------------------------------