├── .gitignore ├── LICENSE ├── README.md ├── dist ├── hyperapp-nestable.js └── hyperapp-nestable.js.map ├── package-lock.json ├── package.json └── src └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Zacharias Enochsson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is only compatible with hyperapp v1, and I do not intend to maintain it any longer. Hence, I am archiving it. 2 | 3 | # hyperapp-nestable 4 | 5 | A tiny wrapper around your app, that lets you embed it within other hyperapp apps, *as if it were a component*. Effectively answering the age-old question: "How do I make components with local state, like React's Object components, in Hyperapp?" 6 | 7 | ## Install 8 | 9 | ### ...using npm 10 | 11 | ``` 12 | npm install hyperapp-nestable 13 | ``` 14 | 15 | And `require` (or `import`, if you're using es6 modules) in your project: 16 | 17 | ```js 18 | 19 | const nestable = require('hyperapp-nestable') 20 | 21 | ``` 22 | 23 | ### ... or include in HTML from CDN 24 | 25 | Add this to the `` of your page: 26 | 27 | ```html 28 | 29 | 30 | 31 | ``` 32 | 33 | this will export `nestable` in the global scope. 34 | 35 | ## Basic Usage 36 | 37 | Use the `nestable` function in the following way: 38 | 39 | ``` 40 | var ComponentName = nestable(initialState, actions, view) 41 | ``` 42 | 43 | ... which is to say, just like you start a normal Hyperapp-app. The difference is, you don't provide a container. That is becuase instead of attaching an app to the dom immediately, `nestable` returns a component-function which will start a hyperapp app, attached to the DOM, when and where the component is rendered. 44 | 45 | An example: 46 | 47 | ```jsx 48 | 49 | /* 50 | Define a stateful Counter component 51 | */ 52 | const Counter = nestable( 53 | //STATE 54 | {value: 0}, 55 | 56 | //ACTIONS 57 | { 58 | up: _ => state => ({value: state.value + 1}), 59 | down: _ => state => ({value: state.value - 1}), 60 | }, 61 | 62 | //VIEW 63 | (state, actions) => ( 64 |

65 | 66 | {state.value} 67 | 68 |

69 | ) 70 | ) 71 | 72 | 73 | /* 74 | Use the Counter-component in an app 75 | */ 76 | 77 | app({}, {}, _ => ( 78 |
79 |

Look, a counter:

80 | 81 |
82 | ), document.body) 83 | 84 | ``` 85 | 86 | ([Live example](https://codepen.io/zaceno/pen/eygwdV)) 87 | 88 | The above example will do exactly what you expect, and render a functioning counter after the heading. 89 | 90 | ## Component's Tagname 91 | 92 | If you look at the html of the app example above, you'll see it looks like: 93 | 94 | ```html 95 |
96 |

Look, a counter:

97 | 98 |

99 | 100 | 0 101 | 102 |

103 |
104 |
105 | ``` 106 | 107 | Notice the `` element in there. That element corresponds to the vnode in the main app's view, where the counter's view is rendered. It's necessary as a means to "connect" the two vtrees. But the tag name `x-` is just the default. If you want it to be something more descriptive, we might call it `counter-` (the dash on the end is just to make it clear it's not a real html element). You would do that by giving it as the fourth argument to the `nestable` function: 108 | 109 | ```js 110 | const Counter = nestable( 111 | initialCounterState, 112 | counterActions, 113 | counterView, 114 | 'counter-' 115 | ) 116 | ``` 117 | 118 | You could make the tag a regular html tag such as `div` or `section` too. You may want that for CSS-reasons. 119 | 120 | 121 | ## Component properties & children 122 | 123 | You can pass props and children to a component (just like any other). In order for your view to be aware of them, return a component from your view. 124 | 125 | ```jsx 126 | const MyComponent = nestable( 127 | //STATE 128 | {...}, 129 | 130 | //ACTIONS 131 | {...}, 132 | 133 | //VIEW 134 | (state, actions) => (props, children) => ( 135 |
136 |

{state.foo}

137 | {children} 138 | 139 | ({value: props.initial || 0}), 177 | up: _ => state => ({value: state.value + 1}), 178 | down: _ => state => ({value: state.value - 1}), 179 | }, 180 | 181 | //VIEW 182 | ... 183 | ) 184 | ``` 185 | 186 | Now, the counter will start at the value it is given the first time it's rendered, but when that value changes, it will not affect the value of the counter. 187 | 188 | Here's a [live example](https://codepen.io/zaceno/pen/ypMLPp) 189 | 190 | ### The special action called `uninit` 191 | 192 | Corresponding to `init`, if you need something done when the component is destroyed, you can put in an action named `uninit` 193 | 194 | Here is a more complex, while somewhat contrived demonstrating many of the features mentioned here. 195 | 196 | https://codepen.io/zaceno/pen/bajpvO?editors=0011 197 | 198 | -------------------------------------------------------------------------------- /dist/hyperapp-nestable.js: -------------------------------------------------------------------------------- 1 | !function(n,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("hyperapp")):"function"==typeof define&&define.amd?define(["hyperapp"],e):n.nestable=e(n.hyperapp)}(this,function(n){"use strict";return function(e,t,o,r){return t._$r=function(){return{}},function(u,i){return n.h(r||"x-",{key:u.key,id:u.id,class:u.class,oncreate:function(r){var p=n.app(e,t,function(n,e){var t=o(n,e);return"function"==typeof t&&(t=t(r._$p,r._$c)),t},r);r._$p=u,r._$c=i,r._$r=p._$r,r._$u=p.uninit,p.init&&p.init(u),u.oncreate&&u.oncreate(r)},onupdate:function(n){n._$p=u,n._$c=i,n._$r(),u.onupdate&&u.onupdate(n)},ondestroy:function(n){n._$u&&n._$u()},onremove:function(n,e){if(!u.onremove)return e();u.onremove(n,e)}})}}}); 2 | //# sourceMappingURL=hyperapp-nestable.js.map -------------------------------------------------------------------------------- /dist/hyperapp-nestable.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["dist/hyperapp-nestable.js"],"names":["global","factory","exports","module","require","define","amd","nestable","hyperapp","this","state","actions","view","tagname","_$r","props","children","h","key","id","class","oncreate","el","wired","app","s","a","v","_$p","_$c","_$u","uninit","init","onupdate","ondestroy","onremove","done"],"mappings":"CAAC,SAAUA,EAAQC,GACC,iBAAZC,SAA0C,oBAAXC,OAAyBA,OAAOD,QAAUD,EAAQG,QAAQ,aAC9E,mBAAXC,QAAyBA,OAAOC,IAAMD,QAAQ,YAAaJ,GACjED,EAAOO,SAAWN,EAAQD,EAAOQ,UAHnC,CAIEC,KAAM,SAAWD,GAAY,aA6C/B,OA3CA,SAAmBE,EAAOC,EAASC,EAAMC,GAErC,OADAF,EAAQG,IAAM,WAAa,UACpB,SAAUC,EAAOC,GACpB,OAAOR,EAASS,EAAEJ,GAAW,MACzBK,IAAKH,EAAMG,IACXC,GAAIJ,EAAMI,GACVC,MAAOL,EAAMK,MACbC,SAAU,SAAUC,GAChB,IAAIC,EAAQf,EAASgB,IACjBd,EACAC,EACA,SAAUc,EAAGC,GACT,IAAIC,EAAIf,EAAKa,EAAGC,GAEhB,MADiB,mBAANC,IAAkBA,EAAIA,EAAEL,EAAGM,IAAKN,EAAGO,MACvCF,GAEXL,GAEJA,EAAGM,IAAMb,EACTO,EAAGO,IAAMb,EACTM,EAAGR,IAAMS,EAAMT,IACfQ,EAAGQ,IAAMP,EAAMQ,OACfR,EAAMS,MAAQT,EAAMS,KAAKjB,GACzBA,EAAMM,UAAYN,EAAMM,SAASC,IAErCW,SAAU,SAAUX,GAChBA,EAAGM,IAAMb,EACTO,EAAGO,IAAMb,EACTM,EAAGR,MACHC,EAAMkB,UAAYlB,EAAMkB,SAASX,IAErCY,UAAW,SAAUZ,GACjBA,EAAGQ,KAAOR,EAAGQ,OAEjBK,SAAU,SAAUb,EAAIc,GACrB,IAAKrB,EAAMoB,SAAU,OAAOC,IAE5BrB,EAAMoB,SAASb,EAAIc","sourcesContent":["(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('hyperapp')) :\n\ttypeof define === 'function' && define.amd ? define(['hyperapp'], factory) :\n\t(global.nestable = factory(global.hyperapp));\n}(this, (function (hyperapp) { 'use strict';\n\nfunction nestable (state, actions, view, tagname) {\n actions._$r = function () {return {}};\n return function (props, children) {\n return hyperapp.h(tagname || 'x-', {\n key: props.key,\n id: props.id,\n class: props.class,\n oncreate: function (el) {\n var wired = hyperapp.app(\n state,\n actions,\n function (s, a) {\n var v = view(s, a);\n if (typeof v === 'function') v = v(el._$p, el._$c);\n return v\n },\n el\n );\n el._$p = props;\n el._$c = children;\n el._$r = wired._$r;\n el._$u = wired.uninit;\n wired.init && wired.init(props);\n props.oncreate && props.oncreate(el);\n },\n onupdate: function (el) {\n el._$p = props;\n el._$c = children;\n el._$r();\n props.onupdate && props.onupdate(el);\n },\n ondestroy: function (el) {\n el._$u && el._$u();\n },\n onremove: function (el, done) {\n if (!props.onremove) return done()\n\n props.onremove(el, done);\n }\n })\n } \n}\n\nreturn nestable;\n\n})));\n//# sourceMappingURL=hyperapp-nestable.js.map\n"]} -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyperapp-nestable", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "commander": { 8 | "version": "2.11.0", 9 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", 10 | "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", 11 | "dev": true 12 | }, 13 | "rollup": { 14 | "version": "0.52.2", 15 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.52.2.tgz", 16 | "integrity": "sha512-1lSh5NghZu0H4QpA9wr4C1Y9lmgYK+p9MtVpriE6zpf5x3IXPbafc3k4Jfaw6upIT8eaVJLjDe4lcy7ByIYcOg==", 17 | "dev": true 18 | }, 19 | "source-map": { 20 | "version": "0.5.7", 21 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 22 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 23 | "dev": true 24 | }, 25 | "uglify-js": { 26 | "version": "3.2.2", 27 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.2.2.tgz", 28 | "integrity": "sha512-++1NO/zZIEdWf6cDIGceSJQPX31SqIpbVAHwFG5+240MtZqPG/NIPoinj8zlXQtAfMBqEt1Jyv2FiLP3n9gVhQ==", 29 | "dev": true, 30 | "requires": { 31 | "commander": "2.11.0", 32 | "source-map": "0.5.7" 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyperapp-nestable", 3 | "version": "1.1.0", 4 | "description": "Stateful components in Hyperapp", 5 | "main": "dist/hyperapp-nestable.js", 6 | "module": "src/index.js", 7 | "scripts": { 8 | "umd": "rollup src/index.js -f umd -n nestable -m -o dist/hyperapp-nestable.js", 9 | "minify": "uglifyjs dist/hyperapp-nestable.js -o dist/hyperapp-nestable.js -mc --source-map includeSources,url=hyperapp-nestable.js.map", 10 | "build": "npm run umd && npm run minify" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/zaceno/hyperapp-nestable.git" 15 | }, 16 | "author": "Zacharias Enochsson", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/zaceno/hyperapp-nestable/issues" 20 | }, 21 | "homepage": "https://github.com/zaceno/hyperapp-nestable#readme", 22 | "devDependencies": { 23 | "uglify-js": "^3.2.2", 24 | "rollup": "^0.52.2" 25 | }, 26 | "peerDependencies": { 27 | "hyperapp": "1.1.x" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import {h, app} from 'hyperapp' 2 | 3 | export default 4 | function nestable (state, actions, view, tagname) { 5 | actions._$r = function () {return {}} 6 | return function (props, children) { 7 | return h(tagname || 'x-', { 8 | key: props.key, 9 | id: props.id, 10 | class: props.class, 11 | oncreate: function (el) { 12 | var wired = app( 13 | state, 14 | actions, 15 | function (s, a) { 16 | var v = view(s, a) 17 | if (typeof v === 'function') v = v(el._$p, el._$c) 18 | return v 19 | }, 20 | el 21 | ) 22 | el._$p = props 23 | el._$c = children 24 | el._$r = wired._$r 25 | el._$u = wired.uninit 26 | wired.init && wired.init(props) 27 | props.oncreate && props.oncreate(el) 28 | }, 29 | onupdate: function (el) { 30 | el._$p = props 31 | el._$c = children 32 | el._$r() 33 | props.onupdate && props.onupdate(el) 34 | }, 35 | ondestroy: function (el) { 36 | el._$u && el._$u() 37 | }, 38 | onremove: function (el, done) { 39 | if (!props.onremove) return done() 40 | 41 | props.onremove(el, done) 42 | } 43 | }) 44 | } 45 | } 46 | 47 | --------------------------------------------------------------------------------