hickery
v0.26.3
Published
Composeable queries for hyperscript trees
Downloads
13
Readme
Hickery
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!
- Tachyons: Tiny Queries for styling your app. Think styled-components but better!
- Hyperscript: The default module, preconfigured for querying hyperscript
- Core: The base general purpose library
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.