Skip to content

Instantly share code, notes, and snippets.

@barneycarroll
Last active December 22, 2016 20:43
Show Gist options
  • Save barneycarroll/6978c0d94f4f0a3aa675ac4070f1468f to your computer and use it in GitHub Desktop.
Save barneycarroll/6978c0d94f4f0a3aa675ac4070f1468f to your computer and use it in GitHub Desktop.
Mithril wormhole: a component whose content is appended to the body instead of rendering in place
import m from 'mithril'
// This dependency resides here:
// https://gist.github.com/barneycarroll/4144b93b6221f419bf7dcb5e847f8a22
import events from 'mithril-events'
// A hash of registered wormholes with values indicating status
const cache = Object.create( null )
// At the beginning of every draw, set the value of each to false,
// to indicate it hasn't registered this draw
events.redraw.add( () => {
for( let key in cache )
cache[ key ].registered = false
}
// Then after draw...
events.config.add( () => {
for( let key in cache ){
const wormhole = cache[ key ]
// If it hasn't re-registered
if( !wormhole.registered ){
// Remove its root from the DOM
document.body.removeChild( wormhole.root )
// And the entry from the cache
delete cache[ key ]
}
} )
// Export is a component: interface is easy & idiomatic.
export default {
view( ctrl, { key = '' }, content ){
let wormhole = cache[ key ]
let fresh = !wormhole
// If the key isn't in the cache, create a new wormhole entry.
if( fresh )
wormhole = cache[ key ] = {
// We need a root to render the content in
root : document.createElement( 'div' )
}
// Render the content.
m.render( wormhole.root, content )
if( fresh )
document.body.appendChild( wormhole.root )
// Record registration to avoid teardown
wormhole.registered = true
}
}
@barneycarroll
Copy link
Author

Useful in the case of fixed position elements like tooltips, modals, etc which need to be rendered in a place (at the end of the document) other than where they are logically defined. Currently these tooltips must be stateless. I'm working on solving that.

import m        from 'mithril'
import wormhole from 'mithril-wormhole'

const style = ( {
  top, right, bottom, left
} ) => ( {
  position  : 'fixed',
  top       : ( ( top + bottom ) / 2 ) + 'px'
  left      : ( ( left + right ) / 2 ) + 'px',
  transform : 'translate( -50%, -50% )',
  background: 'white',
  padding   : '1em',
  boxShadow : '0 0 1em black'
} )

const tooltip = {
  view : ( x, { ctrl }, contents ) =>
    m( '.tooltip', {
      style : style( ctrl.position ),
      config( el, init, ctxt ){
        if( !init )
          return

        document.addEventListener( 'click', function teardown( e ){
          if( e.target != el )
            return

          delete ctrl[ key ]

          e.preventDefault()

          document.removeEventListener( 'click', teardown, true )
        }, true )
      }                  
    },

    longText
  )
}

export default {
  view : ( ctrl, { list } ) =>
    m( '.list[style="overflow-y:auto;" ]',
      list.map( ( {
        key, shortText, longText
      } ) =>
        m( '.item', {
          key     : item.key,
          onclick( el ){
            ctrl[ key ] = true

            ctrl.position = el.getBoundingClientRect()
          }
        },
          shortText,
            ctrl[ key ]
          ? m( wormhole, { key },
              m( tooltip, { ctrl }, longText )
            )
          : ''
        )
      )
    )
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment