masonry-snap-grid-layout
Version:
A performant, responsive masonry layout library with smooth animations, dynamic columns, and zero dependencies.
145 lines (142 loc) • 5.19 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
MasonrySnapGridLayout: () => MasonrySnapGridLayout,
default: () => index_default
});
module.exports = __toCommonJS(index_exports);
// src/MasonrySnapGridLayout.ts
var MasonrySnapGridLayout = class {
/**
* Creates a new MasonrySnapGridLayout instance.
*
* @param container - The HTML element that will act as the grid container.
* @param options - Partial configuration object for layout behavior and rendering.
*/
constructor(container, options) {
/** A reference to all grid item elements currently rendered. */
this.items = [];
/** Tracks the current heights of each column to position new items. */
this.columnHeights = [];
/** Stores requestAnimationFrame ID for layout updates to prevent redundant calls. */
this.rafId = null;
this.container = container;
this.options = {
gutter: 16,
minColWidth: 250,
animate: true,
transitionDuration: 400,
classNames: {
container: "masonry-snap-grid-container",
item: "masonry-snap-grid-item"
},
...options
};
this.container.classList.add(this.options.classNames.container || "");
this.renderItems();
this.setupResizeObserver();
}
/**
* Renders the provided items into the container.
* Clears previous items and re-builds DOM structure.
*/
renderItems() {
this.items.forEach((item) => item.remove());
this.items = [];
const fragment = document.createDocumentFragment();
this.options.items.forEach((itemData) => {
const itemElement = this.options.renderItem(itemData);
itemElement.classList.add(this.options.classNames.item || "");
fragment.appendChild(itemElement);
this.items.push(itemElement);
});
this.container.appendChild(fragment);
this.updateLayout();
}
/**
* Sets up a ResizeObserver to re-calculate layout when container size changes.
*/
setupResizeObserver() {
this.resizeObserver = new ResizeObserver(() => {
if (this.rafId) cancelAnimationFrame(this.rafId);
this.rafId = requestAnimationFrame(() => this.updateLayout());
});
this.resizeObserver.observe(this.container);
}
/**
* Calculates item positions and updates their transforms.
* Also adjusts the container height to fit all items.
*/
updateLayout() {
const { gutter, minColWidth, animate, transitionDuration } = this.options;
const containerWidth = this.container.clientWidth;
const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));
const colWidth = (containerWidth - (columns - 1) * gutter) / columns;
this.columnHeights = new Array(columns).fill(0);
this.items.forEach((item) => {
const height = item.offsetHeight;
const minCol = this.findShortestColumn();
const x = minCol * (colWidth + gutter);
const y = this.columnHeights[minCol];
item.style.width = `${colWidth}px`;
item.style.transform = `translate3d(${x}px, ${y}px, 0)`;
item.style.transition = animate ? `transform ${transitionDuration}ms ease` : "none";
this.columnHeights[minCol] += height + gutter;
});
const maxHeight = Math.max(...this.columnHeights);
this.container.style.height = `${maxHeight}px`;
}
/**
* Finds the index of the column with the smallest total height.
*
* @returns Index of the shortest column.
*/
findShortestColumn() {
return this.columnHeights.indexOf(Math.min(...this.columnHeights));
}
/**
* Replaces current items with a new set and re-renders the layout.
*
* @param newItems - New set of data items to render.
*/
updateItems(newItems) {
this.options.items = newItems;
this.renderItems();
}
/**
* Cleans up event listeners, observers, and DOM modifications.
* This should be called before discarding the instance.
*/
destroy() {
this.resizeObserver?.disconnect();
if (this.rafId) cancelAnimationFrame(this.rafId);
this.container.innerHTML = "";
this.container.removeAttribute("style");
this.container.classList.remove(this.options.classNames.container || "");
}
};
// src/index.ts
var index_default = MasonrySnapGridLayout;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
MasonrySnapGridLayout
});
//# sourceMappingURL=index.js.map