├── .gitignore
├── LICENSE.txt
├── bower.json
├── examples
├── basic
│ └── index.html
├── expressions
│ └── index.html
├── react-bootstrap-browserify
│ ├── app.js
│ ├── browserified.js
│ └── index.html
└── react-bootstrap
│ ├── index.html
│ └── react-bootstrap.min.js
├── gulpfile.js
├── package.json
├── react-mount.js
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Philipp Adrian, philippadrian.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-mount",
3 |
4 | "version": "0.1.3",
5 |
6 | "author": {
7 | "name" : "Philipp Adrian",
8 | "email" : "hello@philippadrian.com",
9 | "website" : "http://philippadrian.com"
10 | },
11 |
12 | "description": "React goes web component – Use custom tags to place react components directly in html.",
13 |
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/greenish/react-mount.git"
17 | },
18 |
19 | "main":"react-mount.js",
20 |
21 | "keywords": [
22 | "react",
23 | "reactjs",
24 | "html",
25 | "client",
26 | "front end",
27 | "mount",
28 | "component",
29 | "custom tag",
30 | "web component"
31 | ],
32 |
33 | "dependencies": {
34 | "react": ">=0.13"
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/examples/basic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | react-mount example
6 |
7 |
21 |
22 |
23 |
24 |
25 |
react-mount
26 |
27 | Basic Example
28 |
29 |
30 |
31 | If you read this, the component has not been mouted. React is not running.
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
50 |
51 |
52 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/examples/expressions/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | react-mount example
6 |
7 |
21 |
22 |
23 |
24 |
25 |
react-mount
26 |
27 | Expressions Example
28 |
29 |
30 |
31 | {window.paragraph}
32 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/examples/react-bootstrap-browserify/app.js:
--------------------------------------------------------------------------------
1 | var mount = require("react-mount");
2 | var bootstrap = require("react-bootstrap");
3 |
4 | mount(
5 | bootstrap,
6 | {
7 | preserveAttributes: ["offsetTop","offsetBottom","onDismiss","dismissAfter","pullRight","bsClass","bsSize","bsStyle","navDropdown","navItem","componentClass","defaultActiveIndex","activeIndex","pauseOnHover","onSlideEnd","onSelect","onAnimateOutEnd","animateIn","animateOut","defaultExpanded","eventKey","activeKey","activeHref","noCaret","hasFeedback","groupClassName","addonBefore","addonAfter","buttonBefore","buttonAfter","wrapperClassName","labelClassName","onClick","closeButton","onRequestHide","defaultNavExpanded","onToggle","navExpanded","fixedTop","fixedBottom","staticTop","toggleButton","toggleNavKey","defaultOverlayShown","onMouseOver","onMouseOut","onFocus","onBlur","delayShow","delayHide","defaultActiveKey","positionLeft","positionTop","arrowOffsetLeft","arrowOffsetTop","isChild","srOnly","interpolateClass","dropdownTitle","autoFocus","checkedLink","valueLink","onChange","isReactClassApproved","hasOwnProperty","dangerouslySetInnerHTML","defaultValue","defaultChecked","isReactClassApprove"]
8 | }
9 | );
10 |
11 |
--------------------------------------------------------------------------------
/examples/react-bootstrap-browserify/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | react-mount example
6 |
7 |
23 |
24 |
25 |
26 | react-mount
27 |
28 | React Bootstrap Browserified Example
29 |
30 |
31 |
32 | Default
33 | Primary
34 | Success
35 | Info
36 | Warning
37 | Danger
38 | Link
39 |
40 | Dropdown link
41 | Dropdown link
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
First slide label
52 |
Nulla vitae elit libero, a pharetra augue mollis interdum.
53 |
54 |
55 |
56 |
57 |
58 |
Second slide label
59 |
Nulla vitae elit libero, a pharetra augue mollis interdum.
60 |
61 |
62 |
63 |
64 |
65 |
Third slide label
66 |
Nulla vitae elit libero, a pharetra augue mollis interdum.
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/examples/react-bootstrap/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | react-mount example
6 |
7 |
23 |
24 |
25 |
26 |
27 | react-mount
28 |
29 | React Bootstrap Example
30 |
31 |
32 |
33 | Default
34 | Primary
35 | Success
36 | Info
37 | Warning
38 | Danger
39 | Link
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/examples/react-bootstrap/react-bootstrap.min.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define(["react"],t):"object"==typeof exports?exports.ReactBootstrap=t(require("react")):e.ReactBootstrap=t(e.React)}(this,function(e){return function(e){function t(n){if(r[n])return r[n].exports;var s=r[n]={exports:{},id:n,loaded:!1};return e[n].call(s.exports,s,s.exports,t),s.loaded=!0,s.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){"use strict";var n=function(e){return e&&e.__esModule?e["default"]:e},s=n(r(23)),o=n(r(24)),i=n(r(15)),a=n(r(25)),p=n(r(3)),l=n(r(26)),c=n(r(7)),u=n(r(10)),d=n(r(27)),h=n(r(31)),f=n(r(28)),m=n(r(29)),v=n(r(30)),y=n(r(8)),g=n(r(32)),b=n(r(11)),T=n(r(12)),P=n(r(16)),O=n(r(34)),N=n(r(35)),E=n(r(36)),C=n(r(17)),x=n(r(37)),w=n(r(38)),S=n(r(39)),k=n(r(40)),_=n(r(41)),D=n(r(42)),M=n(r(18)),I=n(r(44)),A=n(r(19)),j=n(r(43)),B=n(r(45)),K=n(r(13)),L=n(r(46)),H=n(r(49)),R=n(r(20)),W=n(r(47)),U=n(r(48)),z=n(r(50)),q=n(r(51)),V=n(r(52)),F=n(r(53)),G=n(r(54)),Y=n(r(56)),Z=n(r(57)),Q=n(r(55)),J=n(r(58)),X=n(r(59)),$=n(r(9));e.exports={Accordion:s,Affix:o,AffixMixin:i,Alert:a,BootstrapMixin:p,Badge:l,Button:c,ButtonGroup:u,ButtonToolbar:d,CollapsableNav:h,Carousel:f,CarouselItem:m,Col:v,CollapsableMixin:y,DropdownButton:g,DropdownMenu:b,DropdownStateMixin:T,FadeMixin:P,Glyphicon:O,Grid:N,Input:E,Interpolate:C,Jumbotron:x,Label:w,ListGroup:S,ListGroupItem:k,MenuItem:_,Modal:D,Nav:M,Navbar:I,NavItem:A,ModalTrigger:j,OverlayTrigger:B,OverlayMixin:K,PageHeader:L,Panel:H,PanelGroup:R,PageItem:W,Pager:U,Popover:z,ProgressBar:q,Row:V,SplitButton:F,SubNav:G,TabbedArea:Y,Table:Z,TabPane:Q,Tooltip:J,Well:X,constants:$}},function(t){t.exports=e},function(e){function t(){for(var e,r="",n=0;n=t-a?"bottom":null!=i&&i>=r?"top":!1,this.affixed!==p&&(null!=this.unpin&&(e.style.top=""),l="affix"+(p?"-"+p:""),this.affixed=p,this.unpin="bottom"===p?this.getPinnedOffset(e):null,"bottom"===p&&(e.className=e.className.replace(/affix-top|affix-bottom|affix/,"affix-bottom"),c=t-a-e.offsetHeight-o.getOffset(e).top),this.setState({affixClass:l,affixPositionTop:c}))))},checkPositionWithEventLoop:function(){setTimeout(this.checkPosition,0)},componentDidMount:function(){this._onWindowScrollListener=i.listen(window,"scroll",this.checkPosition),this._onDocumentClickListener=i.listen(s.findDOMNode(this).ownerDocument,"click",this.checkPositionWithEventLoop)},componentWillUnmount:function(){this._onWindowScrollListener&&this._onWindowScrollListener.remove(),this._onDocumentClickListener&&this._onDocumentClickListener.remove()},componentDidUpdate:function(e,t){t.affixClass===this.state.affixClass&&this.checkPositionWithEventLoop()}};e.exports=a},function(e,t,r){"use strict";function n(e,t){var r=e.querySelectorAll("."+t.join("."));r=[].map.call(r,function(e){return e});for(var n=0;n=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r},o=Object.assign||function(e){for(var t=1;tt?"prev":"next"},componentWillReceiveProps:function(e){var t=this.getActiveIndex();null!=e.activeIndex&&e.activeIndex!==t&&(clearTimeout(this.timeout),this.setState({previousActiveIndex:t,direction:null!=e.direction?e.direction:this.getDirection(t,e.activeIndex)}))},componentDidMount:function(){this.waitForNext()},componentWillUnmount:function(){clearTimeout(this.timeout)},next:function(e){e&&e.preventDefault();var t=this.getActiveIndex()+1,r=c.numberOf(this.props.children);if(t>r-1){if(!this.props.wrap)return;t=0}this.handleSelect(t,"next")},prev:function(e){e&&e.preventDefault();var t=this.getActiveIndex()-1;if(0>t){if(!this.props.wrap)return;t=c.numberOf(this.props.children)-1}this.handleSelect(t,"prev")},pause:function(){this.isPaused=!0,clearTimeout(this.timeout)},play:function(){this.isPaused=!1,this.waitForNext()},waitForNext:function(){!this.isPaused&&this.props.slide&&this.props.interval&&null==this.props.activeIndex&&(this.timeout=setTimeout(this.next,this.props.interval))},handleMouseOver:function(){this.props.pauseOnHover&&this.pause()},handleMouseOut:function(){this.isPaused&&this.play()},render:function(){var e={carousel:!0,slide:this.props.slide};return i.createElement("div",s({},this.props,{className:p(this.props.className,e),onMouseOver:this.handleMouseOver,onMouseOut:this.handleMouseOut}),this.props.indicators?this.renderIndicators():null,i.createElement("div",{className:"carousel-inner",ref:"inner"},c.map(this.props.children,this.renderItem)),this.props.controls?this.renderControls():null)},renderPrev:function(){return i.createElement("a",{className:"left carousel-control",href:"#prev",key:0,onClick:this.prev},i.createElement("span",{className:"glyphicon glyphicon-chevron-left"
2 | }))},renderNext:function(){return i.createElement("a",{className:"right carousel-control",href:"#next",key:1,onClick:this.next},i.createElement("span",{className:"glyphicon glyphicon-chevron-right"}))},renderControls:function(){if(!this.props.wrap){var e=this.getActiveIndex(),t=c.numberOf(this.props.children);return[0!==e?this.renderPrev():null,e!==t-1?this.renderNext():null]}return[this.renderPrev(),this.renderNext()]},renderIndicator:function(e,t){var r=t===this.getActiveIndex()?"active":null;return i.createElement("li",{key:t,className:r,onClick:this.handleSelect.bind(this,t,null)})},renderIndicators:function(){var e=[];return c.forEach(this.props.children,function(t,r){e.push(this.renderIndicator(t,r)," ")},this),i.createElement("ol",{className:"carousel-indicators"},e)},getActiveIndex:function(){return null!=this.props.activeIndex?this.props.activeIndex:this.state.activeIndex},handleItemAnimateOutEnd:function(){this.setState({previousActiveIndex:null,direction:null},function(){this.waitForNext(),this.props.onSlideEnd&&this.props.onSlideEnd()})},renderItem:function(e,t){var r=this.getActiveIndex(),n=t===r,s=null!=this.state.previousActiveIndex&&this.state.previousActiveIndex===t&&this.props.slide;return a(e,{active:n,ref:e.ref,key:e.key?e.key:t,index:t,animateOut:s,animateIn:n&&null!=this.state.previousActiveIndex&&this.props.slide,direction:this.state.direction,onAnimateOutEnd:s?this.handleItemAnimateOutEnd:null})},handleSelect:function(e,t){clearTimeout(this.timeout);var r=this.getActiveIndex();if(t=t||this.getDirection(r,e),this.props.onSelect&&this.props.onSelect(e,t),null==this.props.activeIndex&&e!==r){if(null!=this.state.previousActiveIndex)return;this.setState({activeIndex:e,previousActiveIndex:r,direction:t})}}});e.exports=u},function(e,t,r){"use strict";var n=function(e){return e&&e.__esModule?e["default"]:e},s=Object.assign||function(e){for(var t=1;t=0&&(t["col-"+s+this.props[n]]=!0),n=r+"Push",s=r+"-push-",this.props[n]>=0&&(t["col-"+s+this.props[n]]=!0),n=r+"Pull",s=r+"-pull-",this.props[n]>=0&&(t["col-"+s+this.props[n]]=!0)},this),o.createElement(e,s({},this.props,{className:i(this.props.className,t)}),this.props.children)}});e.exports=p},function(e,t,r){"use strict";var n=function(e){return e&&e.__esModule?e["default"]:e},s=r(1),o=n(s),i=s.cloneElement,a=n(r(3)),p=n(r(8)),l=n(r(2)),c=n(r(6)),u=n(r(4)),d=n(r(5)),h=o.createClass({displayName:"CollapsableNav",mixins:[a,p],propTypes:{onSelect:o.PropTypes.func,expanded:o.PropTypes.bool,eventKey:o.PropTypes.any},getCollapsableDOMNode:function(){return this.getDOMNode()},getCollapsableDimensionValue:function(){var e=0,t=this.refs;for(var r in t)if(t.hasOwnProperty(r)){var n=t[r].getDOMNode(),s=n.offsetHeight,o=c.getComputedStyles(n);e+=s+parseInt(o.marginTop,10)+parseInt(o.marginBottom,10)}return e},render:function(){var e=this.props.collapsable?this.getCollapsableClassSet():{};return(void 0===this.props.className||-2===this.props.className.split(" ").indexOf("navbar-collapse"))&&(e["navbar-collapse"]=this.props.collapsable),o.createElement("div",{eventKey:this.props.eventKey,className:l(this.props.className,e)},u.map(this.props.children,this.props.collapsable?this.renderCollapsableNavChildren:this.renderChildren))},getChildActiveProp:function(e){return e.props.active?!0:null!=this.props.activeKey&&e.props.eventKey===this.props.activeKey?!0:null!=this.props.activeHref&&e.props.href===this.props.activeHref?!0:e.props.active},renderChildren:function(e,t){var r=e.key?e.key:t;return i(e,{activeKey:this.props.activeKey,activeHref:this.props.activeHref,ref:"nocollapse_"+r,key:r,navItem:!0})},renderCollapsableNavChildren:function(e,t){var r=e.key?e.key:t;return i(e,{active:this.getChildActiveProp(e),activeKey:this.props.activeKey,activeHref:this.props.activeHref,onSelect:d(e.props.onSelect,this.props.onSelect),ref:"collapsable_"+r,key:r,navItem:!0})}});e.exports=h},function(e,t,r){"use strict";var n=function(e){return e&&e.__esModule?e["default"]:e},s=Object.assign||function(e){for(var t=1;t=0:e===t}var s=function(e){return e&&e.__esModule?e["default"]:e},o=r(1),i=s(o),a=o.cloneElement,p=s(r(13)),l=s(r(6)),c=s(r(5)),u=s(r(21)),d=i.createClass({displayName:"OverlayTrigger",mixins:[p],propTypes:{trigger:i.PropTypes.oneOfType([i.PropTypes.oneOf(["manual","click","hover","focus"]),i.PropTypes.arrayOf(i.PropTypes.oneOf(["click","hover","focus"]))]),placement:i.PropTypes.oneOf(["top","right","bottom","left"]),delay:i.PropTypes.number,delayShow:i.PropTypes.number,delayHide:i.PropTypes.number,defaultOverlayShown:i.PropTypes.bool,overlay:i.PropTypes.node.isRequired},getDefaultProps:function(){return{placement:"right",trigger:["hover","focus"]}},getInitialState:function(){return{isOverlayShown:null==this.props.defaultOverlayShown?!1:this.props.defaultOverlayShown,overlayLeft:null,overlayTop:null}},show:function(){this.setState({isOverlayShown:!0},function(){this.updateOverlayPosition()})},hide:function(){this.setState({isOverlayShown:!1})},toggle:function(){this.state.isOverlayShown?this.hide():this.show()},renderOverlay:function(){return this.state.isOverlayShown?a(this.props.overlay,{onRequestHide:this.hide,placement:this.props.placement,positionLeft:this.state.overlayLeft,positionTop:this.state.overlayTop}):i.createElement("span",null)},render:function(){if("manual"===this.props.trigger)return i.Children.only(this.props.children);var e={};return n("click",this.props.trigger)&&(e.onClick=c(this.toggle,this.props.onClick)),n("hover",this.props.trigger)&&(e.onMouseOver=c(this.handleDelayedShow,this.props.onMouseOver),e.onMouseOut=c(this.handleDelayedHide,this.props.onMouseOut)),n("focus",this.props.trigger)&&(e.onFocus=c(this.handleDelayedShow,this.props.onFocus),e.onBlur=c(this.handleDelayedHide,this.props.onBlur)),a(i.Children.only(this.props.children),e)},componentWillUnmount:function(){clearTimeout(this._hoverDelay)},componentDidMount:function(){this.props.defaultOverlayShown&&this.updateOverlayPosition()},handleDelayedShow:function(){if(null!=this._hoverDelay)return clearTimeout(this._hoverDelay),void(this._hoverDelay=null);var e=null!=this.props.delayShow?this.props.delayShow:this.props.delay;return e?void(this._hoverDelay=setTimeout(function(){this._hoverDelay=null,this.show()}.bind(this),e)):void this.show()},handleDelayedHide:function(){if(null!=this._hoverDelay)return clearTimeout(this._hoverDelay),void(this._hoverDelay=null);var e=null!=this.props.delayHide?this.props.delayHide:this.props.delay;return e?void(this._hoverDelay=setTimeout(function(){this._hoverDelay=null,this.hide()}.bind(this),e)):void this.hide()},updateOverlayPosition:function(){if(this.isMounted()){var e=this.calcOverlayPosition();this.setState({overlayLeft:e.left,overlayTop:e.top})}},calcOverlayPosition:function(){var e=this.getPosition(),t=this.getOverlayDOMNode(),r=t.offsetHeight,n=t.offsetWidth;switch(this.props.placement){case"right":return{top:e.top+e.height/2-r/2,left:e.left+e.width};case"left":return{top:e.top+e.height/2-r/2,left:e.left-n};case"top":return{top:e.top-r,left:e.left+e.width/2-n/2};case"bottom":return{top:e.top+e.height,left:e.left+e.width/2-n/2};default:throw new Error('calcOverlayPosition(): No such placement of "'+this.props.placement+'" found.')}},getPosition:function(){var e=i.findDOMNode(this),t=this.getContainerDOMNode(),r="BODY"===t.tagName?l.getOffset(e):l.getPosition(e,t);return u({},r,{height:e.offsetHeight,width:e.offsetWidth})}});e.exports=d},function(e,t,r){"use strict";var n=function(e){return e&&e.__esModule?e["default"]:e},s=Object.assign||function(e){for(var t=1;t=0.13",
37 | "react-tools" : ">=0.13"
38 | },
39 |
40 | "devDependencies": {
41 | "react-mount" : "git://github.com/greenish/react-mount.git#master",
42 | "react": "^0.13.1",
43 | "react-tools" : "^0.13.1",
44 | "react-bootstrap" : "^0.20.1",
45 |
46 | "gulp" : "^3.8.11",
47 | "gulp-jshint" : "^1.9.x",
48 | "gulp-uglify" : "^1.0.x",
49 | "gulp-rename" : "^1.2.2",
50 | "gulp-git" : "^1.2.1",
51 | "gulp-bump" : "^0.3.0",
52 |
53 | "browserify": "^8.0.x",
54 | "vinyl-transform": "^1.0.x",
55 | "reactify" : "^1.1.0"
56 | },
57 |
58 | "engines": {
59 | "node": ">=0.10.x"
60 | },
61 |
62 | "private" : false,
63 |
64 | "browserify": {
65 | "transform": [
66 | ["reactify"]
67 | ]
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/react-mount.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Philipp Adrian, philippadrian.com
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | "use strict";
9 | (function(module, require, window, document, undefined){
10 |
11 | var React = window.React || require("react");
12 | var ReactTransform = window.JSXTransformer ?
13 | window.JSXTransformer.transform :
14 | require("react-tools").transformWithDetails;
15 | var objectKeys = Object.keys || objectKeysShim;
16 | var selfClosingTags = ["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"];
17 | var preserveReactAttributes = ["onBeforeInput", "onBeforeInputCapture", "onCompositionEnd", "onCompositionEndCapture", "onCompositionStart", "onCompositionStartCapture", "onCompositionUpdate", "onCompositionUpdateCapture", "onChange", "onChangeCapture", "ResponderEventPlugin", "SimpleEventPlugin", "TapEventPlugin", "EnterLeaveEventPlugin", "ChangeEventPlugin", "SelectEventPlugin", "BeforeInputEventPlugin", "AnalyticsEventPlugin", "MobileSafariClickEventPlugin", "onMouseEnter", "onMouseLeave", "onSelect", "onSelectCapture", "onBlur", "onBlurCapture", "onClick", "onClickCapture", "onContextMenu", "onContextMenuCapture", "onCopy", "onCopyCapture", "onCut", "onCutCapture", "onDoubleClick", "onDoubleClickCapture", "onDrag", "onDragCapture", "onDragEnd", "onDragEndCapture", "onDragEnter", "onDragEnterCapture", "onDragExit", "onDragExitCapture", "onDragLeave", "onDragLeaveCapture", "onDragOver", "onDragOverCapture", "onDragStart", "onDragStartCapture", "onDrop", "onDropCapture", "onFocus", "onFocusCapture", "onInput", "onInputCapture", "onKeyDown", "onKeyDownCapture", "onKeyPress", "onKeyPressCapture", "onKeyUp", "onKeyUpCapture", "onLoad", "onLoadCapture", "onError", "onErrorCapture", "onMouseDown", "onMouseDownCapture", "onMouseMove", "onMouseMoveCapture", "onMouseOut", "onMouseOutCapture", "onMouseOver", "onMouseOverCapture", "onMouseUp", "onMouseUpCapture", "onPaste", "onPasteCapture", "onReset", "onResetCapture", "onScroll", "onScrollCapture", "onSubmit", "onSubmitCapture", "onTouchCancel", "onTouchCancelCapture", "onTouchEnd", "onTouchEndCapture", "onTouchMove", "onTouchMoveCapture", "onTouchStart", "onTouchStartCapture", "onWheel", "onWheelCapture", "className"];
18 | ///////////////////////////////////////////////////////////////////////////////
19 | // Export / Attach / Public
20 |
21 | module.exports = mount;
22 |
23 | if(typeof window.React === "object")
24 | window.React.mount = mount;
25 | if(typeof define === "function" && define.amd)
26 | define(function(){return mount});
27 |
28 | ///////////////////////////////////////////////////////////////////////////////
29 | ///////////////////////////////////////////////////////////////////////////////
30 |
31 | function getNodes(tags, context) {
32 | var keys = objectKeys(tags);
33 | var returns = [];
34 |
35 | // keys to uppercase to compare to nodeName
36 | for(var i=0; i= 0) {
40 | return [context];
41 | };
42 |
43 | //Mount all top-level child tags within context
44 | var nodes, el, mount, n;
45 | for(var i=0; i= 0) {
52 | mount = false;
53 | break;
54 | }
55 | if(el === context) break;
56 | }
57 | if(mount) returns.push(nodes[n]);
58 | }
59 | }
60 | return returns;
61 | }
62 |
63 | ///////////////////////////////////////////////////////////////////////////////
64 |
65 | function htmlToComponent(str, tags, opts) {
66 | var keys = objectKeys(tags);
67 | var props = opts.props || {};
68 | var preserveAttributes = opts.preserveAttributes || [];
69 | var tagAttributes = [];
70 | var reactTags={};
71 | var reactKeys=[];
72 | var key;
73 | for(var i = 0; i\"']|\".*\"|'.*')*>", "ig"), function(match, start){
87 | // find/replace spell-save attributes within tag to restore capitals
88 | if(start !== "")
89 | for(var a=0; a)", "i"), start+key+"$1");
95 | });
96 | }
97 |
98 | // remove/disable html comments
99 | str = str.replace(//g,"")
100 |
101 | // find/replace global spell-save attributes to restore capitals
102 | var attributes = preserveReactAttributes.concat(preserveAttributes);
103 | for(var i=0; i" to "…/>"
136 | for(var k=0; k\"']|\".*\"|'.*')*)\/?>","ig"),"$1"+" />");
138 | }
139 |
140 | // Remove auto-quotes around {expressions} + Remove {expression}="" attachment
141 | str = str
142 | .replace(/<(?:[^>\"']|\".*\"|'.*')*=(?:[^>\"']|\".*\"|'.*')*>/g, function(match){
143 | return match.replace(/(?:"|')?(\{(?:[^\'"}]|".*"|'.*')*\})(?:"|')?(?:=""|='')?/g, "$1");
144 | });
145 |
146 | // Transform style attribute string ("color:red; background-image:url()") to object ({{color:'red', backgroundImage:'url()'}})
147 | str = str.replace(/(<(?:[^>"']|".*"|'.*')*\sstyle\s*=\s*)((?:"(?:[^"]|\s)*")|(?:'(?:[^']|\s)*'))((?:[^>"']|".*"|'.*')*>)/ig, function(match, start, style, end){
148 | var styles = "";
149 | style
150 | .slice(1,-1)
151 | .replace(/(")|'/g,'"')
152 | .replace(/\s*([^:;]*)\s*:\s*((?:[^;"]|".*")*)\s*;?/g, function(match, key, value){
153 |
154 | // transform key to camelCase
155 | key = key.replace(/-([^-]*)/g, function(match, part){
156 | if(part === "ms") return part;
157 | return part.charAt(0).toUpperCase()+part.slice(1);
158 | });
159 |
160 | styles+=key+":'"+value+"',";
161 |
162 | return match;
163 | });
164 |
165 | return start+"{{"+styles.slice(0,-1)+"}}"+end;
166 | });
167 |
168 | return str;
169 | }
170 |
171 | ///////////////////////////////////////////////////////////////////////////////
172 |
173 | function mount(tags, opts){
174 | opts = opts || {};
175 | var component, wrapper;
176 |
177 | // Get HTML nodes that need to be mounted.
178 | var nodes = getNodes(tags, opts.context || document.body);
179 |
180 | for(var i=0; i\"']|\".*\"|'.*')*\\s)"+search+"(\s*=(?:[^>\"']|\".*\"|'.*')*>)", "ig"), "$1"+replace+"$2");
213 | }
214 |
215 | ///////////////////////////////////////////////////////////////////////////////
216 |
217 | })(
218 | typeof module === "object" ?
219 | module : {},
220 | typeof require === "function" ?
221 | require :
222 | function (required){
223 | throw "react-mount: Error - React and JSXTransformer/react-tools are required for react-mount to work";
224 | },
225 | window,
226 | document);
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # react-mount
2 | React goes web component – Use custom tags to place react components directly in html.
3 |
4 | [](http://badge.fury.io/js/react-mount)
5 | [](http://badge.fury.io/bo/react-mount)
6 |
7 | - [Install](#install)
8 | - [Basic Usage](#basic-usage)
9 | - [Tag Content](#tag-content)
10 | - [Nested Components](#nested-components)
11 | - [Expressions and Properties](#expressions-and-properties)
12 | - [Case-Sensitive Attributes](#case-sensitive-attributes)
13 | - [Html Comments](#html-comments)
14 | - [API: mount( tags _[, opts]_ );](#api)
15 | - [License](#license)
16 |
17 |
18 | ## Install
19 |
20 | ##### Download
21 | From Github, [NPM](https://www.npmjs.org/package/react-mount):
22 |
23 | ```sh
24 | $ npm install --save react-mount
25 | ```
26 | or [Bower](http://bower.io/search/?q=react-mount):
27 | ```sh
28 | $ bower install react-mount
29 | ```
30 |
31 | ##### Include
32 | With AMD or Browserify:
33 | ```js
34 | var mount = require("react-mount");
35 | ```
36 | Vanilla JS:
37 | ```html
38 |
39 |
42 | ```
43 |
44 |
45 | ## Basic Usage
46 | ```html
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
68 |
69 | ```
70 | ##### Output
71 | _Component mounted. React is running._
72 |
73 | ## Tag Content
74 | Content of custom tags can be written in Html or JSX.
75 | ```html
76 |
77 |
78 | Html is transformed to JSX for you.
79 |
80 |
81 | ```
82 | The content of a custom tag is available in the mounted react component with `this.props.children`:
83 | ```js
84 | var translucentComponent = React.createClass({
85 | render: function() {
86 | return (
87 | {this.props.children}
88 | );
89 | }
90 | });
91 | ```
92 | ##### Output
93 | HTML IS TRANSFORMED TO __JSX__ FOR YOU.
94 |
95 | ## Nested Components
96 | Custom component tags can be nested as long as all used components are properly mounted:
97 | ```html
98 |
99 |
100 |
101 | ```
102 | ```js
103 | mount({
104 | "react-component" : ReactComponent,
105 | "translucent-component" : TranslucentComponent
106 | });
107 | ```
108 | ##### Output
109 | _Component mounted. React is running._
110 |
111 | ## Expressions and Properties
112 | `{expressions}` can be used within a tag and are executed properly.
113 | Within `{expressions}` properties from the global namespace `window` can be accessed.
114 | However to keep the global namespace clear, properties can also be defined in the `opts` object passed to the `mount` function:
115 | ```js
116 | window.paragraph = "Component mounted. React is running.";
117 |
118 | React.mount({
119 | "translucent-component" : ReactComponent
120 | },
121 | {
122 | props : {
123 | list : [
124 | "Item 1",
125 | "Item 2",
126 | "Item 3"
127 | ],
128 | attribute : "myAttribute"
129 | }
130 | });
131 | ```
132 | These properties are avaliable within expressions as `props.key`:
133 | ```js
134 | {window.paragraph} // "Component mounted. React is running."
135 |
136 | {props.list.length <= 3 ? "Yes!" : "No!"} // Yes!
137 |
138 | // shortcut for simple reference (props. is appended for you)
139 | {attribute} // "myAttribute"
140 |
141 | ```
142 | ```html
143 |
144 |
145 | {window.paragraph}
146 | Less than four list items? {props.list.length <= 3 ? "Yes!" : "No!"}
147 |
148 | {props.list.map(function(value, i){
149 | return {value} ;
150 | })}
151 |
152 |
153 | ```
154 | Within react component:
155 | `this.props.attribute === "myAttribute"`
156 | `this.props.children` contains `...
`, `...
` and ``.
157 | ##### Output
158 | Component mounted. React is running.
159 |
160 | Less than four list items? __Yes!__
161 |
162 | - Item 1
163 | - Item 2
164 | - Item 3
165 |
166 | ## Case-Sensitive Attributes
167 | Html is case insensitiv and transforms everything outside of strings to lowercase. React `props` however are case sensitive and therefore some components require correctly capitalized attributes.
168 |
169 | There are two ways to preserve the capitalization of attributes:
170 |
171 | __A) Per component__
172 | ```js
173 | mount({
174 | "react-component" : ["camelCaseAttribute", "anotherAttribute", ReactComponent]
175 | });
176 | ```
177 | __B) For all components__
178 | ```js
179 | mount({
180 | "react-component" : ReactComponent
181 | }, {
182 | preserveAttributes : ["camelCaseAttribute", "anotherAttribute"]
183 | });
184 | ```
185 | In both cases you can now safely use the preserved attributes:
186 | ```html
187 |
188 | ```
189 | Within react component:
190 | `this.props.camelCaseAttribute === "preserved attribute"`
191 | __but__
192 | `this.props.notPreservedAttribute === undefined`
193 | `this.props.notpreservedattribute === "not preserved"`
194 |
195 |
196 | ## HTML Comments
197 | Html comments do not affect the output of the rendering in any way.
198 | They can be used to mask unrendered content before react kicks in.
199 | ```html
200 |
201 |
204 |
205 | ```
206 | `this.props.children` still contains `value `.
207 | ##### Output
208 | _value_
209 |
210 | __Tipp:__ Use JSX style `{/* comments */}` for actual comments.
211 |
212 | ## API
213 |
214 | ### `mount( tags [, opts] );`
215 |
216 | ##### tags `required`
217 | > _Type_ `Object`
218 | >
219 | > Object with _tags_ and their corresponding _components_ to be mounted.
220 | >
221 | > ```js
222 | > {
223 | > "tag-name" : ReactComponent,
224 | > "tag-Name" : ReactComponent,
225 | > ...
226 | > "TAG-NAME" : ["camelCaseAttribute", ReactComponent],
227 | > ...
228 | > [tag] : [component | Array]
229 | > }
230 | > ```
231 | > Tag names are __case insensitive__. All the above definitions would do the same / the first component would be mounted to all of the tags.
232 | >
233 | > __Optional:__ Instead of a React component, an array with case-sensitive attributes can be defined. The last item of the array is the react component to be mounted.
234 | > See: [Case-Sensitive Attributes](#case-sensitive-attributes)
235 | >
236 | > __Tipp:__ Lower case tag names containing at least one dash are/will be valid html.
237 | > See: http://w3c.github.io/webcomponents/spec/custom/#concepts
238 |
239 | ##### opts `optional`
240 | > _Type_ `Object`
241 | >
242 | > Available options for `react-mount`:
243 | >
244 | > ```js
245 | > {
246 | > "context" : [HTMLElement],
247 | > "props" : {...},
248 | > "preserveAttributes" : [...],
249 | > "wrapper" : [HTMLElement]
250 | > }
251 | > ```
252 | >
253 | > ###### context
254 | > > _Type_ `HTMLElement`
255 | > > _Default_ `document.body`
256 | > >
257 | > > Only tags within this element will be mounted.
258 | >
259 | > ###### props
260 | > > _Type_ `Object`
261 | > >
262 | > > `key:value` pairs of Properties.
263 | > > Properties can be used in `{expressions}` within the mounted tags.
264 | > >
265 | > > See: [Expressions and Properties](#expressions-and-properties)
266 | >
267 | > ###### preserveAttributes
268 | > > _Type_ `Array`
269 | > >
270 | > > Array of case-sensitive attributes to preserve capitatization.
271 | > >
272 | > > See: [Case-Sensitive Attributes](#case-sensitive-attributes)
273 | >
274 | > ###### wrapper
275 | > > _Type_ `HTMLElement`
276 | > > _Default_ `document.createElement("div")`
277 | > >
278 | > > Define a custom wrapper to mount react components into.
279 |
280 | ## License
281 |
282 | [The MIT License (MIT)](http://opensource.org/licenses/MIT)
283 |
284 | Copyright (c) 2015 Philipp Adrian, philippadrian.com
285 |
286 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
287 |
288 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
289 |
290 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
291 |
--------------------------------------------------------------------------------