masonry-snap-grid-layout 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{MasonrySnapGridLayout-CPmdhc_q.d.cts → MasonrySnapGridLayout-CGay5M_v.d.cts} +45 -32
- package/dist/{MasonrySnapGridLayout-CPmdhc_q.d.ts → MasonrySnapGridLayout-CGay5M_v.d.ts} +45 -32
- package/dist/esm/index.js +161 -37
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react.js +227 -78
- package/dist/esm/react.js.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +161 -37
- package/dist/index.js.map +1 -1
- package/dist/react.d.cts +7 -17
- package/dist/react.d.ts +7 -17
- package/dist/react.js +225 -77
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ A **performant, SSR-friendly** masonry grid layout library with smooth animation
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
## ✨ What's New (v1.0.
|
|
13
|
+
## ✨ What's New (v1.0.17)
|
|
14
14
|
✅ **SSR-Ready Rendering** — On the server, items are rendered as plain HTML so your grid is SEO-friendly and instantly visible.
|
|
15
15
|
✅ **Hydration Takeover** — On the client, the library recalculates and animates the masonry layout after hydration.
|
|
16
16
|
✅ **Zero Dependencies** — Written in TypeScript, works with React and Vanilla JS.
|
|
@@ -59,63 +59,76 @@ interface MasonrySnapGridRef {
|
|
|
59
59
|
layout: MasonrySnapGridLayout | null;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/**
|
|
63
|
-
* MasonrySnapGridLayout
|
|
64
|
-
*
|
|
65
|
-
* A lightweight, generic TypeScript implementation of a responsive Masonry-style grid layout
|
|
66
|
-
* with optional animations, custom rendering, and automatic re-layout on resize.
|
|
67
|
-
*
|
|
68
|
-
* @template T - The type of data items that will be rendered into grid elements.
|
|
69
|
-
*/
|
|
70
62
|
declare class MasonrySnapGridLayout<T = any> {
|
|
71
|
-
/** The container element where the Masonry grid will be rendered. */
|
|
72
63
|
private readonly container;
|
|
73
|
-
/** Fully resolved options object with defaults merged. */
|
|
74
64
|
private readonly options;
|
|
75
|
-
/** A reference to all grid item elements currently rendered. */
|
|
76
65
|
private items;
|
|
77
|
-
/** Tracks the current heights of each column to position new items. */
|
|
78
66
|
private columnHeights;
|
|
79
|
-
/** Observes container resizing to trigger re-layout. */
|
|
80
67
|
private resizeObserver;
|
|
81
|
-
/** Stores requestAnimationFrame ID for layout updates to prevent redundant calls. */
|
|
82
68
|
private rafId;
|
|
69
|
+
private lastContainerWidth;
|
|
70
|
+
private itemPool;
|
|
71
|
+
private isDestroyed;
|
|
72
|
+
constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>);
|
|
83
73
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* @param container - The HTML element that will act as the grid container.
|
|
87
|
-
* @param options - Partial configuration object for layout behavior and rendering.
|
|
74
|
+
* Initialize layout: applies base classes, renders initial items,
|
|
75
|
+
* and sets up resize monitoring.
|
|
88
76
|
*/
|
|
89
|
-
|
|
77
|
+
private init;
|
|
90
78
|
/**
|
|
91
|
-
* Renders
|
|
92
|
-
*
|
|
79
|
+
* Renders items into the container using a pooled DOM strategy:
|
|
80
|
+
* - Avoids DOM churn by reusing elements where possible
|
|
81
|
+
* - Only creates new nodes when needed
|
|
82
|
+
* - Removes unused pool items when shrinking
|
|
93
83
|
*/
|
|
94
84
|
private renderItems;
|
|
95
85
|
/**
|
|
96
|
-
* Sets up a ResizeObserver to re-
|
|
86
|
+
* Sets up a ResizeObserver on the container to trigger re-layout
|
|
87
|
+
* when width changes — throttled to animation frames for performance.
|
|
97
88
|
*/
|
|
98
89
|
private setupResizeObserver;
|
|
99
90
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
91
|
+
* Core layout function:
|
|
92
|
+
* - Calculates number of columns based on container width & min column width
|
|
93
|
+
* - Measures all items to avoid forced reflows during positioning
|
|
94
|
+
* - Positions items in the shortest column to maintain balance
|
|
102
95
|
*/
|
|
103
96
|
private updateLayout;
|
|
104
97
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
98
|
+
* Measures item heights without affecting layout:
|
|
99
|
+
* - Temporarily forces block layout for accurate measurement
|
|
100
|
+
* - Restores original styles after measuring
|
|
101
|
+
*/
|
|
102
|
+
private measureItems;
|
|
103
|
+
/**
|
|
104
|
+
* Positions items column-by-column:
|
|
105
|
+
* - Chooses the shortest column for each item to maintain balance
|
|
106
|
+
* - Uses transform for GPU-accelerated positioning
|
|
107
|
+
*/
|
|
108
|
+
private positionItems;
|
|
109
|
+
/**
|
|
110
|
+
* Sets the container height to match the tallest column
|
|
111
|
+
* while subtracting trailing gutter space for a clean edge.
|
|
112
|
+
*/
|
|
113
|
+
private setContainerHeight;
|
|
114
|
+
/**
|
|
115
|
+
* Simple fallback layout in case the Masonry calculation fails:
|
|
116
|
+
* stacks items vertically in one column.
|
|
117
|
+
*/
|
|
118
|
+
private applyFallbackLayout;
|
|
119
|
+
/**
|
|
120
|
+
* Finds the column with the least accumulated height.
|
|
108
121
|
*/
|
|
109
122
|
private findShortestColumn;
|
|
110
123
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* @param newItems - New set of data items to render.
|
|
124
|
+
* Public method to replace current items and trigger a full re-render.
|
|
114
125
|
*/
|
|
115
126
|
updateItems(newItems: T[]): void;
|
|
116
127
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
128
|
+
* Cleanly tears down the layout:
|
|
129
|
+
* - Stops observing size changes
|
|
130
|
+
* - Cancels pending animation frames
|
|
131
|
+
* - Clears DOM references and resets container
|
|
119
132
|
*/
|
|
120
133
|
destroy(): void;
|
|
121
134
|
}
|
|
@@ -59,63 +59,76 @@ interface MasonrySnapGridRef {
|
|
|
59
59
|
layout: MasonrySnapGridLayout | null;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/**
|
|
63
|
-
* MasonrySnapGridLayout
|
|
64
|
-
*
|
|
65
|
-
* A lightweight, generic TypeScript implementation of a responsive Masonry-style grid layout
|
|
66
|
-
* with optional animations, custom rendering, and automatic re-layout on resize.
|
|
67
|
-
*
|
|
68
|
-
* @template T - The type of data items that will be rendered into grid elements.
|
|
69
|
-
*/
|
|
70
62
|
declare class MasonrySnapGridLayout<T = any> {
|
|
71
|
-
/** The container element where the Masonry grid will be rendered. */
|
|
72
63
|
private readonly container;
|
|
73
|
-
/** Fully resolved options object with defaults merged. */
|
|
74
64
|
private readonly options;
|
|
75
|
-
/** A reference to all grid item elements currently rendered. */
|
|
76
65
|
private items;
|
|
77
|
-
/** Tracks the current heights of each column to position new items. */
|
|
78
66
|
private columnHeights;
|
|
79
|
-
/** Observes container resizing to trigger re-layout. */
|
|
80
67
|
private resizeObserver;
|
|
81
|
-
/** Stores requestAnimationFrame ID for layout updates to prevent redundant calls. */
|
|
82
68
|
private rafId;
|
|
69
|
+
private lastContainerWidth;
|
|
70
|
+
private itemPool;
|
|
71
|
+
private isDestroyed;
|
|
72
|
+
constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>);
|
|
83
73
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* @param container - The HTML element that will act as the grid container.
|
|
87
|
-
* @param options - Partial configuration object for layout behavior and rendering.
|
|
74
|
+
* Initialize layout: applies base classes, renders initial items,
|
|
75
|
+
* and sets up resize monitoring.
|
|
88
76
|
*/
|
|
89
|
-
|
|
77
|
+
private init;
|
|
90
78
|
/**
|
|
91
|
-
* Renders
|
|
92
|
-
*
|
|
79
|
+
* Renders items into the container using a pooled DOM strategy:
|
|
80
|
+
* - Avoids DOM churn by reusing elements where possible
|
|
81
|
+
* - Only creates new nodes when needed
|
|
82
|
+
* - Removes unused pool items when shrinking
|
|
93
83
|
*/
|
|
94
84
|
private renderItems;
|
|
95
85
|
/**
|
|
96
|
-
* Sets up a ResizeObserver to re-
|
|
86
|
+
* Sets up a ResizeObserver on the container to trigger re-layout
|
|
87
|
+
* when width changes — throttled to animation frames for performance.
|
|
97
88
|
*/
|
|
98
89
|
private setupResizeObserver;
|
|
99
90
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
91
|
+
* Core layout function:
|
|
92
|
+
* - Calculates number of columns based on container width & min column width
|
|
93
|
+
* - Measures all items to avoid forced reflows during positioning
|
|
94
|
+
* - Positions items in the shortest column to maintain balance
|
|
102
95
|
*/
|
|
103
96
|
private updateLayout;
|
|
104
97
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
98
|
+
* Measures item heights without affecting layout:
|
|
99
|
+
* - Temporarily forces block layout for accurate measurement
|
|
100
|
+
* - Restores original styles after measuring
|
|
101
|
+
*/
|
|
102
|
+
private measureItems;
|
|
103
|
+
/**
|
|
104
|
+
* Positions items column-by-column:
|
|
105
|
+
* - Chooses the shortest column for each item to maintain balance
|
|
106
|
+
* - Uses transform for GPU-accelerated positioning
|
|
107
|
+
*/
|
|
108
|
+
private positionItems;
|
|
109
|
+
/**
|
|
110
|
+
* Sets the container height to match the tallest column
|
|
111
|
+
* while subtracting trailing gutter space for a clean edge.
|
|
112
|
+
*/
|
|
113
|
+
private setContainerHeight;
|
|
114
|
+
/**
|
|
115
|
+
* Simple fallback layout in case the Masonry calculation fails:
|
|
116
|
+
* stacks items vertically in one column.
|
|
117
|
+
*/
|
|
118
|
+
private applyFallbackLayout;
|
|
119
|
+
/**
|
|
120
|
+
* Finds the column with the least accumulated height.
|
|
108
121
|
*/
|
|
109
122
|
private findShortestColumn;
|
|
110
123
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* @param newItems - New set of data items to render.
|
|
124
|
+
* Public method to replace current items and trigger a full re-render.
|
|
114
125
|
*/
|
|
115
126
|
updateItems(newItems: T[]): void;
|
|
116
127
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
128
|
+
* Cleanly tears down the layout:
|
|
129
|
+
* - Stops observing size changes
|
|
130
|
+
* - Cancels pending animation frames
|
|
131
|
+
* - Clears DOM references and resets container
|
|
119
132
|
*/
|
|
120
133
|
destroy(): void;
|
|
121
134
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
// src/MasonrySnapGridLayout.ts
|
|
2
2
|
var MasonrySnapGridLayout = class {
|
|
3
|
-
/**
|
|
4
|
-
* Creates a new MasonrySnapGridLayout instance.
|
|
5
|
-
*
|
|
6
|
-
* @param container - The HTML element that will act as the grid container.
|
|
7
|
-
* @param options - Partial configuration object for layout behavior and rendering.
|
|
8
|
-
*/
|
|
9
3
|
constructor(container, options) {
|
|
10
|
-
|
|
4
|
+
// Active DOM elements currently in the layout
|
|
11
5
|
this.items = [];
|
|
12
|
-
|
|
6
|
+
// Running height for each column (used for placement calculations)
|
|
13
7
|
this.columnHeights = [];
|
|
14
|
-
|
|
8
|
+
// Tracks a pending animation frame request for layout updates
|
|
15
9
|
this.rafId = null;
|
|
10
|
+
// Cache last measured container width to avoid unnecessary relayouts
|
|
11
|
+
this.lastContainerWidth = 0;
|
|
12
|
+
// Pool of DOM elements for recycling between renders (avoids costly re-creation)
|
|
13
|
+
this.itemPool = [];
|
|
14
|
+
// Flag to prevent operations after destruction
|
|
15
|
+
this.isDestroyed = false;
|
|
16
|
+
if (!container) {
|
|
17
|
+
throw new Error("Container element is required");
|
|
18
|
+
}
|
|
16
19
|
this.container = container;
|
|
17
20
|
this.options = {
|
|
18
21
|
gutter: 16,
|
|
@@ -25,87 +28,208 @@ var MasonrySnapGridLayout = class {
|
|
|
25
28
|
},
|
|
26
29
|
...options
|
|
27
30
|
};
|
|
31
|
+
this.init();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Initialize layout: applies base classes, renders initial items,
|
|
35
|
+
* and sets up resize monitoring.
|
|
36
|
+
*/
|
|
37
|
+
init() {
|
|
38
|
+
if (this.isDestroyed) return;
|
|
28
39
|
this.container.classList.add(this.options.classNames.container || "");
|
|
29
40
|
this.renderItems();
|
|
30
41
|
this.setupResizeObserver();
|
|
31
42
|
}
|
|
32
43
|
/**
|
|
33
|
-
* Renders
|
|
34
|
-
*
|
|
44
|
+
* Renders items into the container using a pooled DOM strategy:
|
|
45
|
+
* - Avoids DOM churn by reusing elements where possible
|
|
46
|
+
* - Only creates new nodes when needed
|
|
47
|
+
* - Removes unused pool items when shrinking
|
|
35
48
|
*/
|
|
36
49
|
renderItems() {
|
|
37
|
-
this.
|
|
50
|
+
if (this.isDestroyed) return;
|
|
51
|
+
this.items.forEach((item) => {
|
|
52
|
+
if (!this.options.items.some((_, i) => this.itemPool[i] === item)) {
|
|
53
|
+
item.remove();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
38
56
|
this.items = [];
|
|
57
|
+
this.columnHeights = [];
|
|
39
58
|
const fragment = document.createDocumentFragment();
|
|
40
|
-
this.options.items.forEach((itemData) => {
|
|
41
|
-
|
|
42
|
-
itemElement
|
|
59
|
+
this.options.items.forEach((itemData, index) => {
|
|
60
|
+
let itemElement = this.itemPool[index];
|
|
61
|
+
if (!itemElement) {
|
|
62
|
+
itemElement = document.createElement("div");
|
|
63
|
+
itemElement.classList.add(this.options.classNames.item || "");
|
|
64
|
+
this.itemPool[index] = itemElement;
|
|
65
|
+
}
|
|
66
|
+
const content = this.options.renderItem(itemData);
|
|
67
|
+
if (typeof content === "string") {
|
|
68
|
+
itemElement.innerHTML = content;
|
|
69
|
+
} else if (content instanceof Node) {
|
|
70
|
+
itemElement.innerHTML = "";
|
|
71
|
+
itemElement.appendChild(content);
|
|
72
|
+
}
|
|
43
73
|
fragment.appendChild(itemElement);
|
|
44
74
|
this.items.push(itemElement);
|
|
45
75
|
});
|
|
76
|
+
while (this.itemPool.length > this.options.items.length) {
|
|
77
|
+
const item = this.itemPool.pop();
|
|
78
|
+
item.remove();
|
|
79
|
+
}
|
|
46
80
|
this.container.appendChild(fragment);
|
|
47
81
|
this.updateLayout();
|
|
48
82
|
}
|
|
49
83
|
/**
|
|
50
|
-
* Sets up a ResizeObserver to re-
|
|
84
|
+
* Sets up a ResizeObserver on the container to trigger re-layout
|
|
85
|
+
* when width changes — throttled to animation frames for performance.
|
|
51
86
|
*/
|
|
52
87
|
setupResizeObserver() {
|
|
88
|
+
if (this.resizeObserver) {
|
|
89
|
+
this.resizeObserver.disconnect();
|
|
90
|
+
}
|
|
53
91
|
this.resizeObserver = new ResizeObserver(() => {
|
|
54
92
|
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
55
|
-
this.rafId = requestAnimationFrame(() =>
|
|
93
|
+
this.rafId = requestAnimationFrame(() => {
|
|
94
|
+
const newWidth = this.container.clientWidth;
|
|
95
|
+
if (newWidth !== this.lastContainerWidth) {
|
|
96
|
+
this.lastContainerWidth = newWidth;
|
|
97
|
+
this.updateLayout();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
56
100
|
});
|
|
57
101
|
this.resizeObserver.observe(this.container);
|
|
58
102
|
}
|
|
59
103
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
104
|
+
* Core layout function:
|
|
105
|
+
* - Calculates number of columns based on container width & min column width
|
|
106
|
+
* - Measures all items to avoid forced reflows during positioning
|
|
107
|
+
* - Positions items in the shortest column to maintain balance
|
|
62
108
|
*/
|
|
63
109
|
updateLayout() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
110
|
+
if (this.isDestroyed || !this.container.isConnected) return;
|
|
111
|
+
try {
|
|
112
|
+
const { gutter, minColWidth, animate, transitionDuration } = this.options;
|
|
113
|
+
const containerWidth = this.container.clientWidth;
|
|
114
|
+
if (containerWidth <= 0) {
|
|
115
|
+
this.container.style.height = "0";
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));
|
|
119
|
+
const colWidth = (containerWidth - (columns - 1) * gutter) / columns;
|
|
120
|
+
this.columnHeights = new Array(columns).fill(0);
|
|
121
|
+
const itemHeights = this.measureItems(colWidth);
|
|
122
|
+
this.positionItems(colWidth, gutter, animate, transitionDuration, itemHeights);
|
|
123
|
+
this.setContainerHeight(gutter);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error("Masonry layout failed:", error);
|
|
126
|
+
this.applyFallbackLayout();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Measures item heights without affecting layout:
|
|
131
|
+
* - Temporarily forces block layout for accurate measurement
|
|
132
|
+
* - Restores original styles after measuring
|
|
133
|
+
*/
|
|
134
|
+
measureItems(colWidth) {
|
|
135
|
+
return this.items.map((item) => {
|
|
136
|
+
const originalStyles = {
|
|
137
|
+
display: item.style.display,
|
|
138
|
+
visibility: item.style.visibility,
|
|
139
|
+
position: item.style.position,
|
|
140
|
+
width: item.style.width
|
|
141
|
+
};
|
|
142
|
+
item.style.display = "block";
|
|
143
|
+
item.style.visibility = "hidden";
|
|
144
|
+
item.style.position = "absolute";
|
|
145
|
+
item.style.width = `${colWidth}px`;
|
|
70
146
|
const height = item.offsetHeight;
|
|
147
|
+
Object.assign(item.style, originalStyles);
|
|
148
|
+
return height;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Positions items column-by-column:
|
|
153
|
+
* - Chooses the shortest column for each item to maintain balance
|
|
154
|
+
* - Uses transform for GPU-accelerated positioning
|
|
155
|
+
*/
|
|
156
|
+
positionItems(colWidth, gutter, animate, transitionDuration, itemHeights) {
|
|
157
|
+
this.items.forEach((item, index) => {
|
|
158
|
+
const height = itemHeights[index];
|
|
71
159
|
const minCol = this.findShortestColumn();
|
|
72
160
|
const x = minCol * (colWidth + gutter);
|
|
73
161
|
const y = this.columnHeights[minCol];
|
|
74
162
|
item.style.width = `${colWidth}px`;
|
|
75
163
|
item.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
|
76
164
|
item.style.transition = animate ? `transform ${transitionDuration}ms ease` : "none";
|
|
165
|
+
item.style.willChange = "transform";
|
|
77
166
|
this.columnHeights[minCol] += height + gutter;
|
|
78
167
|
});
|
|
79
|
-
const maxHeight = Math.max(...this.columnHeights);
|
|
80
|
-
this.container.style.height = `${maxHeight}px`;
|
|
81
168
|
}
|
|
82
169
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
|
|
170
|
+
* Sets the container height to match the tallest column
|
|
171
|
+
* while subtracting trailing gutter space for a clean edge.
|
|
172
|
+
*/
|
|
173
|
+
setContainerHeight(gutter) {
|
|
174
|
+
const maxHeight = Math.max(0, ...this.columnHeights);
|
|
175
|
+
const containerHeight = maxHeight > 0 ? maxHeight - gutter : 0;
|
|
176
|
+
this.container.style.height = `${containerHeight}px`;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Simple fallback layout in case the Masonry calculation fails:
|
|
180
|
+
* stacks items vertically in one column.
|
|
181
|
+
*/
|
|
182
|
+
applyFallbackLayout() {
|
|
183
|
+
let top = 0;
|
|
184
|
+
this.items.forEach((item) => {
|
|
185
|
+
item.style.transform = `translate3d(0, ${top}px, 0)`;
|
|
186
|
+
top += item.offsetHeight + this.options.gutter;
|
|
187
|
+
});
|
|
188
|
+
this.container.style.height = `${top - this.options.gutter}px`;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Finds the column with the least accumulated height.
|
|
86
192
|
*/
|
|
87
193
|
findShortestColumn() {
|
|
88
|
-
|
|
194
|
+
let minIndex = 0;
|
|
195
|
+
let minHeight = Infinity;
|
|
196
|
+
this.columnHeights.forEach((height, index) => {
|
|
197
|
+
if (height < minHeight) {
|
|
198
|
+
minHeight = height;
|
|
199
|
+
minIndex = index;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return minIndex;
|
|
89
203
|
}
|
|
90
204
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* @param newItems - New set of data items to render.
|
|
205
|
+
* Public method to replace current items and trigger a full re-render.
|
|
94
206
|
*/
|
|
95
207
|
updateItems(newItems) {
|
|
208
|
+
if (this.isDestroyed) return;
|
|
96
209
|
this.options.items = newItems;
|
|
97
210
|
this.renderItems();
|
|
98
211
|
}
|
|
99
212
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
213
|
+
* Cleanly tears down the layout:
|
|
214
|
+
* - Stops observing size changes
|
|
215
|
+
* - Cancels pending animation frames
|
|
216
|
+
* - Clears DOM references and resets container
|
|
102
217
|
*/
|
|
103
218
|
destroy() {
|
|
219
|
+
if (this.isDestroyed) return;
|
|
220
|
+
this.isDestroyed = true;
|
|
104
221
|
this.resizeObserver?.disconnect();
|
|
105
|
-
|
|
222
|
+
this.resizeObserver = void 0;
|
|
223
|
+
if (this.rafId) {
|
|
224
|
+
cancelAnimationFrame(this.rafId);
|
|
225
|
+
this.rafId = null;
|
|
226
|
+
}
|
|
106
227
|
this.container.innerHTML = "";
|
|
107
228
|
this.container.removeAttribute("style");
|
|
108
229
|
this.container.classList.remove(this.options.classNames.container || "");
|
|
230
|
+
this.items = [];
|
|
231
|
+
this.columnHeights = [];
|
|
232
|
+
this.itemPool = [];
|
|
109
233
|
}
|
|
110
234
|
};
|
|
111
235
|
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/MasonrySnapGridLayout.ts","../../src/index.ts"],"sourcesContent":["import { MasonrySnapGridLayoutOptions } from './types';\n\n/**\n * MasonrySnapGridLayout\n *\n * A lightweight, generic TypeScript implementation of a responsive Masonry-style grid layout\n * with optional animations, custom rendering, and automatic re-layout on resize.\n *\n * @template T - The type of data items that will be rendered into grid elements.\n */\nexport default class MasonrySnapGridLayout<T = any> {\n /** The container element where the Masonry grid will be rendered. */\n private readonly container: HTMLElement;\n\n /** Fully resolved options object with defaults merged. */\n private readonly options: Required<MasonrySnapGridLayoutOptions<T>>;\n\n /** A reference to all grid item elements currently rendered. */\n private items: HTMLElement[] = [];\n\n /** Tracks the current heights of each column to position new items. */\n private columnHeights: number[] = [];\n\n /** Observes container resizing to trigger re-layout. */\n private resizeObserver: ResizeObserver | undefined;\n\n /** Stores requestAnimationFrame ID for layout updates to prevent redundant calls. */\n private rafId: number | null = null;\n\n /**\n * Creates a new MasonrySnapGridLayout instance.\n *\n * @param container - The HTML element that will act as the grid container.\n * @param options - Partial configuration object for layout behavior and rendering.\n */\n constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>) {\n this.container = container;\n\n // Merge default settings with user-provided options\n this.options = {\n gutter: 16,\n minColWidth: 250,\n animate: true,\n transitionDuration: 400,\n classNames: {\n container: 'masonry-snap-grid-container',\n item: 'masonry-snap-grid-item',\n },\n ...options,\n };\n\n // Apply base container class for styling\n this.container.classList.add(this.options.classNames.container || \"\");\n\n // Initial rendering and layout setup\n this.renderItems();\n this.setupResizeObserver();\n }\n\n /**\n * Renders the provided items into the container.\n * Clears previous items and re-builds DOM structure.\n */\n private renderItems() {\n // Remove any previously rendered items\n this.items.forEach(item => item.remove());\n this.items = [];\n\n // Use DocumentFragment for efficient DOM updates\n const fragment = document.createDocumentFragment();\n\n // Create elements for each item and append to fragment\n this.options.items.forEach(itemData => {\n const itemElement = this.options.renderItem(itemData);\n itemElement.classList.add(this.options.classNames.item || \"\");\n fragment.appendChild(itemElement);\n this.items.push(itemElement);\n });\n\n // Append all items to container at once\n this.container.appendChild(fragment);\n\n // Trigger layout positioning\n this.updateLayout();\n }\n\n /**\n * Sets up a ResizeObserver to re-calculate layout when container size changes.\n */\n private setupResizeObserver() {\n this.resizeObserver = new ResizeObserver(() => {\n // Throttle updates to animation frame to avoid layout thrashing\n if (this.rafId) cancelAnimationFrame(this.rafId);\n this.rafId = requestAnimationFrame(() => this.updateLayout());\n });\n this.resizeObserver.observe(this.container);\n }\n\n /**\n * Calculates item positions and updates their transforms.\n * Also adjusts the container height to fit all items.\n */\n private updateLayout() {\n const { gutter, minColWidth, animate, transitionDuration } = this.options;\n const containerWidth = this.container.clientWidth;\n\n // Determine number of columns based on available width and minColWidth\n const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));\n\n // Calculate actual column width including gutters\n const colWidth = (containerWidth - (columns - 1) * gutter) / columns;\n\n // Reset heights for all columns\n this.columnHeights = new Array(columns).fill(0);\n\n // Position each item in the shortest column\n this.items.forEach((item) => {\n const height = item.offsetHeight;\n const minCol = this.findShortestColumn();\n const x = minCol * (colWidth + gutter);\n const y = this.columnHeights[minCol];\n\n // Apply positioning and animation\n item.style.width = `${colWidth}px`;\n item.style.transform = `translate3d(${x}px, ${y}px, 0)`;\n item.style.transition = animate\n ? `transform ${transitionDuration}ms ease`\n : 'none';\n\n // Update column height to account for this item\n this.columnHeights[minCol] += height + gutter;\n });\n\n // Set container height to the tallest column\n const maxHeight = Math.max(...this.columnHeights);\n this.container.style.height = `${maxHeight}px`;\n }\n\n /**\n * Finds the index of the column with the smallest total height.\n *\n * @returns Index of the shortest column.\n */\n private findShortestColumn(): number {\n return this.columnHeights.indexOf(Math.min(...this.columnHeights));\n }\n\n /**\n * Replaces current items with a new set and re-renders the layout.\n *\n * @param newItems - New set of data items to render.\n */\n public updateItems(newItems: T[]) {\n this.options.items = newItems;\n this.renderItems();\n }\n\n /**\n * Cleans up event listeners, observers, and DOM modifications.\n * This should be called before discarding the instance.\n */\n public destroy() {\n this.resizeObserver?.disconnect();\n if (this.rafId) cancelAnimationFrame(this.rafId);\n this.container.innerHTML = '';\n this.container.removeAttribute('style');\n this.container.classList.remove(this.options.classNames.container || \"\");\n }\n}\n","import './index.css'; // Base CSS styles for MasonrySnapGridLayout\nimport MasonrySnapGridLayout from './MasonrySnapGridLayout';\nimport { MasonrySnapGridLayoutOptions } from './types';\n\n/**\n * Entry point for the MasonrySnapGridLayout package.\n *\n * - Imports the default styles for the layout (`index.css`).\n * - Re-exports the core class and associated type definitions.\n * - This allows consumers to either:\n * - Import the default class directly.\n * - Or import named exports for more flexibility.\n */\n\nexport default MasonrySnapGridLayout;\nexport {\n MasonrySnapGridLayout,\n MasonrySnapGridLayoutOptions\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/MasonrySnapGridLayout.ts","../../src/index.ts"],"sourcesContent":["import { MasonrySnapGridLayoutOptions } from './types';\n\nexport default class MasonrySnapGridLayout<T = any> {\n // Main container for the grid\n private readonly container: HTMLElement;\n // Normalized config options with defaults applied\n private readonly options: Required<MasonrySnapGridLayoutOptions<T>>;\n // Active DOM elements currently in the layout\n private items: HTMLElement[] = [];\n // Running height for each column (used for placement calculations)\n private columnHeights: number[] = [];\n // Resize observer to detect container width changes\n private resizeObserver: ResizeObserver | undefined;\n // Tracks a pending animation frame request for layout updates\n private rafId: number | null = null;\n // Cache last measured container width to avoid unnecessary relayouts\n private lastContainerWidth = 0;\n // Pool of DOM elements for recycling between renders (avoids costly re-creation)\n private itemPool: HTMLElement[] = [];\n // Flag to prevent operations after destruction\n private isDestroyed = false;\n\n constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>) {\n if (!container) {\n throw new Error('Container element is required');\n }\n\n this.container = container;\n // Merge user-provided options with defaults\n this.options = {\n gutter: 16,\n minColWidth: 250,\n animate: true,\n transitionDuration: 400,\n classNames: {\n container: 'masonry-snap-grid-container',\n item: 'masonry-snap-grid-item',\n },\n ...options,\n };\n\n this.init();\n }\n\n /**\n * Initialize layout: applies base classes, renders initial items,\n * and sets up resize monitoring.\n */\n private init(): void {\n if (this.isDestroyed) return;\n\n this.container.classList.add(this.options.classNames.container || '');\n this.renderItems();\n this.setupResizeObserver();\n }\n\n /**\n * Renders items into the container using a pooled DOM strategy:\n * - Avoids DOM churn by reusing elements where possible\n * - Only creates new nodes when needed\n * - Removes unused pool items when shrinking\n */\n private renderItems(): void {\n if (this.isDestroyed) return;\n\n // Remove orphaned elements from the DOM\n this.items.forEach(item => {\n if (!this.options.items.some((_, i) => this.itemPool[i] === item)) {\n item.remove();\n }\n });\n\n this.items = [];\n this.columnHeights = [];\n\n // Use a fragment for batch DOM insertion (better performance)\n const fragment = document.createDocumentFragment();\n this.options.items.forEach((itemData, index) => {\n let itemElement = this.itemPool[index];\n\n if (!itemElement) {\n itemElement = document.createElement('div');\n itemElement.classList.add(this.options.classNames.item || '');\n this.itemPool[index] = itemElement;\n }\n\n // Render content via provided renderItem function\n const content = this.options.renderItem(itemData);\n if (typeof content === 'string') {\n itemElement.innerHTML = content;\n } else if (content instanceof Node) {\n itemElement.innerHTML = '';\n itemElement.appendChild(content);\n }\n\n fragment.appendChild(itemElement);\n this.items.push(itemElement);\n });\n\n // Trim excess pooled items\n while (this.itemPool.length > this.options.items.length) {\n const item = this.itemPool.pop()!;\n item.remove();\n }\n\n this.container.appendChild(fragment);\n this.updateLayout();\n }\n\n /**\n * Sets up a ResizeObserver on the container to trigger re-layout\n * when width changes — throttled to animation frames for performance.\n */\n private setupResizeObserver(): void {\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n }\n\n this.resizeObserver = new ResizeObserver(() => {\n if (this.rafId) cancelAnimationFrame(this.rafId);\n this.rafId = requestAnimationFrame(() => {\n const newWidth = this.container.clientWidth;\n if (newWidth !== this.lastContainerWidth) {\n this.lastContainerWidth = newWidth;\n this.updateLayout();\n }\n });\n });\n\n this.resizeObserver.observe(this.container);\n }\n\n /**\n * Core layout function:\n * - Calculates number of columns based on container width & min column width\n * - Measures all items to avoid forced reflows during positioning\n * - Positions items in the shortest column to maintain balance\n */\n private updateLayout(): void {\n if (this.isDestroyed || !this.container.isConnected) return;\n\n try {\n const { gutter, minColWidth, animate, transitionDuration } = this.options;\n const containerWidth = this.container.clientWidth;\n\n // Avoid layout if container is hidden or collapsed\n if (containerWidth <= 0) {\n this.container.style.height = '0';\n return;\n }\n\n // Determine column count and width\n const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));\n const colWidth = (containerWidth - (columns - 1) * gutter) / columns;\n\n // Reset tracking for column heights\n this.columnHeights = new Array(columns).fill(0);\n\n // Measure all items with the new column width before positioning\n const itemHeights = this.measureItems(colWidth);\n\n // Place each item in the shortest available column\n this.positionItems(colWidth, gutter, animate, transitionDuration, itemHeights);\n\n // Adjust container height to fit the tallest column\n this.setContainerHeight(gutter);\n } catch (error) {\n console.error('Masonry layout failed:', error);\n // Fallback: simple vertical stacking\n this.applyFallbackLayout();\n }\n }\n\n /**\n * Measures item heights without affecting layout:\n * - Temporarily forces block layout for accurate measurement\n * - Restores original styles after measuring\n */\n private measureItems(colWidth: number): number[] {\n return this.items.map(item => {\n const originalStyles = {\n display: item.style.display,\n visibility: item.style.visibility,\n position: item.style.position,\n width: item.style.width\n };\n\n item.style.display = 'block';\n item.style.visibility = 'hidden';\n item.style.position = 'absolute';\n item.style.width = `${colWidth}px`;\n\n const height = item.offsetHeight;\n\n Object.assign(item.style, originalStyles);\n return height;\n });\n }\n\n /**\n * Positions items column-by-column:\n * - Chooses the shortest column for each item to maintain balance\n * - Uses transform for GPU-accelerated positioning\n */\n private positionItems(\n colWidth: number,\n gutter: number,\n animate: boolean,\n transitionDuration: number,\n itemHeights: number[]\n ): void {\n this.items.forEach((item, index) => {\n const height = itemHeights[index];\n const minCol = this.findShortestColumn();\n const x = minCol * (colWidth + gutter);\n const y = this.columnHeights[minCol];\n\n item.style.width = `${colWidth}px`;\n item.style.transform = `translate3d(${x}px, ${y}px, 0)`;\n item.style.transition = animate\n ? `transform ${transitionDuration}ms ease`\n : 'none';\n item.style.willChange = 'transform';\n\n this.columnHeights[minCol] += height + gutter;\n });\n }\n\n /**\n * Sets the container height to match the tallest column\n * while subtracting trailing gutter space for a clean edge.\n */\n private setContainerHeight(gutter: number): void {\n const maxHeight = Math.max(0, ...this.columnHeights);\n const containerHeight = maxHeight > 0 ? maxHeight - gutter : 0;\n this.container.style.height = `${containerHeight}px`;\n }\n\n /**\n * Simple fallback layout in case the Masonry calculation fails:\n * stacks items vertically in one column.\n */\n private applyFallbackLayout(): void {\n let top = 0;\n this.items.forEach(item => {\n item.style.transform = `translate3d(0, ${top}px, 0)`;\n top += item.offsetHeight + this.options.gutter;\n });\n this.container.style.height = `${top - this.options.gutter}px`;\n }\n\n /**\n * Finds the column with the least accumulated height.\n */\n private findShortestColumn(): number {\n let minIndex = 0;\n let minHeight = Infinity;\n\n this.columnHeights.forEach((height, index) => {\n if (height < minHeight) {\n minHeight = height;\n minIndex = index;\n }\n });\n\n return minIndex;\n }\n\n /**\n * Public method to replace current items and trigger a full re-render.\n */\n public updateItems(newItems: T[]): void {\n if (this.isDestroyed) return;\n this.options.items = newItems;\n this.renderItems();\n }\n\n /**\n * Cleanly tears down the layout:\n * - Stops observing size changes\n * - Cancels pending animation frames\n * - Clears DOM references and resets container\n */\n public destroy(): void {\n if (this.isDestroyed) return;\n\n this.isDestroyed = true;\n\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n this.container.innerHTML = '';\n this.container.removeAttribute('style');\n this.container.classList.remove(this.options.classNames.container || '');\n\n this.items = [];\n this.columnHeights = [];\n this.itemPool = [];\n }\n}\n","import './index.css'; // Base CSS styles for MasonrySnapGridLayout\nimport MasonrySnapGridLayout from './MasonrySnapGridLayout';\nimport { MasonrySnapGridLayoutOptions } from './types';\n\n/**\n * Entry point for the MasonrySnapGridLayout package.\n *\n * - Imports the default styles for the layout (`index.css`).\n * - Re-exports the core class and associated type definitions.\n * - This allows consumers to either:\n * - Import the default class directly.\n * - Or import named exports for more flexibility.\n */\n\nexport default MasonrySnapGridLayout;\nexport {\n MasonrySnapGridLayout,\n MasonrySnapGridLayoutOptions\n};\n"],"mappings":"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","names":[]}
|