Skip to content

Commit 1e1e5ef

Browse files
committed
adding readme
1 parent 81fb505 commit 1e1e5ef

File tree

4 files changed

+148
-12
lines changed

4 files changed

+148
-12
lines changed

README.md

Lines changed: 142 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ citizens. The main features are:
1313
- It's tiny, only about 5 kB minified (and 1.9 kB gzipped).
1414
- It uses Proxy under the hood for a little :sparkler:
1515
- It is meant for custom-elements, so you can use it with Vanilla-JS or any framework like [lit-element](https://www.npmjs.com/package/lit-element), [Brick](https://www.npmjs.com/package/brick-element).
16-
- Implements the usual flow: ACTION->REDUCER->STORE but with A LOOOOT less painfull sintax.
16+
- Implements the usual flow: **ACTION**->**REDUCER**->**STORE** but with A LOOOOT less painfull sintax.
1717
- You can break the STORE in parts as small as you like.
1818
- Works with async out of the box.
1919
- Saves the state to ``localStorage`` automatically.
2020

21+
If you are not familiar with the ACTION->REDUCER->STORE pattern and why is a good idea, have a look at the [Redux Docs](https://redux.js.org/introduction/core-concepts) where it is very well explained.
22+
23+
2124
# Demo
2225

2326
[Simple todo App demo](https://webcomponenthelpers.github.io/ImperaJS/demo/litDemo.html) made with **ImperaJs** and **Lit-Element**.
@@ -37,25 +40,156 @@ while a transition from one app state to another is implemented by **State-Trans
3740
State-Variables and Transitions can be hooked to custom-elements, so that on StateVarible change, or on dispatch of a Transition, the custom-element
3841
can apply its own UI-related changes.
3942

43+
### Install
44+
45+
```bash
46+
npm i impera-js
47+
```
48+
4049
### StateVariables
50+
A StateVariable hold the state of the App, its content can be a String, Object, Number and Boolean. Its **DEFAULT** value is passed at creation time and defines the type of the variable, the type cannot be changed later. A StateVariable is automatically stored in **localStorage**, if a value already exist it is automatically loaded. You can have a look at [a more complete example here](https://github.com/WebComponentHelpers/ImperaJS/blob/master/demo/Store.js).
4151

52+
```js
53+
import {StateVariable} from 'impera-js'
54+
55+
// Initializzation to an empty list
56+
var todos = new StateVariable("todos",[]);
57+
58+
// Attach/Detach watchers
59+
// Target is a custom-element and the callback is a function
60+
// that modify its UI (it will work with any object really)
61+
todos.attachWatcher( target:HTMLElement, callback:Function )
62+
todos.detachWatcher( target:HTMLElement )
63+
64+
// modifying the state is easy,
65+
// this will fire the watchers callbacks
66+
todos.value.push({txt:"first todo", isComplete:false})
67+
68+
// Note that **value** returns a proxy!
69+
// this will also fire the watchers callbacks
70+
let myProxy = todos.value[0]
71+
myProxy.txt = "modified todo"
72+
73+
```
74+
The property **value** of a stateVariable returns a proxy to the content of the stateVariable, whenever it is set (directly or indirectly using Array.push for example) will run the callback for all watchers.
75+
76+
77+
### StateTransitions
78+
Transitions must be **Pure Functions**, they only compute a final state, they are defined by initial state and input data only, they reproduce always the same result for same inputs.
4279

4380
```js
44-
// init
81+
82+
// Adding a Transition to a StateVariable
83+
todos.addTransition("addTodo",(text)=>{
84+
let todo = {txt : text, isComplete : false}
85+
todos.value.push(todo)
86+
})
87+
88+
89+
// State change via transition
90+
// this will fire the watchers callbacks
91+
todos.applyTransition("addTodo", "new Todo")
92+
93+
94+
// Note that now this will throw, this is to make
95+
// sure one can only change state by defined transitions
96+
todos.value.push(some_todo)
97+
98+
// this protection can be overridden
99+
todos.allowStandaloneAssign = true
100+
todos.value.push(some_todo) // now will not throw
101+
45102
```
103+
The idea here is that once you add a transition to a stateVariable you limit its allowed chage space, the framework makes sure that now you are only allowed to change the stateVariable via transitions. You can of course override this behaviour.
104+
Note that transition owned by a state variable will bind **this** to the state variable itself, however make sure to use a named function then and NOT an arrow function as in the example.
46105

47106
```js
48-
// note is a proxy
49-
let myProxy = var.value.whatever
50-
myProxy = 9 // this change the variable and run side-effects
107+
import {StateTransition} from 'impera-js'
108+
109+
// Global transitions definition
110+
var removeTodo = new StateTransition("removeTodo",(index)=>{
111+
todos.value.splice(index,1)
112+
113+
// any other stateVariables change can go below here
114+
// ....
115+
});
116+
117+
// Dispatch the transition, andcall watchers callbacks.
118+
// Whatcher can be attached in same way as for stateVariables
119+
removeTodo.applyTransition( 1 )
120+
51121
```
122+
A global stateTransition is a global function that is meant to apply simultaneously an overall state change, this can be made of just one variable change or multiple stateVariables changes at the same time, so that the initial and final states are always well defined, it guarantees that UI updates are made at transition completion (final state) only.
123+
124+
### StateMixin
125+
The StateMixins are a way to attach custom-element callbacks to a stateVariable or a stateTransition in an easy way. The callbacks get attached and detached automatically when the custom-element is connected/disconnected from the DOM.
52126

53127
```js
54-
// Attach/Detach watchers
128+
import {statesMixin} from 'impera-js'
129+
130+
// Mixin applied to generic custom-element
131+
class myTodo extends statesMixin([todos,removeTodo], HTMLElement){
132+
constructor(){
133+
super()
134+
135+
// the element has a read-only prop connected
136+
// to each stateVariable in the list above
137+
let myTodos = this.todos[0]
138+
// this will throw
139+
myTodos.txt = "new todo"
140+
}
141+
142+
// override callback that fires on "todos" changes
143+
on_todos_update(){
144+
// do something to update the UI
145+
}
146+
147+
// override callback that fires on transition "removeTodo"
148+
on_removeTodo(){
149+
// do something here
150+
}
151+
152+
onclick(){
153+
// the element now has a hook to all transition of
154+
// states in the list.
155+
this.applyTransition("addTodo", "new todo")
156+
this.applyTransition("removeTodo", 1 )
157+
}
158+
}
159+
55160
```
161+
For any **stateVariables** in the list a read-only property named as the stateVariable will be added to the element. Also an **applyTransition** method to dispatch the added transitions (either of a stateVariable or of a global stateTransition) will be added. Callbacks to react on stateVariable change needs to be overwritten by the user and have a predefiend naming scheme: **on_"stateVarName"\_update**. Callbacks to react to transitions are instead called **on_"stateTransitionName"**, in the latter case also the transition input data are passed.
56162

57-
### StateTransitions
163+
### Usage with Lit-Element
58164

165+
The usage with Lit-Element is very similar to what shown above, with the exception that
166+
each update of any stateVariable or dispatch of Transition will request a render of the element. You can have a look at [a more complete example here](https://github.com/WebComponentHelpers/ImperaJS/blob/master/demo/litWebComponents.js), while a demo can be found [here](https://webcomponenthelpers.github.io/ImperaJS/demo/litDemo.html).
59167

60-
### StateMixin
168+
```js
169+
import {litStatesMixin} from 'impera-js'
170+
171+
class myTodo extends litStatesMixin([todos,removeTodo],LitElement){
172+
173+
static get properties() { return { index: Number } }
174+
175+
render(){
176+
return html`
177+
<span @click="${this.toggle}">
178+
${this.todos[this.index].isComplete === "true" ? "Done" : "Pending"}
179+
</span>
180+
<span class="text">${this.todos[this.index].txt}</span>
181+
<a class="tag is-delete is-light" @click="${this.remove}"></a>
182+
</div>
183+
`;
184+
}
185+
toggle(){
186+
this.applyTransition("toggleTodo",this.index);
187+
}
188+
remove(){
189+
this.applyTransition('removeTodo',this.index)
190+
}
191+
}
192+
193+
customElements.define("my-todo",myTodo);
61194

195+
```

build/stateElement.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ let baseMixin = (listOfComponents, baseClass) => class extends baseClass {
299299
* @param listOfComponents is a list of StateVariables and StateTransition to add to the web-component
300300
* @param baseClass The class on which the mixin is applied
301301
*/
302-
export let statesMixin = (listOfComponents, baseClass) => class extends baseMixin(listOfComponents, baseClass) {
302+
export var statesMixin = (listOfComponents, baseClass) => class extends baseMixin(listOfComponents, baseClass) {
303303
connectedCallback() {
304304
if (super['connectedCallback'] !== undefined) {
305305
super.connectedCallback();

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "impera-js",
33
"version": "0.0.0",
44
"sideEffects": false,
5-
"description": "Proxy based state managment and data bindings for web-components",
5+
"description": "Tiny, Proxy based App State Managment for custom-elements",
66
"main": "build/impera.js",
77
"types": "src/impera.ts",
88
"scripts": {
@@ -20,7 +20,9 @@
2020
"state",
2121
"app-state",
2222
"proxy",
23-
"managment"
23+
"managment",
24+
"lit-element",
25+
"custom-element"
2426
],
2527
"author": "panmanfredini",
2628
"license": "MIT",

src/stateElement.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ let baseMixin = (listOfComponents:Array<StateVariable|StateTransition|Message>,
355355
* @param listOfComponents is a list of StateVariables and StateTransition to add to the web-component
356356
* @param baseClass The class on which the mixin is applied
357357
*/
358-
export let statesMixin = (listOfComponents:Array<StateVariable|StateTransition|Message>, baseClass:any) => class extends baseMixin(listOfComponents, baseClass) {
358+
export var statesMixin = (listOfComponents:Array<StateVariable|StateTransition|Message>, baseClass:any) => class extends baseMixin(listOfComponents, baseClass) {
359359

360360
connectedCallback(){
361361
if(super['connectedCallback'] !== undefined) {

0 commit comments

Comments
 (0)