masonry-snap-grid-layout 0.0.4 → 0.0.5
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 +3 -1
- package/dist/MasonrySnapGridLayout-CF1GeXL9.d.cts +71 -0
- package/dist/MasonrySnapGridLayout-CF1GeXL9.d.ts +71 -0
- package/dist/esm/index.js +43 -102
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react.js +237 -0
- package/dist/esm/react.js.map +1 -0
- package/dist/index.d.cts +2 -119
- package/dist/index.d.ts +2 -119
- package/dist/index.js +43 -102
- package/dist/index.js.map +1 -1
- package/dist/react.d.cts +16 -0
- package/dist/react.d.ts +16 -0
- package/dist/react.js +252 -0
- package/dist/react.js.map +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -31,6 +31,7 @@ yarn add masonry-snap-grid-layout
|
|
|
31
31
|
|
|
32
32
|
```ts
|
|
33
33
|
import MasonrySnapGridLayout from 'masonry-snap-grid-layout';
|
|
34
|
+
import 'masonry-snap-grid-layout/dist/index.css'; // Import the CSS!
|
|
34
35
|
|
|
35
36
|
const container = document.getElementById('masonry-container')!;
|
|
36
37
|
const masonry = new MasonrySnapGridLayout(container, {
|
|
@@ -61,7 +62,8 @@ In your HTML:
|
|
|
61
62
|
'use client';
|
|
62
63
|
|
|
63
64
|
import React from 'react';
|
|
64
|
-
import MasonrySnapGrid from 'masonry-snap-grid-layout/react';
|
|
65
|
+
import MasonrySnapGrid from 'masonry-snap-grid-layout/react';
|
|
66
|
+
import 'masonry-snap-grid-layout/dist/index.css'; // Import the CSS!
|
|
65
67
|
|
|
66
68
|
export default function MasonryGrid() {
|
|
67
69
|
const items = Array.from({ length: 25 }, (_, i) => i);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MasonrySnapGridLayoutClassNames
|
|
3
|
+
* Optional CSS class names to override default styling selectors.
|
|
4
|
+
* Enables users to customize CSS classes easily.
|
|
5
|
+
*/
|
|
6
|
+
interface MasonrySnapGridLayoutClassNames {
|
|
7
|
+
container: string;
|
|
8
|
+
item?: string;
|
|
9
|
+
itemContent?: string;
|
|
10
|
+
itemHeader?: string;
|
|
11
|
+
itemTitle?: string;
|
|
12
|
+
itemId?: string;
|
|
13
|
+
itemBody?: string;
|
|
14
|
+
progressBar?: string;
|
|
15
|
+
progress?: string;
|
|
16
|
+
itemFooter?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* MasonrySnapGridLayoutOptions
|
|
20
|
+
* Configuration options accepted by MasonrySnapGridLayout.
|
|
21
|
+
*/
|
|
22
|
+
interface MasonrySnapGridLayoutOptions {
|
|
23
|
+
/** Spacing between items in pixels (default: 16) */
|
|
24
|
+
gutter?: number;
|
|
25
|
+
/** Minimum width for each column in pixels (default: 250) */
|
|
26
|
+
minColWidth?: number;
|
|
27
|
+
/** Enable or disable animations (default: true) */
|
|
28
|
+
animate?: boolean;
|
|
29
|
+
/** Duration of animation transitions in milliseconds (default: 400) */
|
|
30
|
+
transitionDuration?: number;
|
|
31
|
+
/** Initial number of items to generate on init (default: 30) */
|
|
32
|
+
initialItems?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Optional class names object to override default CSS class names.
|
|
35
|
+
*/
|
|
36
|
+
classNames?: MasonrySnapGridLayoutClassNames;
|
|
37
|
+
/**
|
|
38
|
+
* Item content can be:
|
|
39
|
+
* - a callback function that receives the item index and returns an HTMLElement or string (HTML markup),
|
|
40
|
+
* - a static HTMLElement or string (HTML markup) for all items,
|
|
41
|
+
* - or null to use the default template.
|
|
42
|
+
*/
|
|
43
|
+
itemContent?: ((index: number) => HTMLElement | string) | HTMLElement | string | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare class MasonrySnapGridLayout {
|
|
47
|
+
private readonly container;
|
|
48
|
+
private readonly options;
|
|
49
|
+
private classNames;
|
|
50
|
+
private items;
|
|
51
|
+
private positions;
|
|
52
|
+
private columnHeights;
|
|
53
|
+
private columns;
|
|
54
|
+
private lastPositions;
|
|
55
|
+
private resizeRaf;
|
|
56
|
+
private resizeObserver;
|
|
57
|
+
private boundResizeHandler;
|
|
58
|
+
constructor(container: HTMLElement, options?: MasonrySnapGridLayoutOptions);
|
|
59
|
+
private init;
|
|
60
|
+
private setupEventListeners;
|
|
61
|
+
generateItems(count: number): void;
|
|
62
|
+
private createItem;
|
|
63
|
+
calculateLayout(): void;
|
|
64
|
+
applyLayout(animate?: boolean): void;
|
|
65
|
+
private handleResize;
|
|
66
|
+
shuffleItems(): void;
|
|
67
|
+
addItems(count: number): void;
|
|
68
|
+
destroy(): void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { MasonrySnapGridLayout as M, type MasonrySnapGridLayoutOptions as a };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MasonrySnapGridLayoutClassNames
|
|
3
|
+
* Optional CSS class names to override default styling selectors.
|
|
4
|
+
* Enables users to customize CSS classes easily.
|
|
5
|
+
*/
|
|
6
|
+
interface MasonrySnapGridLayoutClassNames {
|
|
7
|
+
container: string;
|
|
8
|
+
item?: string;
|
|
9
|
+
itemContent?: string;
|
|
10
|
+
itemHeader?: string;
|
|
11
|
+
itemTitle?: string;
|
|
12
|
+
itemId?: string;
|
|
13
|
+
itemBody?: string;
|
|
14
|
+
progressBar?: string;
|
|
15
|
+
progress?: string;
|
|
16
|
+
itemFooter?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* MasonrySnapGridLayoutOptions
|
|
20
|
+
* Configuration options accepted by MasonrySnapGridLayout.
|
|
21
|
+
*/
|
|
22
|
+
interface MasonrySnapGridLayoutOptions {
|
|
23
|
+
/** Spacing between items in pixels (default: 16) */
|
|
24
|
+
gutter?: number;
|
|
25
|
+
/** Minimum width for each column in pixels (default: 250) */
|
|
26
|
+
minColWidth?: number;
|
|
27
|
+
/** Enable or disable animations (default: true) */
|
|
28
|
+
animate?: boolean;
|
|
29
|
+
/** Duration of animation transitions in milliseconds (default: 400) */
|
|
30
|
+
transitionDuration?: number;
|
|
31
|
+
/** Initial number of items to generate on init (default: 30) */
|
|
32
|
+
initialItems?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Optional class names object to override default CSS class names.
|
|
35
|
+
*/
|
|
36
|
+
classNames?: MasonrySnapGridLayoutClassNames;
|
|
37
|
+
/**
|
|
38
|
+
* Item content can be:
|
|
39
|
+
* - a callback function that receives the item index and returns an HTMLElement or string (HTML markup),
|
|
40
|
+
* - a static HTMLElement or string (HTML markup) for all items,
|
|
41
|
+
* - or null to use the default template.
|
|
42
|
+
*/
|
|
43
|
+
itemContent?: ((index: number) => HTMLElement | string) | HTMLElement | string | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare class MasonrySnapGridLayout {
|
|
47
|
+
private readonly container;
|
|
48
|
+
private readonly options;
|
|
49
|
+
private classNames;
|
|
50
|
+
private items;
|
|
51
|
+
private positions;
|
|
52
|
+
private columnHeights;
|
|
53
|
+
private columns;
|
|
54
|
+
private lastPositions;
|
|
55
|
+
private resizeRaf;
|
|
56
|
+
private resizeObserver;
|
|
57
|
+
private boundResizeHandler;
|
|
58
|
+
constructor(container: HTMLElement, options?: MasonrySnapGridLayoutOptions);
|
|
59
|
+
private init;
|
|
60
|
+
private setupEventListeners;
|
|
61
|
+
generateItems(count: number): void;
|
|
62
|
+
private createItem;
|
|
63
|
+
calculateLayout(): void;
|
|
64
|
+
applyLayout(animate?: boolean): void;
|
|
65
|
+
private handleResize;
|
|
66
|
+
shuffleItems(): void;
|
|
67
|
+
addItems(count: number): void;
|
|
68
|
+
destroy(): void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { MasonrySnapGridLayout as M, type MasonrySnapGridLayoutOptions as a };
|
package/dist/esm/index.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
// src/MasonrySnapGridLayout.ts
|
|
2
2
|
var MasonrySnapGridLayout = class {
|
|
3
|
-
/**
|
|
4
|
-
* Constructor initializes the layout with container element and options
|
|
5
|
-
* @param container HTMLElement container where masonry items are rendered
|
|
6
|
-
* @param options configuration options for layout and styling
|
|
7
|
-
*/
|
|
8
3
|
constructor(container2, options = {}) {
|
|
9
4
|
this.items = [];
|
|
10
5
|
this.positions = [];
|
|
@@ -12,65 +7,46 @@ var MasonrySnapGridLayout = class {
|
|
|
12
7
|
this.columns = 0;
|
|
13
8
|
this.lastPositions = [];
|
|
14
9
|
this.resizeRaf = null;
|
|
10
|
+
this.resizeObserver = null;
|
|
11
|
+
this.boundResizeHandler = this.handleResize.bind(this);
|
|
15
12
|
this.container = container2;
|
|
13
|
+
this.classNames = {
|
|
14
|
+
container: "masonry-snap-grid-container",
|
|
15
|
+
item: "masonry-snap-grid-item",
|
|
16
|
+
itemContent: "masonry-snap-grid-item-content",
|
|
17
|
+
itemHeader: "masonry-snap-grid-item-header",
|
|
18
|
+
itemTitle: "masonry-snap-grid-item-title",
|
|
19
|
+
itemId: "masonry-snap-grid-item-id",
|
|
20
|
+
itemBody: "masonry-snap-grid-item-body",
|
|
21
|
+
progressBar: "masonry-snap-grid-progress-bar",
|
|
22
|
+
progress: "masonry-snap-grid-progress",
|
|
23
|
+
itemFooter: "masonry-snap-grid-item-footer",
|
|
24
|
+
...options.classNames || {}
|
|
25
|
+
};
|
|
16
26
|
this.options = {
|
|
17
27
|
gutter: 16,
|
|
18
28
|
minColWidth: 250,
|
|
19
29
|
animate: true,
|
|
20
30
|
transitionDuration: 400,
|
|
21
|
-
initialItems:
|
|
31
|
+
initialItems: 0,
|
|
22
32
|
itemContent: null,
|
|
23
|
-
classNames:
|
|
24
|
-
container: "masonry-snap-grid-layout-container",
|
|
25
|
-
item: "masonry-snap-grid-layout-item",
|
|
26
|
-
itemContent: "item-content",
|
|
27
|
-
itemHeader: "item-header",
|
|
28
|
-
itemTitle: "item-title",
|
|
29
|
-
itemId: "item-id",
|
|
30
|
-
itemBody: "item-body",
|
|
31
|
-
progressBar: "progress-bar",
|
|
32
|
-
progress: "progress",
|
|
33
|
-
itemFooter: "item-footer"
|
|
34
|
-
},
|
|
33
|
+
classNames: this.classNames,
|
|
35
34
|
...options
|
|
36
35
|
};
|
|
37
|
-
this.classNames = {
|
|
38
|
-
container: "masonry-snap-grid-layout-container",
|
|
39
|
-
item: "masonry-snap-grid-layout-item",
|
|
40
|
-
itemContent: "item-content",
|
|
41
|
-
itemHeader: "item-header",
|
|
42
|
-
itemTitle: "item-title",
|
|
43
|
-
itemId: "item-id",
|
|
44
|
-
itemBody: "item-body",
|
|
45
|
-
progressBar: "progress-bar",
|
|
46
|
-
progress: "progress",
|
|
47
|
-
itemFooter: "item-footer",
|
|
48
|
-
...this.options.classNames || {}
|
|
49
|
-
};
|
|
50
36
|
this.container.classList.add(this.classNames.container);
|
|
51
37
|
this.init();
|
|
52
38
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Initialization: set listeners, generate initial items, layout
|
|
55
|
-
*/
|
|
56
39
|
init() {
|
|
57
40
|
this.setupEventListeners();
|
|
58
41
|
this.generateItems(this.options.initialItems);
|
|
59
42
|
this.calculateLayout();
|
|
60
43
|
this.applyLayout(false);
|
|
61
44
|
}
|
|
62
|
-
/**
|
|
63
|
-
* Setup event listeners for window resize and container resize observer
|
|
64
|
-
*/
|
|
65
45
|
setupEventListeners() {
|
|
66
|
-
|
|
67
|
-
resizeObserver.observe(this.container);
|
|
68
|
-
window.addEventListener("resize",
|
|
46
|
+
this.resizeObserver = new ResizeObserver(() => this.handleResize());
|
|
47
|
+
this.resizeObserver.observe(this.container);
|
|
48
|
+
window.addEventListener("resize", this.boundResizeHandler);
|
|
69
49
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Generate specified number of items, removing excess if any
|
|
72
|
-
* @param count number of items to generate
|
|
73
|
-
*/
|
|
74
50
|
generateItems(count) {
|
|
75
51
|
if (count < this.items.length) {
|
|
76
52
|
this.items.slice(count).forEach((item) => item.remove());
|
|
@@ -86,11 +62,6 @@ var MasonrySnapGridLayout = class {
|
|
|
86
62
|
}
|
|
87
63
|
this.container.appendChild(fragment);
|
|
88
64
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Create a single masonry item HTMLElement, with content from renderItemContent callback or default template
|
|
91
|
-
* @param index index of the item
|
|
92
|
-
* @returns HTMLElement for the item
|
|
93
|
-
*/
|
|
94
65
|
createItem(index) {
|
|
95
66
|
const div = document.createElement("div");
|
|
96
67
|
div.className = this.classNames.item;
|
|
@@ -101,52 +72,17 @@ var MasonrySnapGridLayout = class {
|
|
|
101
72
|
} else if (contentOption instanceof HTMLElement || typeof contentOption === "string") {
|
|
102
73
|
content = contentOption;
|
|
103
74
|
} else {
|
|
104
|
-
content =
|
|
75
|
+
content = `Item ${index + 1}`;
|
|
105
76
|
}
|
|
106
77
|
if (typeof content === "string") {
|
|
107
|
-
div.
|
|
78
|
+
div.textContent = content;
|
|
108
79
|
} else {
|
|
109
80
|
div.appendChild(content);
|
|
110
81
|
}
|
|
111
|
-
const height = 120 + Math.floor(Math.random() *
|
|
82
|
+
const height = 120 + Math.floor(Math.random() * 100);
|
|
112
83
|
div.style.height = `${height}px`;
|
|
113
|
-
const hue = index * 137.508 % 360;
|
|
114
|
-
div.style.background = `linear-gradient(135deg, hsl(${hue}, 70%, 60%), hsl(${hue + 40}, 70%, 50%))`;
|
|
115
84
|
return div;
|
|
116
85
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Default item content template string
|
|
119
|
-
* @param index index of the item
|
|
120
|
-
* @returns string HTML template for item content
|
|
121
|
-
*/
|
|
122
|
-
defaultItemContent(index) {
|
|
123
|
-
return `
|
|
124
|
-
<div class="${this.classNames.itemContent}">
|
|
125
|
-
<div class="${this.classNames.itemHeader}">
|
|
126
|
-
<div class="${this.classNames.itemTitle}">Item ${index + 1}</div>
|
|
127
|
-
<div class="${this.classNames.itemId}">#${index + 1}</div>
|
|
128
|
-
</div>
|
|
129
|
-
<div class="${this.classNames.itemBody}">
|
|
130
|
-
<div class="${this.classNames.progressBar}">
|
|
131
|
-
<div class="${this.classNames.progress}"></div>
|
|
132
|
-
</div>
|
|
133
|
-
</div>
|
|
134
|
-
<div class="${this.classNames.itemFooter}">
|
|
135
|
-
${this.getRandomEmoji()}
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
`;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Returns a random emoji from a fixed set for item footer
|
|
142
|
-
*/
|
|
143
|
-
getRandomEmoji() {
|
|
144
|
-
const emojis = ["\u{1F680}", "\u2728", "\u{1F525}", "\u{1F4A1}", "\u{1F31F}", "\u{1F3AF}", "\u26A1", "\u{1F4BB}", "\u{1F527}", "\u{1F4CA}"];
|
|
145
|
-
return emojis[Math.floor(Math.random() * emojis.length)];
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Calculate positions of each item in masonry grid based on container width, gutter, min column width
|
|
149
|
-
*/
|
|
150
86
|
calculateLayout() {
|
|
151
87
|
const { gutter, minColWidth } = this.options;
|
|
152
88
|
const containerWidth = this.container.clientWidth;
|
|
@@ -171,10 +107,6 @@ var MasonrySnapGridLayout = class {
|
|
|
171
107
|
const maxHeight = Math.max(...this.columnHeights);
|
|
172
108
|
this.container.style.height = `${maxHeight}px`;
|
|
173
109
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Apply calculated positions to each item with optional animation
|
|
176
|
-
* @param animate whether to animate layout changes (default false)
|
|
177
|
-
*/
|
|
178
110
|
applyLayout(animate = false) {
|
|
179
111
|
const duration = this.options.transitionDuration;
|
|
180
112
|
this.items.forEach((item, i) => {
|
|
@@ -187,7 +119,7 @@ var MasonrySnapGridLayout = class {
|
|
|
187
119
|
item.style.transition = "none";
|
|
188
120
|
item.style.transform = `translate3d(${pos.x + dx}px, ${pos.y + dy}px, 0)`;
|
|
189
121
|
void item.offsetHeight;
|
|
190
|
-
item.style.transition = `transform ${duration}ms
|
|
122
|
+
item.style.transition = `transform ${duration}ms ease`;
|
|
191
123
|
item.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;
|
|
192
124
|
} else {
|
|
193
125
|
item.style.transition = "none";
|
|
@@ -195,9 +127,6 @@ var MasonrySnapGridLayout = class {
|
|
|
195
127
|
}
|
|
196
128
|
});
|
|
197
129
|
}
|
|
198
|
-
/**
|
|
199
|
-
* Handle container resize using RAF to optimize performance
|
|
200
|
-
*/
|
|
201
130
|
handleResize() {
|
|
202
131
|
if (this.resizeRaf) cancelAnimationFrame(this.resizeRaf);
|
|
203
132
|
this.resizeRaf = requestAnimationFrame(() => {
|
|
@@ -205,9 +134,6 @@ var MasonrySnapGridLayout = class {
|
|
|
205
134
|
this.applyLayout(true);
|
|
206
135
|
});
|
|
207
136
|
}
|
|
208
|
-
/**
|
|
209
|
-
* Shuffle items randomly and reapply layout with animation
|
|
210
|
-
*/
|
|
211
137
|
shuffleItems() {
|
|
212
138
|
for (let i = this.items.length - 1; i > 0; i--) {
|
|
213
139
|
const j = Math.floor(Math.random() * (i + 1));
|
|
@@ -219,16 +145,31 @@ var MasonrySnapGridLayout = class {
|
|
|
219
145
|
this.calculateLayout();
|
|
220
146
|
this.applyLayout(true);
|
|
221
147
|
}
|
|
222
|
-
/**
|
|
223
|
-
* Add more items dynamically
|
|
224
|
-
* @param count number of items to add
|
|
225
|
-
*/
|
|
226
148
|
addItems(count) {
|
|
227
149
|
const newCount = this.items.length + count;
|
|
228
150
|
this.generateItems(newCount);
|
|
229
151
|
this.calculateLayout();
|
|
230
152
|
this.applyLayout(true);
|
|
231
153
|
}
|
|
154
|
+
destroy() {
|
|
155
|
+
if (this.resizeObserver) {
|
|
156
|
+
this.resizeObserver.disconnect();
|
|
157
|
+
this.resizeObserver = null;
|
|
158
|
+
}
|
|
159
|
+
window.removeEventListener("resize", this.boundResizeHandler);
|
|
160
|
+
if (this.resizeRaf) {
|
|
161
|
+
cancelAnimationFrame(this.resizeRaf);
|
|
162
|
+
this.resizeRaf = null;
|
|
163
|
+
}
|
|
164
|
+
this.container.innerHTML = "";
|
|
165
|
+
this.container.removeAttribute("style");
|
|
166
|
+
this.container.classList.remove(this.classNames.container);
|
|
167
|
+
this.items = [];
|
|
168
|
+
this.positions = [];
|
|
169
|
+
this.columnHeights = [];
|
|
170
|
+
this.columns = 0;
|
|
171
|
+
this.lastPositions = [];
|
|
172
|
+
}
|
|
232
173
|
};
|
|
233
174
|
|
|
234
175
|
// src/index.ts
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/MasonrySnapGridLayout.ts","../../src/index.ts"],"sourcesContent":["/**\r\n * MasonrySnapGridLayout\r\n * A performant masonry grid layout library with smooth animations,\r\n * customizable gutter, columns, and dynamic item content.\r\n *\r\n * Package name: masonry-snap-grid-layout\r\n */\r\n\r\nimport { MasonrySnapGridLayoutClassNames, MasonrySnapGridLayoutOptions } from \"./types\";\r\n\r\nexport default class MasonrySnapGridLayout {\r\n private readonly container: HTMLElement;\r\n private readonly options: Required<MasonrySnapGridLayoutOptions>;\r\n private classNames: Required<MasonrySnapGridLayoutClassNames>;\r\n\r\n private items: HTMLElement[] = [];\r\n private positions: { x: number; y: number; width: number; height: number }[] = [];\r\n private columnHeights: number[] = [];\r\n private columns: number = 0;\r\n private lastPositions: typeof this.positions = [];\r\n private resizeRaf: number | null = null;\r\n\r\n /**\r\n * Constructor initializes the layout with container element and options\r\n * @param container HTMLElement container where masonry items are rendered\r\n * @param options configuration options for layout and styling\r\n */\r\n constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions = {}) {\r\n this.container = container;\r\n\r\n // Set default options with overrides from user\r\n this.options = {\r\n gutter: 16,\r\n minColWidth: 250,\r\n animate: true,\r\n transitionDuration: 400,\r\n initialItems: 30,\r\n itemContent: null,\r\n classNames: {\r\n container: \"masonry-snap-grid-layout-container\",\r\n item: \"masonry-snap-grid-layout-item\",\r\n itemContent: \"item-content\",\r\n itemHeader: \"item-header\",\r\n itemTitle: \"item-title\",\r\n itemId: \"item-id\",\r\n itemBody: \"item-body\",\r\n progressBar: \"progress-bar\",\r\n progress: \"progress\",\r\n itemFooter: \"item-footer\",\r\n },\r\n ...options,\r\n };\r\n\r\n // Merge default classNames with user provided classNames\r\n this.classNames = {\r\n container: \"masonry-snap-grid-layout-container\",\r\n item: \"masonry-snap-grid-layout-item\",\r\n itemContent: \"item-content\",\r\n itemHeader: \"item-header\",\r\n itemTitle: \"item-title\",\r\n itemId: \"item-id\",\r\n itemBody: \"item-body\",\r\n progressBar: \"progress-bar\",\r\n progress: \"progress\",\r\n itemFooter: \"item-footer\",\r\n ...(this.options.classNames || {}),\r\n };\r\n\r\n // Add container class for styling\r\n this.container.classList.add(this.classNames.container);\r\n\r\n // Initialize the layout\r\n this.init();\r\n }\r\n\r\n /**\r\n * Initialization: set listeners, generate initial items, layout\r\n */\r\n private init() {\r\n this.setupEventListeners();\r\n this.generateItems(this.options.initialItems);\r\n this.calculateLayout();\r\n this.applyLayout(false);\r\n }\r\n\r\n /**\r\n * Setup event listeners for window resize and container resize observer\r\n */\r\n private setupEventListeners() {\r\n // Use ResizeObserver to handle container size changes\r\n const resizeObserver = new ResizeObserver(() => this.handleResize());\r\n resizeObserver.observe(this.container);\r\n\r\n window.addEventListener(\"resize\", () => this.handleResize());\r\n }\r\n\r\n /**\r\n * Generate specified number of items, removing excess if any\r\n * @param count number of items to generate\r\n */\r\n private generateItems(count: number) {\r\n // Remove extra items if reducing count\r\n if (count < this.items.length) {\r\n this.items.slice(count).forEach((item) => item.remove());\r\n this.items = this.items.slice(0, count);\r\n return;\r\n }\r\n\r\n const startIndex = this.items.length;\r\n const fragment = document.createDocumentFragment();\r\n\r\n for (let i = startIndex; i < count; i++) {\r\n const item = this.createItem(i);\r\n fragment.appendChild(item);\r\n this.items.push(item);\r\n }\r\n\r\n this.container.appendChild(fragment);\r\n }\r\n\r\n /**\r\n * Create a single masonry item HTMLElement, with content from renderItemContent callback or default template\r\n * @param index index of the item\r\n * @returns HTMLElement for the item\r\n */\r\n private createItem(index: number): HTMLElement {\r\n const div = document.createElement(\"div\");\r\n div.className = this.classNames.item;\r\n\r\n const contentOption = this.options.itemContent;\r\n\r\n // Determine content type: function, static HTMLElement, static string, or fallback to default\r\n let content: HTMLElement | string;\r\n\r\n if (typeof contentOption === \"function\") {\r\n // Call function with index to get content\r\n content = contentOption(index);\r\n } else if (contentOption instanceof HTMLElement || typeof contentOption === \"string\") {\r\n // Use static content\r\n content = contentOption;\r\n } else {\r\n // Fallback to default item content template string\r\n content = this.defaultItemContent(index);\r\n }\r\n\r\n // Insert content into item element\r\n if (typeof content === \"string\") {\r\n div.innerHTML = content;\r\n } else {\r\n div.appendChild(content);\r\n }\r\n\r\n // Random height to simulate masonry effect\r\n const height = 120 + Math.floor(Math.random() * 180);\r\n div.style.height = `${height}px`;\r\n\r\n // Color with HSL for distinct appearance\r\n const hue = (index * 137.508) % 360; // golden angle\r\n div.style.background = `linear-gradient(135deg, hsl(${hue}, 70%, 60%), hsl(${hue + 40}, 70%, 50%))`;\r\n\r\n return div;\r\n }\r\n\r\n /**\r\n * Default item content template string\r\n * @param index index of the item\r\n * @returns string HTML template for item content\r\n */\r\n private defaultItemContent(index: number): string {\r\n return `\r\n <div class=\"${this.classNames.itemContent}\">\r\n <div class=\"${this.classNames.itemHeader}\">\r\n <div class=\"${this.classNames.itemTitle}\">Item ${index + 1}</div>\r\n <div class=\"${this.classNames.itemId}\">#${index + 1}</div>\r\n </div>\r\n <div class=\"${this.classNames.itemBody}\">\r\n <div class=\"${this.classNames.progressBar}\">\r\n <div class=\"${this.classNames.progress}\"></div>\r\n </div>\r\n </div>\r\n <div class=\"${this.classNames.itemFooter}\">\r\n ${this.getRandomEmoji()}\r\n </div>\r\n </div>\r\n `;\r\n }\r\n\r\n /**\r\n * Returns a random emoji from a fixed set for item footer\r\n */\r\n private getRandomEmoji(): string {\r\n const emojis = [\"🚀\", \"✨\", \"🔥\", \"💡\", \"🌟\", \"🎯\", \"⚡\", \"💻\", \"🔧\", \"📊\"];\r\n return emojis[Math.floor(Math.random() * emojis.length)];\r\n }\r\n\r\n /**\r\n * Calculate positions of each item in masonry grid based on container width, gutter, min column width\r\n */\r\n private calculateLayout() {\r\n const { gutter, minColWidth } = this.options;\r\n const containerWidth = this.container.clientWidth;\r\n\r\n // Calculate number of columns that fit\r\n this.columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));\r\n\r\n // Calculate each column width\r\n const colWidth = (containerWidth - (this.columns - 1) * gutter) / this.columns;\r\n\r\n // Store previous positions for animation\r\n this.lastPositions = [...this.positions];\r\n\r\n // Reset column heights array\r\n this.columnHeights = new Array(this.columns).fill(0);\r\n this.positions = [];\r\n\r\n // Calculate position for each item and assign column\r\n this.items.forEach((item, i) => {\r\n const height = item.offsetHeight;\r\n\r\n // Find the shortest column index\r\n let minCol = 0;\r\n for (let c = 1; c < this.columns; c++) {\r\n if (this.columnHeights[c] < this.columnHeights[minCol]) {\r\n minCol = c;\r\n }\r\n }\r\n\r\n // Calculate item's x,y position\r\n const x = minCol * (colWidth + gutter);\r\n const y = this.columnHeights[minCol];\r\n\r\n // Save calculated position\r\n this.positions[i] = { x, y, width: colWidth, height };\r\n\r\n // Update column height to include this item + gutter\r\n this.columnHeights[minCol] += height + gutter;\r\n });\r\n\r\n // Set container height to max column height for proper scrolling\r\n const maxHeight = Math.max(...this.columnHeights);\r\n this.container.style.height = `${maxHeight}px`;\r\n }\r\n\r\n /**\r\n * Apply calculated positions to each item with optional animation\r\n * @param animate whether to animate layout changes (default false)\r\n */\r\n private applyLayout(animate: boolean = false) {\r\n const duration = this.options.transitionDuration;\r\n\r\n this.items.forEach((item, i) => {\r\n const pos = this.positions[i] || { x: 0, y: 0, width: 0 };\r\n const lastPos = this.lastPositions[i] || { x: 0, y: 0 };\r\n\r\n // Set item width for responsive columns\r\n item.style.width = `${pos.width}px`;\r\n\r\n if (animate) {\r\n // Calculate differences for smooth animation\r\n const dx = lastPos.x - pos.x;\r\n const dy = lastPos.y - pos.y;\r\n\r\n // Apply initial transform to old position (without transition)\r\n item.style.transition = \"none\";\r\n item.style.transform = `translate3d(${pos.x + dx}px, ${pos.y + dy}px, 0)`;\r\n\r\n // Trigger reflow to apply the style immediately\r\n void item.offsetHeight;\r\n\r\n // Animate transform to new position\r\n item.style.transition = `transform ${duration}ms cubic-bezier(0.4, 0, 0.2, 1)`;\r\n item.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;\r\n } else {\r\n // Directly set transform without animation\r\n item.style.transition = \"none\";\r\n item.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Handle container resize using RAF to optimize performance\r\n */\r\n private handleResize() {\r\n if (this.resizeRaf) cancelAnimationFrame(this.resizeRaf);\r\n this.resizeRaf = requestAnimationFrame(() => {\r\n this.calculateLayout();\r\n this.applyLayout(true);\r\n });\r\n }\r\n\r\n /**\r\n * Shuffle items randomly and reapply layout with animation\r\n */\r\n public shuffleItems() {\r\n // Fisher-Yates shuffle algorithm\r\n for (let i = this.items.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1));\r\n [this.items[i], this.items[j]] = [this.items[j], this.items[i]];\r\n }\r\n\r\n // Re-append items in new order in DOM\r\n const fragment = document.createDocumentFragment();\r\n this.items.forEach((item) => fragment.appendChild(item));\r\n this.container.appendChild(fragment);\r\n\r\n // Update layout positions and animate\r\n this.calculateLayout();\r\n this.applyLayout(true);\r\n }\r\n\r\n /**\r\n * Add more items dynamically\r\n * @param count number of items to add\r\n */\r\n public addItems(count: number) {\r\n const newCount = this.items.length + count;\r\n this.generateItems(newCount);\r\n this.calculateLayout();\r\n this.applyLayout(true);\r\n }\r\n}\r\n","import './masonry-snap-grid-layout.css';\r\nimport MasonrySnapGridLayout from './MasonrySnapGridLayout';\r\n\r\nconst container = document.getElementById('masonry');\r\nif (container) {\r\n const masonry = new MasonrySnapGridLayout(container, {\r\n gutter: 20,\r\n minColWidth: 200,\r\n animate: true,\r\n initialItems: 40,\r\n\r\n // Custom item content example\r\n itemContent: (index) => {\r\n const div = document.createElement('div');\r\n div.textContent = `Custom Item ${index + 1}`;\r\n div.style.color = '#fff';\r\n div.style.fontWeight = 'bold';\r\n div.style.textAlign = 'center';\r\n div.style.padding = '1rem';\r\n return div;\r\n },\r\n\r\n classNames: {\r\n container: 'masonry-snap-grid-layout-container',\r\n item: 'masonry-snap-grid-layout-item',\r\n },\r\n });\r\n\r\n // Example to update count later\r\n masonry.addItems(5);\r\n\r\n // Example to shuffle items\r\n masonry.shuffleItems();\r\n}\r\n\r\nexport default MasonrySnapGridLayout;\r\nexport { MasonrySnapGridLayout };"],"mappings":"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","names":["container"]}
|
|
1
|
+
{"version":3,"sources":["../../src/MasonrySnapGridLayout.ts","../../src/index.ts"],"sourcesContent":["import { MasonrySnapGridLayoutClassNames, MasonrySnapGridLayoutOptions } from './types';\r\n\r\nexport default class MasonrySnapGridLayout {\r\n private readonly container: HTMLElement;\r\n private readonly options: Required<MasonrySnapGridLayoutOptions>;\r\n private classNames: Required<MasonrySnapGridLayoutClassNames>;\r\n\r\n private items: HTMLElement[] = [];\r\n private positions: { x: number; y: number; width: number; height: number }[] = [];\r\n private columnHeights: number[] = [];\r\n private columns: number = 0;\r\n private lastPositions: typeof this.positions = [];\r\n private resizeRaf: number | null = null;\r\n\r\n private resizeObserver: ResizeObserver | null = null;\r\n private boundResizeHandler = this.handleResize.bind(this);\r\n\r\n constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions = {}) {\r\n this.container = container;\r\n this.classNames = {\r\n container: 'masonry-snap-grid-container',\r\n item: 'masonry-snap-grid-item',\r\n itemContent: 'masonry-snap-grid-item-content',\r\n itemHeader: 'masonry-snap-grid-item-header',\r\n itemTitle: 'masonry-snap-grid-item-title',\r\n itemId: 'masonry-snap-grid-item-id',\r\n itemBody: 'masonry-snap-grid-item-body',\r\n progressBar: 'masonry-snap-grid-progress-bar',\r\n progress: 'masonry-snap-grid-progress',\r\n itemFooter: 'masonry-snap-grid-item-footer',\r\n ...(options.classNames || {}),\r\n }\r\n this.options = {\r\n gutter: 16,\r\n minColWidth: 250,\r\n animate: true,\r\n transitionDuration: 400,\r\n initialItems: 0,\r\n itemContent: null,\r\n classNames: this.classNames,\r\n ...options,\r\n };\r\n\r\n\r\n this.container.classList.add(this.classNames.container);\r\n this.init();\r\n }\r\n\r\n private init() {\r\n this.setupEventListeners();\r\n this.generateItems(this.options.initialItems);\r\n this.calculateLayout();\r\n this.applyLayout(false);\r\n }\r\n\r\n private setupEventListeners() {\r\n this.resizeObserver = new ResizeObserver(() => this.handleResize());\r\n this.resizeObserver.observe(this.container);\r\n\r\n window.addEventListener('resize', this.boundResizeHandler);\r\n }\r\n\r\n public generateItems(count: number) {\r\n if (count < this.items.length) {\r\n this.items.slice(count).forEach((item) => item.remove());\r\n this.items = this.items.slice(0, count);\r\n return;\r\n }\r\n\r\n const startIndex = this.items.length;\r\n const fragment = document.createDocumentFragment();\r\n\r\n for (let i = startIndex; i < count; i++) {\r\n const item = this.createItem(i);\r\n fragment.appendChild(item);\r\n this.items.push(item);\r\n }\r\n\r\n this.container.appendChild(fragment);\r\n }\r\n\r\n private createItem(index: number): HTMLElement {\r\n const div = document.createElement('div');\r\n div.className = this.classNames.item;\r\n\r\n const contentOption = this.options.itemContent;\r\n let content: HTMLElement | string;\r\n\r\n if (typeof contentOption === 'function') {\r\n content = contentOption(index);\r\n } else if (contentOption instanceof HTMLElement || typeof contentOption === 'string') {\r\n content = contentOption;\r\n } else {\r\n content = `Item ${index + 1}`;\r\n }\r\n\r\n if (typeof content === 'string') {\r\n div.textContent = content;\r\n } else {\r\n div.appendChild(content);\r\n }\r\n\r\n // Assigning height dynamically for layout simulation (no styles applied)\r\n const height = 120 + Math.floor(Math.random() * 100);\r\n div.style.height = `${height}px`;\r\n\r\n return div;\r\n }\r\n\r\n public calculateLayout() {\r\n const { gutter, minColWidth } = this.options;\r\n const containerWidth = this.container.clientWidth;\r\n\r\n this.columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));\r\n const colWidth = (containerWidth - (this.columns - 1) * gutter) / this.columns;\r\n\r\n this.lastPositions = [...this.positions];\r\n this.columnHeights = new Array(this.columns).fill(0);\r\n this.positions = [];\r\n\r\n this.items.forEach((item, i) => {\r\n const height = item.offsetHeight;\r\n\r\n let minCol = 0;\r\n for (let c = 1; c < this.columns; c++) {\r\n if (this.columnHeights[c] < this.columnHeights[minCol]) {\r\n minCol = c;\r\n }\r\n }\r\n\r\n const x = minCol * (colWidth + gutter);\r\n const y = this.columnHeights[minCol];\r\n\r\n this.positions[i] = { x, y, width: colWidth, height };\r\n this.columnHeights[minCol] += height + gutter;\r\n });\r\n\r\n const maxHeight = Math.max(...this.columnHeights);\r\n this.container.style.height = `${maxHeight}px`;\r\n }\r\n\r\n applyLayout(animate: boolean = false) {\r\n const duration = this.options.transitionDuration;\r\n\r\n this.items.forEach((item, i) => {\r\n const pos = this.positions[i] || { x: 0, y: 0, width: 0 };\r\n const lastPos = this.lastPositions[i] || { x: 0, y: 0 };\r\n\r\n item.style.width = `${pos.width}px`;\r\n\r\n if (animate) {\r\n const dx = lastPos.x - pos.x;\r\n const dy = lastPos.y - pos.y;\r\n\r\n item.style.transition = 'none';\r\n item.style.transform = `translate3d(${pos.x + dx}px, ${pos.y + dy}px, 0)`;\r\n void item.offsetHeight;\r\n\r\n item.style.transition = `transform ${duration}ms ease`;\r\n item.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;\r\n } else {\r\n item.style.transition = 'none';\r\n item.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;\r\n }\r\n });\r\n }\r\n\r\n private handleResize() {\r\n if (this.resizeRaf) cancelAnimationFrame(this.resizeRaf);\r\n this.resizeRaf = requestAnimationFrame(() => {\r\n this.calculateLayout();\r\n this.applyLayout(true);\r\n });\r\n }\r\n\r\n public shuffleItems() {\r\n for (let i = this.items.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1));\r\n [this.items[i], this.items[j]] = [this.items[j], this.items[i]];\r\n }\r\n\r\n const fragment = document.createDocumentFragment();\r\n this.items.forEach((item) => fragment.appendChild(item));\r\n this.container.appendChild(fragment);\r\n\r\n this.calculateLayout();\r\n this.applyLayout(true);\r\n }\r\n\r\n public addItems(count: number) {\r\n const newCount = this.items.length + count;\r\n this.generateItems(newCount);\r\n this.calculateLayout();\r\n this.applyLayout(true);\r\n }\r\n\r\n public destroy(): void {\r\n if (this.resizeObserver) {\r\n this.resizeObserver.disconnect();\r\n this.resizeObserver = null;\r\n }\r\n\r\n window.removeEventListener('resize', this.boundResizeHandler);\r\n\r\n if (this.resizeRaf) {\r\n cancelAnimationFrame(this.resizeRaf);\r\n this.resizeRaf = null;\r\n }\r\n\r\n this.container.innerHTML = '';\r\n this.container.removeAttribute('style');\r\n this.container.classList.remove(this.classNames.container);\r\n\r\n this.items = [];\r\n this.positions = [];\r\n this.columnHeights = [];\r\n this.columns = 0;\r\n this.lastPositions = [];\r\n }\r\n}","import './masonry-snap-grid-layout.css';\r\nimport MasonrySnapGridLayout from './MasonrySnapGridLayout';\r\nimport {MasonrySnapGridLayoutOptions} from './types';\r\n\r\nconst container = document.getElementById('masonry');\r\nif (container) {\r\n const masonry = new MasonrySnapGridLayout(container, {\r\n gutter: 20,\r\n minColWidth: 200,\r\n animate: true,\r\n initialItems: 40,\r\n\r\n // Custom item content example\r\n itemContent: (index) => {\r\n const div = document.createElement('div');\r\n div.textContent = `Custom Item ${index + 1}`;\r\n div.style.color = '#fff';\r\n div.style.fontWeight = 'bold';\r\n div.style.textAlign = 'center';\r\n div.style.padding = '1rem';\r\n return div;\r\n },\r\n\r\n classNames: {\r\n container: 'masonry-snap-grid-layout-container',\r\n item: 'masonry-snap-grid-layout-item',\r\n },\r\n });\r\n\r\n // Example to update count later\r\n masonry.addItems(5);\r\n\r\n // Example to shuffle items\r\n masonry.shuffleItems();\r\n}\r\n\r\nexport default MasonrySnapGridLayout;\r\nexport { MasonrySnapGridLayout, MasonrySnapGridLayoutOptions };"],"mappings":"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","names":["container"]}
|