Skip to content

Instantly share code, notes, and snippets.

@barneycarroll
Last active July 7, 2022 14:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barneycarroll/e11e05e91e0fb8d08ae569a897f4fe4c to your computer and use it in GitHub Desktop.
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
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