Add masonry layout functionality

This commit is contained in:
Svilen Markov 2024-10-13 18:33:26 +01:00
parent 17c8071de9
commit ab0b11cc92
3 changed files with 67 additions and 0 deletions

View file

@ -0,0 +1,52 @@
import { clamp } from "./utils.js";
export function setupMasonries(options = {}, selector = ".masonry") {
options = {
minColumnWidth: 300,
maxColumns: 6,
...options
};
const masonryContainers = document.querySelectorAll(selector);
for (let i = 0; i < masonryContainers.length; i++) {
const container = masonryContainers[i];
const items = Array.from(container.children);
let previousColumnsCount = 0;
const render = function() {
const columnsCount = clamp(
Math.floor(container.offsetWidth / options.minColumnWidth),
1,
Math.min(options.maxColumns, items.length)
);
if (columnsCount === previousColumnsCount) {
return;
} else {
container.textContent = "";
previousColumnsCount = columnsCount;
}
const columnsFragment = document.createDocumentFragment();
for (let i = 0; i < columnsCount; i++) {
const column = document.createElement("div");
column.className = "masonry-column";
columnsFragment.append(column);
}
// poor man's masonry
// TODO: add an option that allows placing items in the
// shortest column instead of iterating the columns in order
for (let i = 0; i < items.length; i++) {
columnsFragment.children[i % columnsCount].appendChild(items[i]);
}
container.append(columnsFragment);
};
const observer = new ResizeObserver(() => requestAnimationFrame(render));
observer.observe(container);
}
}

View file

@ -23,3 +23,7 @@ export function throttledDebounce(callback, maxDebounceTimes, debounceDelay) {
export function isElementVisible(element) {
return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
}
export function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}

View file

@ -440,6 +440,17 @@ kbd:active {
box-shadow: 0 0 0 0 var(--color-widget-background-highlight);
}
.masonry {
display: flex;
gap: var(--widget-gap);
}
.masonry-column {
flex: 1;
display: flex;
flex-direction: column;
}
.popover-container, [data-popover-html] {
display: none;
}