Skip to content

A collection of tiny, lightweight, client-side web libraries with a minimal API. Single file components without transpiling.

License

Notifications You must be signed in to change notification settings

simplygreatwork/heroic

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

About

A collection of tiny, lightweight, client-side web libraries with a minimal API. Single file components without transpiling. The examples are hosted on GitHub.

Story

Create an html page "index.html"

<html></html>

Add a component.

<div name="child" data-component="./child.html"></div>

When "index.html" loads, "./child.html" inserts, and Component.start runs.

Component.start(({ component }) => { ... })

Redirect the child component to show other content.

Component.start(({ component }) => {
	const data = {}
	component.child('child').redirect('./child.html', data)
})

Routing

Watch a url endpoint.

router.register('products', {
	enter: () => { ... }
})

Redirect a child component.

router.register('products', {
	enter: () => child.redirect('./products.html')
})

Inject data from the router's url into the component.

router.register('products', {
	enter: (data) => child.redirect('./products.html', data)
})

The router provides path, last path part, index of the last path part, and matched values.

router.register('products', {
	enter: ({ path, part, index, values }) => child.redirect('./products.html', { path, part, index, values })
})

After "products.html" loads, watch another endpoint: "products/:id".

router.register('products/:id', {
	enter: () => { ... }
})

A path part beginning with a colon represents a variable; e.g. ":product_id"

router.register('products/:product_id', {
	enter: ({ values }) => {
		const product_id = values.product_id
	}
})

You can pass these values, which contain "product_id", along to the next component, "product.html"

router.register('products/:product_id', {
	enter: ({ values, then }) => child.redirect('./product.html', { values }, then )
})

Register "enter" and "exit" handlers. When the url changes and a path part appears, it enters. When a path part leaves, it exits.

router.register('products', {
	enter: ({ then }) => child.redirect('./products.html', {}, then ),
	exit: ({ then }) => child.redirect('./empty.html', {}, then )
})

Imagine, a url which changes from "1/2/3" to "1/b/c". "3" exits. "2" exits. "b" enters. "c" enters.

Handlers are called only when a change to each url path part is detected.

router.register('products', {
	enter: ({ then }) => child.redirect('./products.html', {}, then ),
	exit: ({ then }) => child.redirect('./empty.html', {}, then )
})
router.register('products/:product_id', {
	enter: ({ then }) => child.redirect('./product.html', {}, then ),
	exit: ({ then }) => child.redirect('./empty.html', {}, then )
})
  • When the url changes from "" to "products", "products" will enter.
  • When the url changes from "products" to "products/1", "products/:product_id" will enter.
  • When the url changes from "products/1" to "products", "products/:product_id" will exit.
  • When the url changes from "products" to "", "products" will exit.

Components

Components are typically created inside html markup.

<div name="name" data-component="./child.html"></div>

Data is passed into a child component when it's been redirected or cloned.

Component.ready(({ component, data }) => {
	const item = data.item
})

The function '$' returns a single element inside the component which matches the selector.

Component.ready(({ component, $ }) => {
	const selector = 'button'
	const button = $(selector)
})

When no arguments are provided, the function '$' returns every element inside the component.

Component.ready(({ component, $ }) => {
	const elements = $()
	const header = elements.header
	const button = elements.button[1]
})

Find a component's child by its name.

Component.ready(({ component }) => {
	const child = component.child('name')
})

Find a component's child by its index.

Component.ready(({ component }) => {
	const child = component.child(0)
})

Redirect a child component.

Component.ready(({ component }) => {
	const child = component.child('name')
	child.redirect('./another.html')
})

Clone a component.

Component.ready(({ component }) => {
	const data = {}
	const template = component.child('template')
	const clone = template.clone(data)
})

When you clone a component, it's inserted in the DOM directly above the subject. Cloning is mostly used for creating lists of items.

Cloud

Create a cloud and share it.

const cloud = Cloud()

Service providers subscribe and listen to the cloud.

const { on_set, on_get } = cloud
on_set((key, value) => { ... })
on_get((key) => { ... })

Send a value to the cloud.

const { set } = cloud
set('key', 'value')

Watch for a value to change.

const { on_change } = cloud
on_change('key', (key, value, old) => { ... })

Interact with data in the cloud. Announce changes and requests. Providers listen to changes and provide responses.

Bind

Scope

Clouds have a scope.

const scope = cloud.scope

Register functions to dispose of later when you're finished with a scope.

let off
scope.plug(off = register_an_event_listener())
scope.plug(off = register_another_event_listener())

When you're finished with a scope, unplug. The handlers will be disconnected and disposed.

scope.unplug()

This scope is unplugged and disposed when the router path part exits.

router.register(`realm`, {
	exit: () => scope.unplug()
})

About

A collection of tiny, lightweight, client-side web libraries with a minimal API. Single file components without transpiling.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages