├── .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 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Dropdown link 41 | Dropdown link 42 | 43 | 44 | 45 |
46 | 47 | 48 | 49 | 1140x500 50 | 54 | 55 | 56 | 1140x500 57 | 61 | 62 | 63 | 1140x500 64 | 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 | 34 | 35 | 36 | 37 | 38 | 39 | 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 !== ")", "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 | [![npm version](https://badge.fury.io/js/react-mount.svg)](http://badge.fury.io/js/react-mount) 5 | [![Bower version](https://badge.fury.io/bo/react-mount.svg)](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 | --------------------------------------------------------------------------------