├── .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 |
140 | )
141 | )
142 |
143 | ```
144 |
145 | ### Special properties
146 |
147 | #### `key`
148 |
149 | They key property is set on the component's tag (not the top node of the component's `view`). This can be very important when you have components as siblings to eachother in the virtual dom.
150 |
151 | #### `class` & `id`
152 |
153 | These are set on the component's tag. Mainly useful for css purposes.
154 |
155 | #### `oncreate`, `onupdate`, `ondestroy`
156 |
157 | These lifecycle events are first use for managing the component, but afterward, will be called just as usual, for the component's node.
158 |
159 | ## Nestable component lifecycle
160 |
161 | ### The special action called `init`
162 |
163 | If you add an action called `init` to your component, this action will be called when the component is first rendered. It will be passed the component's props as its first argument.
164 |
165 | Such as if you want your `Counter` component to accept a starting value as a prop, you don't want the actual value to change when whatever was the basis for the starting value changes. So you could implement the counter this way:
166 |
167 | ```js
168 |
169 | const Counter = nestable(
170 |
171 | //INITIAL STATE
172 | { value: 0 },
173 |
174 | //ACTIONS
175 | {
176 | init: props => ({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 |
--------------------------------------------------------------------------------