Nimble Pirate Monitor

    hickery

    0.26.3 • Public • Published

    Hickery

    pipeline status coverage report

    But by and by pap got too handy with his hick 'ry, and I couldn't stand it.

    Scalable data manipulation with a special focus on Virtual DOM trees.

    A Lens-like Library with a convenient interface.
    • Very Composeable
    • VDOM library neutral
    • Query any structure, built in utils for frontend.
    • fast
    • <3kb
    • Client + Server Compatible (no tricks!)
    • Built for testing, decorating, theming and state management!

    Quick Start

    // Works with any hyperscript query library (see configs)
    const $ = require('hickery')
     
    // A given hyperscript library (could be JSX too)
    const m = require('mithril')
     
    const tree = () =>
        m( 'div'
            , m('ul'
                ,m('li', 1)
                ,m('li', 2)
                ,m('li', 3)
            )
        )
     
     
    const query = f =>
        $.compose( 
            $.child.first
            , $.child.all
            , $.className 
        )
     
    query ( x => x + ' red') (tree)
     
    /* Returns:
    *
    * m('div'
    *    ,m('ul
    *        ,m('li.red', 1)
    *        ,m('li.red', 2)
    *        ,m('li.red', 3)
    *    )
    * )
    *
    */

    Modules

    Hickery is a general purpose idea. It's easy to use them to work with any data format. But hickery comes with some modules preconfigured for certain use cases. If there's a module you'd like to see here, please submit a pull request!

    Why would I use hickery queries?

    Let's say you are using an autocomplete library like manuel.

    It offers oodles of configuration, but despite all that, you find you still need to update a classname in a place that would require recreating business logic to override.

    This library let's you define queries that allow you to make that transform. And you can decorate the original component so the rest of your codebase doesn't need to know about the changes you've made.

    // queries
    const $div = $.root
    const $ul = $div($.child.nth(1))
    const $li = $ul($.child.all)
    const $input = $div($.child.first)
     
    // action
    const addRedToListItems =
        $li(
            $.className(
                $.class.add('red')
            )
         )
    // action
    const increaseInputFontSize =
        $input(
            $.style( $.merge({ fontSize: '1.1em' }) )
        )
     
    // a decorated component, it will be styled after the original component runs
    const decorated =
        $.compose(
            increaseInputFontSize,
            addRedToListItems,
            autocomplete
        )
     
    // in your view, call it as normal
    decorated(normalLibraryOptions)

    The one smell in all of this is that you need to know the particular HTML structure of the library you are using. But this is where I'd like to encourage library others to export queries to points of interest in their components.

    The dream is, instead of providing 100's of overrides and hooks, the library can provide you with a composeable interface to add styles, attributes and event listeners without needing to open any issues or pull requests.

    Of course it's early days, but I'm planning to do exactly that with my libraries. And in doing so I'll be able to delete a lot of hooks that guess at my user's requirements.

    You don't need to import Hickery to do that. It's just a particular pattern you can use when providing a hook.

    const aQuery = visitor = context => {
     
        // do something with visitor on context
        visitor(context)
     
        // return an object with the same structure as the original context
        return context
    }

    If you follow this pattern, your queries will compose with other queries. And we can build a standard library for composeable traversals of nested data. Meanwhile your users are handed a query to a particular focal point in your DOM heirarchy and their transformations are decoupled from library internals.

    API

    TODO UNSTABLE

    • pathOr
    • path
    • Query
    • QueryOr
    • PureQuery
    • PureQueryOr
    • compose
    • merge
    • class.add
    • class.remove
    • child.nth
    • child.all
    • child.first
    • child.last
    • style
    • root
    • attrs
    • children
    • tag
    • props
    • events
    • className
    • select
    • set
    • lens.view
    • lens.set
    • lens.over

    Examples

    TODO UNSTABLE

    Supporting Hickery Queries for your Component Library

    Hickery Queries are more a pattern than a library. In the same that node style callbacks allowed a lot of libraries to write interoperable async code prior to the introduction of promises - Hickery is a pattern that allows users of your library to safely transform output.

    You can support Hickery Queries without importing Hickery. Let's say for example, you want to provide the user of your library the ability to query an input that is nested within a larger structure. You don't want them to hard code against that structure, but you also don't want to assume their intentions and give them an overly specific hook.

    This is how you could do that in 100% Vanilla JS

    export $input = visitor => rootNode => {
     
        const inputReference =
            // manually grab it wherever it lives
            rootNode.children[0].children[0]
     
        visitor(n)
     
        return rootNode
    }

    You could export this as part of your library, and you can ignore the naming conventions hickery uses.

    E.g. you might export $input as:

    export default {
        someComponent
        ,queries: {
            input: $input
        }
    }

    Or

    export default {
        someComponent
        ,tranformInputNode: $input
    }

    Now users of your library could use this function directly to define hooks that are relevant to them.

    import { transformInputNode, someComponent } from 'wherever'
     
     
    const addOnFocusOnBlurHooks =
        transformInputNode( input => Object.assign(input.attrs,{ onfocus, onblur }) )
     
    addOnFocusOnBlurHooks( someComponent({ /* normal component options */ }) )

    But because you followed this standard query format, they could import hickery and access a suite of utilities for transforming virtual dom trees.

    const { transformInputNode, someComponent } = require('wherever')
    const $ = require('hickery')
     
    const addOnFocusOnBlurHooks =
        transformInputNode(
            // vdom library neutral transform
            $.events( o => ({...o, onfocus, onblur))
        )
     
    const newComponent =
        $.compose(
            addOnFocusOnBlurHooks
            ,someComponent
        )
     
    // decorated component
    newComponent(/* normal component options */)

    Acknowledgements and Prior Art

    Thanks to Keith Cirkel for helping me set up the automated NPM deployment.

    Thanks to all the thinkers who in aggregate invented/uncovered functional references. Including Jeremy Gibbons, Bruno C. D. S Oliviera, Luke Palmer, Twan van Laarhoven, Russel O'Connor and Edward Knett. I don't know enough of the history (yet) but I appreciate all the deep thought and hard work required.

    Special thanks to Hardy Jones, Scott Christopher, Johnny Hauser, Vesa Karvonen and Brad Compton for helping me to understand Lenses. Hanging out in the Ramda chatroom is good for the soul.

    Special thanks to Barney Carroll for all the design discussions and the many insights you provided. Patchinko was a huge inspiration for this library's interpretation of lenses.

    Keywords

    none

    Install

    npm i hickery

    DownloadsWeekly Downloads

    9

    Version

    0.26.3

    License

    MIT

    Unpacked Size

    45.7 kB

    Total Files

    12

    Last publish

    Collaborators

    • jaforbes