/superhistory

A tiny history API with nested contexts.

Primary LanguageTypeScript

superhistory

A tiny history API with nested contexts.

This is being iterated on quite often, expect breaking changes for a while.

Example

Check out this example on flems

Quick Start

npm install superhistory@next
import superhistory from 'superhistory'

const A = superhistory()

A.go('/a/b/c/d')

window.location.pathname
// '/a/b/c/d'

A.get()
// { path: 'a/b/c/d' }

A.go('/a/b/c/d/e')
A.go('/a/b/c/d/e/f', { replace: true })
A.get()
// { path: '/a/b/c/d/e/f', localPath: '/a/b/c/d/e/f' }
A.back()
A.get()
// { path: '/a/b/c/d', localPath: '/a/b/c/d' }


const B = A.get('/a/b')
const C = B.get('/c')

B.get()
// { localPath: '/c/d', path: '/a/b/c/d' }

C.get()
// { localPath: '/d', path: '/a/b/c/d' }

C.go('/g')
;[C.get(), B.get(), A.get()]
// [
//     { localPath: '/g', path: '/a/b/c/g' },
//     { localPath: '/c/g', path: '/a/b/c/g' } ,
//     { localPath: '/a/b/c/g', path: '/a/b/c/g' }
// ]

C.preview('/h')
// 'a/b/c/h'
C.get()
// { localPath: '/a/b/c/g', path: '/a/b/c/g' }

API

superhistory(...)

superhistory(
    options?: { 
        _window?: Window, 
        onChange?: (state: superhistory.State) => void 
    }
): superhistory.Instance

Creates a superhistory instance. You can optionally pass in onChange to be notified any time a path is set, or popstate fires

The _window parameter there is available as an override for serverside usage or tests. For example you could use JSDOM.

instance.get()

type State = {
    path: string | undefined,
	localPath: string | undefined
}

instance.get(): State

Returns the instance's localPath in its local context and the global path. Returns undefined for both properties if a nested history is not compatible with the current browser location pathname.

instance.preview(path: string): string

Shows what the browser location path will be were you call instance.go(path).

Useful for generating href's for anchor tags.

instance.go(...)

instance.go(path: string, options?: { replace: boolean })

A simple wrapper around history.pushState / history.replaceState that works with nested contexts.

instance.back()

instance.back(): void

A simple wrapper around history.back()

instance.end()

instance.end(): void

For the root node, this removes the listener for the popstate event and clears the list of children

Removes a child instance and any descendents from the list of child nodes used for notifying onChange callbacks. This will allow it to be gc'd but will not prevent you from using a child node as a getter/setter if it is still in scope.

instance.prefix()

instance.prefix(): string

Returns the complete normalized prefix of the child instance (including all parent prefixes). For the sake of polymorphism, the root node returns '/' despite not having a prefix.

instance.child(...)

instance.child(options: { prefix: string, onChange?: (state: State) => void })

Create a nested istory context. All history operations will receive/return a local pathname that does not include the prefix that you supply.

These operations are applied directly to the history API by the child instance using the concatenated prefixes of all of its parents.

You can also optionally provide an onChange callback which will notify you when a route is set or on onpopstate.

Note there is no diffing for onChange callbacks, if any route instance updates all route instances get notified that the URL is being changed.

Utilities

superhistory.joinPath

superhistory.joinPath(a: string, b: string): string

The function used internally to combine any two paths.

superhistory.normalizePath

Advanced

superhistory.normalizePath(path:string): string

The function used internally to normalize the formatting of paths.

Extracting variables out of paths

This library isn't a router, its just a small wrapper around the browser history API that you could build a pattern matching router on top of.

If you're looking for a pattern matching router engine: Check out superouter

Path / Prefix normalization

While not strictly correct, superhistory removes trailing slashes as applications tend to treat /a/b/c and /a/b/c/ as equivalent. By normalizing paths in this way the internal logic becomes much simpler when gluing together prefixes on nested child nodes.

In addition to removing trailing slashes, superhistory also converts empty strings ('') into single slashes ('/'). This behaviour ensures all paths and prefixes start with a slash which again simplifies concatenation.