Skip to content

KidkArolis/moonwave

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

18 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸŒ— Moonwave

Moonwave is a small framework for building web applications with JavaScript.

Moonwave is 100 lines of code, because it combines an existing set of composable libraries.

Each of these libraries is independent and can be useful on their own. Moonwave is just one, simple way to combine the three for common cases.

If Moonwave doesn't quite fit your needs as your application grows, you can combine the three libraries, or others, in your own way and use Moonwave as a reference implementation.

Usage

yarn add moonwave

Example

const Preact = require('preact')
const { moonwave } = require('moonwave/preact')

// each route maps to a Preact component
const routes = [
  ['/', MainView]
]

// actions is how we update application state
const actions = {
  update: (get, split, title) => {
    split({ title })
  }
}

// evolve is a flexible hook for implementing custom
// action strategies, such as modularising or namespacing
// this implementation calls the actions we declared above
const evolve = (get, split, action) => {
  actions[action.type](get, split, action.payload)
}

// the preact component receives full application state
// and a `split` function for dispatching actions or updates
// use ConnectAtom component to map state/split to props
function MainView ({ state, split }) {
  return (
    <div>
      <h1>Title: {state.title}</h1>
      <input type='text' value={state.title} onInput={update} />
    </div>
  )

  function update (e) {
    split('update', e.target.value)
  }
}

// assemble all the pieces
moonwave()
  .state({ title: 'Default title' })
  .evolve(evolve)
  .routes(routes)
  .mount(document.body)

This example application uses preact and will compile to around 6KB. The individual components are around:

preact        3.5KB
tiny-atom     0.5KB
space-router  1.5KB
example-app   0.5KB
-------------------
              6.0KB

API

const app = moonwave(options)

Create an app. Available options are:

  • store.merge - custom state merge strategy, default implementation is (state, update) => Object.assign({}, state, update).
  • store.debug - a debug hook. Set to debug: require('moonwave/log') for console logger or debug: require('moonwave/devtools') for integration with Redux dev tools.
  • router.mode - one of history, hash, memory. Default is history.
  • router.interceptLinks - whether clicks on links are automatically handled by the router. Default is true.
  • router.qs - custom query string parser. Object of shape { parse, stringify }.

See tiny-atom docs for more information on the store. See space-router docs for more information on the router.

app.state()

Provide initial state.

app.state({})
app.state({ count: 0 })
app.state(Immutable.Map({})) // note: in this case, you'll need to provide a custom store.merge function

app.evolve()

Provide a function of signature (get, split, action) that will receive all actions that were split by the app.

Note: think of split as dispatch if you're familiar with that.

app.evolve((get, split, action) => {
  actions[action.type](get, split, action.payload)
})

app.evolve(async (get, split, action) => {
  switch (action.type) {
    case 'increment':
      split({ count: get().count + 1 })
      break
    case 'decrement':
      split({ count: get().count - 1 })
      break
    case 'fetch':
      split({ loading: true })
      const res = await axios.get('/data')
      split({ items: res.data, loading: false })
      break;
  }
})

app.actions()

Provide a map of actions. Can be used instead of the evolve for common cases.

app.actions({
  increment: (get, split, x) => {
    split({ count: get().count + x })
  },
  decrement: (get, split, x) => {
    split({ count: get().count - x })
  }
})

If both app.actions() and app.evolve() is used, the actions provided is passed as the 4th argument to the evolve function.

app.routes()

Provide an array of routes. An optional second argument can be used for a custom onTransition implementation.

// if you don't need routing
app.routes([
  ['*', SinglePage]
])

// common case
app.routes([
  ['/', Home],
  ['/Space', Space],
  ['/Ocean', Ocean]
])

// nested routes
app.routes([
  ['', Shell, [
    ['/inbox', Inbox],
    ['/account', Account, [
      ['password', Password]
    ]],
    ['*', NotFound]
  ]]
])

// async route loading
app.routes([
  ['/', { load: () => System.import('./pages/Index') }],
  ['/Space', { load: () => System.import('./pages/Space') }]
], async function onTransition (route, data) {
  await Promise.all(data.map(async d => {
    if (!d.Component) d.Component = await d.load()
  }))
  atom.split({ route })
})

To navigate around your application programmatically, you split an action like so:

function MyApp ({ split }) {
  return <form onSubmit={onSubmit}>...</form>

  function onSubmit () {
    const id = 1
    split('navigate', {
      // path
      path: `/space/${id}`,
      // query params
      query: { angle: 0 },
      // push vs replace the url
      replace: false
    })
  }
}

app.mount()

Mount the app to a DOM element. Initialise the store, router and render the app into DOM. Defaults to document.body

app.mount(document.getElementById('root'))

app.unmount()

Stop the router, unrender the app from DOM.

app.unmount()

About

πŸŒ— A small web application framework.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published