Last active
July 7, 2022 14:59
-
-
Save barneycarroll/e11e05e91e0fb8d08ae569a897f4fe4c to your computer and use it in GitHub Desktop.
A component that consumes a populated CSS grid container and applies column styles according to bin packing optimisation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import m from 'mithril' | |
import O from 'mergerino' | |
import {viewOf} from 'mithril-machine-tools' | |
export default Simple | |
export function Simple(){ | |
let size = 0 | |
let column = 0 | |
let slot | |
let display | |
return { | |
view: v => { | |
const it = viewOf(v)() | |
if(!slot){ | |
display = it.attrs?.style?.display | |
it.attrs = O(it.attrs, {style: {display: 'none'}}) | |
} | |
return it | |
}, | |
oncreate({dom}){ | |
const frag = document.createDocumentFragment() | |
m.render(frag, Inert()) | |
slot = document.body.appendChild(frag.firstChild) | |
process(...arguments) | |
dom.style.display = display | |
}, | |
onupdate: process, | |
} | |
function process(v){ | |
pure_render( | |
slot, | |
viewOf(v)().children, | |
m.redraw, | |
) | |
const edge = slot.lastElementChild.getBoundingClientRect().right | |
if(size != edge){ | |
size = edge | |
for(const {offsetWidth} of slot.children) | |
if(offsetWidth > column) | |
column = offsetWidth | |
} | |
v.dom.style.gridTemplateColumns = `repeat(auto-fill, minmax(${ column }px, 1fr))` | |
} | |
} | |
export function Complex(){ | |
return { | |
view: () => | |
Slot(), | |
oncreate: process, | |
onupdate: process, | |
} | |
function process(v){ | |
let lab | |
let grid | |
let slot | |
pure_render( | |
v.dom, | |
[ | |
[lab, grid] = Lab(v), | |
slot = Slot(), | |
], | |
m.redraw, | |
) | |
const columns = columnise({ | |
grid : grid.dom.firstElementChild, | |
items : lab.dom.children, | |
}) | |
m.render( | |
slot.dom, | |
viewOf(v)(), | |
m.redraw, | |
) | |
slot.dom.firstChild.style.gridTemplateColumns = columns.map(x => x +'px').join(' ') | |
} | |
} | |
function Lab(v){ | |
const grid = viewOf(v)() | |
const items = grid.children | |
grid.children = [] | |
return [ | |
Inert(grid), | |
Inert(items), | |
] | |
} | |
function Slot(){ | |
return ( | |
m('slot', { | |
style: ` | |
display: contents; | |
`, | |
}) | |
) | |
} | |
function Inert(){ | |
return ( | |
m('fieldset', { | |
disabled : true, | |
inert : true, | |
style : { | |
position : 'fixed', | |
overflow : 'hidden', | |
top : '100vh', | |
left : '100vw', | |
}, | |
}, | |
...arguments, | |
) | |
) | |
} | |
function columnise({grid : $grid, items: $items}){ | |
const styles = getComputedStyle($grid) | |
const items = $items.map(x => x.offsetWidth) | |
const grid = $grid.clientWidth + int(styles.paddingLeft) + int(styles.paddingRight) | |
const gap = styles.columnGap | |
const columns = [] | |
let index = 0 | |
let buffer = 0 | |
for(const item of items){ | |
if(index !== 0) | |
buffer += gap | |
if(buffer + item > width){ | |
buffer = 0 | |
index = 0 | |
} | |
const column = columns[index] | |
if(item > column) | |
columns[index] = item | |
if(index === 0){ | |
buffer += item | |
index += 1 | |
} | |
} | |
return columns | |
} | |
function int(string){ | |
Number(string.match(/\d+/)) | |
} | |
function pure_render(site, content, callback){ | |
const signal = Symbol() | |
let vnode | |
try { | |
m.render( | |
site, | |
m.fragment({ | |
oncreate(){ throw signal }, | |
onupdate(){ throw signal }, | |
}, | |
vnode = content, | |
), | |
callback, | |
) | |
} | |
catch(error){ | |
if(error !== signal) | |
throw error | |
} | |
return vnode | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment