Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gvergnaud/evolui
A tiny reactive user interface library, built on top of RxJs.
https://github.com/gvergnaud/evolui
observable reactive-programming rxjs ui virtual-dom
Last synced: 2 months ago
JSON representation
A tiny reactive user interface library, built on top of RxJs.
- Host: GitHub
- URL: https://github.com/gvergnaud/evolui
- Owner: gvergnaud
- License: mit
- Created: 2017-08-15T14:33:44.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2018-09-02T17:20:41.000Z (over 6 years ago)
- Last Synced: 2024-11-02T02:33:28.942Z (2 months ago)
- Topics: observable, reactive-programming, rxjs, ui, virtual-dom
- Language: JavaScript
- Homepage:
- Size: 1.19 MB
- Stars: 54
- Watchers: 6
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- jimsghstars - gvergnaud/evolui - A tiny reactive user interface library, built on top of RxJs. (JavaScript)
README
evolui
A tiny reactive user interface library.## Features
* **Async** — evolui magically understands `Observables` and `Promises`. Just put them where they need to be displayed and, when they update, your UI will be refreshed for you.
* **Virtual DOM** — evolui has a fast virtual DOM diffing algorithm and do the less work possible by only updating the closest node from the values that changed.
* **Components** — You can build large applications by splitting its complexity inside encapsulated and predictable components.
* **Tiny** — The API surface is very small and the whole library is only `4kB` gziped.## Install
```
npm install evolui rxjs
```## Examples
* All examples [Demo](https://7yv1494p9x.codesandbox.io/) — [see code](https://codesandbox.io/s/github/gvergnaud/evolui/tree/master/example)
* Simple Animation [Demo](https://72wkn61x21.codesandbox.io/) — [see code](https://codesandbox.io/s/72wkn61x21)
* Complex Animation [Demo](https://31z431n4m.codesandbox.io/) — [see code](https://codesandbox.io/s/31z431n4m)
* Animated Pinterest Like Grid [Demo](https://wqyl0xmo47.codesandbox.io/) — [see code](https://codesandbox.io/s/wqyl0xmo47)**To jump to the code, visite the [`example`](https://github.com/gvergnaud/evolui/tree/master/example) folder.**
## Getting Started
### Promises
```js
import html, { render } from 'evolui'
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))const promise = delay(1000).then(() => 'World!')
render(
html`
Hello, ${promise}
`,
document.querySelector('#mount')
)
```![Promise demo](https://github.com/gvergnaud/evolui/blob/media/gifs/evolui-1.gif?raw=true)
### Observables
```js
import html, { render } from 'evolui'
import { interval } from 'rxjs'
import { take, map } from 'rxjs/operators'const text$ = interval(1000).pipe(
take(4),
map(index => ['.', '..', '...', 'World!'][index])
)render(
html`
Hello, ${text$}
`,
document.querySelector('#mount')
)
```![Observable demo](https://github.com/gvergnaud/evolui/blob/media/gifs/evolui-2.gif?raw=true)
## Simple App
```js
import html, { render } from 'evolui'
import { createState } from 'evolui/extra'const Counter = () => {
const state = createState({ count: 0 })return html`
count: ${state.count}
state.count.set(c => c - 1)}>-
state.count.set(c => c + 1)}>+
`
}render(html`<${Counter} />`, document.querySelector('#mount'))
```## Concept
The main goal of evolui is to make dealing with observables as easy as dealing with regular values.
Observables are a great way to represent values that change over time. The hard part though is combining them. This is where evolui comes in handy. It understands **any** combination of `Array`s, `Promise`s and `Observable`s, so you never have to worry about the way you should combine them before putting them inside your template.
```js
import html from 'evolui'
import { from } from 'rxjs'
import { startWith } from 'rxjs/operators'const getCharacterName = id =>
fetch(`https://swapi.co/api/people/${id}`)
.then(res => res.json())
.then(character => character.name)html`
${'' /* this will return an array of observables. */}
${'' /* Don't panic! evolui understands that as well */}
${[1, 2, 3].map(
id => html`
${from(getCharacterName(id)).pipe(startWith('Loading...'))}
`
)}
`
```![list demo](https://github.com/gvergnaud/evolui/blob/media/gifs/evolui-3.gif?raw=true)
## Components
Evolui lets you organize your code in components.
Components are defined as a simple function of `Observable Props -> Observable VirtualDOM`:
```js
import html, { render } from 'evolui'
import { createState } from 'evolui/extra'
import { map } from 'rxjs/operators'const Button = props$ =>
props$.pipe(
map(
({ text, onClick }) => html`
${text}
`
)
)const App = () => {
const state = createState({ count: 0 })return html`
<${Button}
text="-"
onClick=${() => state.count.set(c => c - 1)}
/>count: ${state.count}
<${Button}
text="+"
onClick=${() => state.count.set(c => c + 1)}
/>
`
}render(html`<${App} />`, document.querySelector('#mount'))
```### children
Components can have children 👍
```js
import html, { render } from 'evolui'
import { map } from 'rxjs/operators'const CrazyLayout = map(({ children }) => html`
${children}`)render(
html`
<${CrazyLayout}>
I'm the content
${CrazyLayout}>
`,
document.querySelector('#mount')
)
```## Extra
### Animations
`evolui/extra` exports a **spring** animation helper called `ease`.
```typescript
ease: (stiffness: number, damping: number, id: string?) => Observable => Observable
```You just have to pipe any of your observables to `ease(, )` to make it animated.
If you are interested in using this feature separately, check out [`rx-ease`](https://github.com/gvergnaud/rx-ease)
```js
import html, { render } from 'evolui'
import { ease } from 'evolui/extra'
import { fromEvent } from 'rxjs'
import { map, startWith } from 'rxjs/operators'const stiffness = 120
const damping = 20const style$ = fromEvent(window, 'click').pipe(
map(() => ({ x: e.clientX, y: e.clientY })),
startWith({ x: 0, y: 0 }),
ease({
x: [stiffness, damping],
y: [stiffness, damping],
}),
map({ x, y }) => ({
transform: `translate(${x}px,${y}px)`
})
)render(
html`
`,
document.querySelector('#mount')
)
```![animation demo](https://raw.githubusercontent.com/gvergnaud/evolui/c445de8161c151c24d84d0ad61af0a6185f0d62d/dot-animation.gif)
For single values, you can pass the `stiffness` and `damping` directly
```js
import html, { render } from 'evolui'
import { ease } from 'evolui/extra'
import { interval } from 'rxjs'
import { map } from 'rxjs/operators'const width$ = interval(1000).pipe(
map(i => i * 50),
ease(120, 20)
)render(
html`
`,
document.querySelector('#mount')
)
```## API
#### text :: TemplateLiteral -> Observable String
```js
import { text } from 'evolui'const style$ = text`
position: absolute;
transform: translate(${x$}px, ${y$}px);
`
```#### html :: TemplateLiteral -> Observable VirtualDOM
```js
import html from 'evolui'const App = () => html`
`
```#### render :: Observable VirtualDOM -> DOMNode -> ()
```js
import { render } from 'evolui'render(html`<${App} />`, document.querySelector('#mount'))
```#### ease :: (Number, Number) -> Observable Number -> Observable Number
```js
import { ease } from 'evolui/extra'
import { interval } from 'rxjs'interval(1000).pipe(
ease(120, 20),
subscribe(x => console.log(x)) // every values will be interpolated
)
```#### createState :: Object -> State
Create an object of mutable reactive values.
Each key on your initial state will be transformed into a stream, with a special `set` method on it.
`set` can take either a value or a mapper function.```js
import html, { render } from 'evolui'
import { createState } from 'evolui/extra'const state = createState({ count: 0 })
console.log(state.count)
// => Observable.of(0)const reset = () => state.count.set(0)
const add1 = () => state.count.set(c => c + 1)render(
html`
count: ${state.count}
reset
+
`,
document.querySelector('#mount')
)
```#### all :: [Observable a] -> Observable [a]
```js
import { all } from 'evolui/extra'const z$ = all([x$, y$]).map(([x, y]) => x + y)
```### Lifecycle
* **mount** — after the element as been rendered
* **update** — after the dom element as been update
* **unmount** — before the dom element is removed from the dom```js
html`
`
```## Contributing
If you find this interesting and you want to contribute, don't hesitate to open an issue or to reach me out on twitter [@GabrielVergnaud](https://twitter.com/GabrielVergnaud)!