masonry-snap-grid-layout 1.0.3 → 1.0.4
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/dist/MasonrySnapGridLayout-D7ctVJaF.d.cts +34 -0
- package/dist/MasonrySnapGridLayout-D7ctVJaF.d.ts +34 -0
- package/dist/esm/index.css +17 -0
- package/dist/esm/index.css.map +1 -0
- package/dist/esm/index.js +84 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/react.js +141 -0
- package/dist/esm/react.js.map +1 -0
- package/dist/index.css +17 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +1 -0
- package/dist/react.d.cts +13 -0
- package/dist/react.d.ts +13 -0
- package/dist/react.js +167 -0
- package/dist/react.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
interface MasonrySnapGridLayoutClassNames {
|
|
2
|
+
container?: string;
|
|
3
|
+
item?: string;
|
|
4
|
+
}
|
|
5
|
+
interface MasonrySnapGridLayoutOptions<T = unknown> {
|
|
6
|
+
gutter?: number;
|
|
7
|
+
minColWidth?: number;
|
|
8
|
+
animate?: boolean;
|
|
9
|
+
transitionDuration?: number;
|
|
10
|
+
items: T[];
|
|
11
|
+
renderItem: (item: T) => HTMLElement;
|
|
12
|
+
classNames?: MasonrySnapGridLayoutClassNames;
|
|
13
|
+
}
|
|
14
|
+
interface MasonrySnapGridRef {
|
|
15
|
+
layout: MasonrySnapGridLayout | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare class MasonrySnapGridLayout<T = any> {
|
|
19
|
+
private readonly container;
|
|
20
|
+
private readonly options;
|
|
21
|
+
private items;
|
|
22
|
+
private columnHeights;
|
|
23
|
+
private resizeObserver;
|
|
24
|
+
private rafId;
|
|
25
|
+
constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>);
|
|
26
|
+
private renderItems;
|
|
27
|
+
private setupResizeObserver;
|
|
28
|
+
private updateLayout;
|
|
29
|
+
private findShortestColumn;
|
|
30
|
+
updateItems(newItems: T[]): void;
|
|
31
|
+
destroy(): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { MasonrySnapGridLayout as M, type MasonrySnapGridLayoutOptions as a, type MasonrySnapGridRef as b };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
interface MasonrySnapGridLayoutClassNames {
|
|
2
|
+
container?: string;
|
|
3
|
+
item?: string;
|
|
4
|
+
}
|
|
5
|
+
interface MasonrySnapGridLayoutOptions<T = unknown> {
|
|
6
|
+
gutter?: number;
|
|
7
|
+
minColWidth?: number;
|
|
8
|
+
animate?: boolean;
|
|
9
|
+
transitionDuration?: number;
|
|
10
|
+
items: T[];
|
|
11
|
+
renderItem: (item: T) => HTMLElement;
|
|
12
|
+
classNames?: MasonrySnapGridLayoutClassNames;
|
|
13
|
+
}
|
|
14
|
+
interface MasonrySnapGridRef {
|
|
15
|
+
layout: MasonrySnapGridLayout | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare class MasonrySnapGridLayout<T = any> {
|
|
19
|
+
private readonly container;
|
|
20
|
+
private readonly options;
|
|
21
|
+
private items;
|
|
22
|
+
private columnHeights;
|
|
23
|
+
private resizeObserver;
|
|
24
|
+
private rafId;
|
|
25
|
+
constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>);
|
|
26
|
+
private renderItems;
|
|
27
|
+
private setupResizeObserver;
|
|
28
|
+
private updateLayout;
|
|
29
|
+
private findShortestColumn;
|
|
30
|
+
updateItems(newItems: T[]): void;
|
|
31
|
+
destroy(): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { MasonrySnapGridLayout as M, type MasonrySnapGridLayoutOptions as a, type MasonrySnapGridRef as b };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* src/index.css */
|
|
2
|
+
.masonry-snap-grid-container {
|
|
3
|
+
position: relative;
|
|
4
|
+
width: 100%;
|
|
5
|
+
transition: height 0.4s ease-out;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
}
|
|
8
|
+
.masonry-snap-grid-item {
|
|
9
|
+
position: absolute;
|
|
10
|
+
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
11
|
+
will-change: transform;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
transform-origin: top left;
|
|
14
|
+
backface-visibility: hidden;
|
|
15
|
+
perspective: 1000px;
|
|
16
|
+
}
|
|
17
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/index.css"],"sourcesContent":[".masonry-snap-grid-container {\r\n position: relative;\r\n width: 100%;\r\n transition: height 0.4s ease-out;\r\n overflow: hidden;\r\n}\r\n\r\n.masonry-snap-grid-item {\r\n position: absolute;\r\n transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);\r\n will-change: transform;\r\n box-sizing: border-box;\r\n transform-origin: top left;\r\n backface-visibility: hidden;\r\n perspective: 1000px;\r\n}"],"mappings":";AAAA,CAAC;AACG,YAAU;AACV,SAAO;AACP,cAAY,OAAO,KAAK;AACxB,YAAU;AACd;AAEA,CAAC;AACG,YAAU;AACV,cAAY,UAAU,KAAK,aAAa,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE;AACrD,eAAa;AACb,cAAY;AACZ,oBAAkB,IAAI;AACtB,uBAAqB;AACrB,eAAa;AACjB;","names":[]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// src/MasonrySnapGridLayout.ts
|
|
2
|
+
var MasonrySnapGridLayout = class {
|
|
3
|
+
constructor(container, options) {
|
|
4
|
+
this.items = [];
|
|
5
|
+
this.columnHeights = [];
|
|
6
|
+
this.rafId = null;
|
|
7
|
+
this.container = container;
|
|
8
|
+
this.options = {
|
|
9
|
+
gutter: 16,
|
|
10
|
+
minColWidth: 250,
|
|
11
|
+
animate: true,
|
|
12
|
+
transitionDuration: 400,
|
|
13
|
+
classNames: {
|
|
14
|
+
container: "masonry-snap-grid-container",
|
|
15
|
+
item: "masonry-snap-grid-item"
|
|
16
|
+
},
|
|
17
|
+
...options
|
|
18
|
+
};
|
|
19
|
+
this.container.classList.add(this.options.classNames.container || "");
|
|
20
|
+
this.renderItems();
|
|
21
|
+
this.setupResizeObserver();
|
|
22
|
+
}
|
|
23
|
+
renderItems() {
|
|
24
|
+
this.items.forEach((item) => item.remove());
|
|
25
|
+
this.items = [];
|
|
26
|
+
const fragment = document.createDocumentFragment();
|
|
27
|
+
this.options.items.forEach((itemData) => {
|
|
28
|
+
const itemElement = this.options.renderItem(itemData);
|
|
29
|
+
itemElement.classList.add(this.options.classNames.item || "");
|
|
30
|
+
fragment.appendChild(itemElement);
|
|
31
|
+
this.items.push(itemElement);
|
|
32
|
+
});
|
|
33
|
+
this.container.appendChild(fragment);
|
|
34
|
+
this.updateLayout();
|
|
35
|
+
}
|
|
36
|
+
setupResizeObserver() {
|
|
37
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
38
|
+
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
39
|
+
this.rafId = requestAnimationFrame(() => this.updateLayout());
|
|
40
|
+
});
|
|
41
|
+
this.resizeObserver.observe(this.container);
|
|
42
|
+
}
|
|
43
|
+
updateLayout() {
|
|
44
|
+
const { gutter, minColWidth, animate, transitionDuration } = this.options;
|
|
45
|
+
const containerWidth = this.container.clientWidth;
|
|
46
|
+
const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));
|
|
47
|
+
const colWidth = (containerWidth - (columns - 1) * gutter) / columns;
|
|
48
|
+
this.columnHeights = new Array(columns).fill(0);
|
|
49
|
+
this.items.forEach((item) => {
|
|
50
|
+
const height = item.offsetHeight;
|
|
51
|
+
const minCol = this.findShortestColumn();
|
|
52
|
+
const x = minCol * (colWidth + gutter);
|
|
53
|
+
const y = this.columnHeights[minCol];
|
|
54
|
+
item.style.width = `${colWidth}px`;
|
|
55
|
+
item.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
|
56
|
+
item.style.transition = animate ? `transform ${transitionDuration}ms ease` : "none";
|
|
57
|
+
this.columnHeights[minCol] += height + gutter;
|
|
58
|
+
});
|
|
59
|
+
const maxHeight = Math.max(...this.columnHeights);
|
|
60
|
+
this.container.style.height = `${maxHeight}px`;
|
|
61
|
+
}
|
|
62
|
+
findShortestColumn() {
|
|
63
|
+
return this.columnHeights.indexOf(Math.min(...this.columnHeights));
|
|
64
|
+
}
|
|
65
|
+
updateItems(newItems) {
|
|
66
|
+
this.options.items = newItems;
|
|
67
|
+
this.renderItems();
|
|
68
|
+
}
|
|
69
|
+
destroy() {
|
|
70
|
+
this.resizeObserver?.disconnect();
|
|
71
|
+
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
72
|
+
this.container.innerHTML = "";
|
|
73
|
+
this.container.removeAttribute("style");
|
|
74
|
+
this.container.classList.remove(this.options.classNames.container || "");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/index.ts
|
|
79
|
+
var index_default = MasonrySnapGridLayout;
|
|
80
|
+
export {
|
|
81
|
+
MasonrySnapGridLayout,
|
|
82
|
+
index_default as default
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/MasonrySnapGridLayout.ts","../../src/index.ts"],"sourcesContent":["import { MasonrySnapGridLayoutOptions } from './types';\r\n\r\nexport default class MasonrySnapGridLayout<T = any> {\r\n private readonly container: HTMLElement;\r\n private readonly options: Required<MasonrySnapGridLayoutOptions<T>>;\r\n private items: HTMLElement[] = [];\r\n private columnHeights: number[] = [];\r\n private resizeObserver: ResizeObserver | undefined;\r\n private rafId: number | null = null;\r\n\r\n constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>) {\r\n this.container = container;\r\n this.options = {\r\n gutter: 16,\r\n minColWidth: 250,\r\n animate: true,\r\n transitionDuration: 400,\r\n classNames: {\r\n container: 'masonry-snap-grid-container',\r\n item: 'masonry-snap-grid-item',\r\n },\r\n ...options,\r\n };\r\n\r\n // Apply container class\r\n this.container.classList.add(this.options.classNames.container || \"\");\r\n\r\n // Initialize\r\n this.renderItems();\r\n this.setupResizeObserver();\r\n }\r\n\r\n private renderItems() {\r\n // Clear existing items\r\n this.items.forEach(item => item.remove());\r\n this.items = [];\r\n\r\n // Create fragment for batch DOM insertion\r\n const fragment = document.createDocumentFragment();\r\n\r\n // Create and append items\r\n this.options.items.forEach(itemData => {\r\n const itemElement = this.options.renderItem(itemData);\r\n itemElement.classList.add(this.options.classNames.item|| \"\");\r\n fragment.appendChild(itemElement);\r\n this.items.push(itemElement);\r\n });\r\n\r\n this.container.appendChild(fragment);\r\n this.updateLayout();\r\n }\r\n\r\n private setupResizeObserver() {\r\n this.resizeObserver = new ResizeObserver(() => {\r\n if (this.rafId) cancelAnimationFrame(this.rafId);\r\n this.rafId = requestAnimationFrame(() => this.updateLayout());\r\n });\r\n this.resizeObserver.observe(this.container);\r\n }\r\n\r\n private updateLayout() {\r\n const { gutter, minColWidth, animate, transitionDuration } = this.options;\r\n const containerWidth = this.container.clientWidth;\r\n\r\n // Calculate columns\r\n const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));\r\n const colWidth = (containerWidth - (columns - 1) * gutter) / columns;\r\n\r\n // Reset column heights\r\n this.columnHeights = new Array(columns).fill(0);\r\n\r\n // Position items\r\n this.items.forEach((item) => {\r\n const height = item.offsetHeight;\r\n const minCol = this.findShortestColumn();\r\n const x = minCol * (colWidth + gutter);\r\n const y = this.columnHeights[minCol];\r\n\r\n // Apply position and size\r\n item.style.width = `${colWidth}px`;\r\n item.style.transform = `translate3d(${x}px, ${y}px, 0)`;\r\n item.style.transition = animate\r\n ? `transform ${transitionDuration}ms ease`\r\n : 'none';\r\n\r\n // Update column height\r\n this.columnHeights[minCol] += height + gutter;\r\n });\r\n\r\n // Set container height\r\n const maxHeight = Math.max(...this.columnHeights);\r\n this.container.style.height = `${maxHeight}px`;\r\n }\r\n\r\n private findShortestColumn(): number {\r\n return this.columnHeights.indexOf(Math.min(...this.columnHeights));\r\n }\r\n\r\n public updateItems(newItems: T[]) {\r\n this.options.items = newItems;\r\n this.renderItems();\r\n }\r\n\r\n public destroy() {\r\n this.resizeObserver?.disconnect();\r\n if (this.rafId) cancelAnimationFrame(this.rafId);\r\n this.container.innerHTML = '';\r\n this.container.removeAttribute('style');\r\n this.container.classList.remove(this.options.classNames.container || \"\");\r\n }\r\n}","import './index.css';\r\nimport MasonrySnapGridLayout from './MasonrySnapGridLayout';\r\nimport {MasonrySnapGridLayoutOptions} from './types';\r\n\r\nexport default MasonrySnapGridLayout;\r\nexport { MasonrySnapGridLayout, MasonrySnapGridLayoutOptions };"],"mappings":";AAEA,IAAqB,wBAArB,MAAoD;AAAA,EAQhD,YAAY,WAAwB,SAA0C;AAL9E,SAAQ,QAAuB,CAAC;AAChC,SAAQ,gBAA0B,CAAC;AAEnC,SAAQ,QAAuB;AAG3B,SAAK,YAAY;AACjB,SAAK,UAAU;AAAA,MACX,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,SAAS;AAAA,MACT,oBAAoB;AAAA,MACpB,YAAY;AAAA,QACR,WAAW;AAAA,QACX,MAAM;AAAA,MACV;AAAA,MACA,GAAG;AAAA,IACP;AAGA,SAAK,UAAU,UAAU,IAAI,KAAK,QAAQ,WAAW,aAAa,EAAE;AAGpE,SAAK,YAAY;AACjB,SAAK,oBAAoB;AAAA,EAC7B;AAAA,EAEQ,cAAc;AAElB,SAAK,MAAM,QAAQ,UAAQ,KAAK,OAAO,CAAC;AACxC,SAAK,QAAQ,CAAC;AAGd,UAAM,WAAW,SAAS,uBAAuB;AAGjD,SAAK,QAAQ,MAAM,QAAQ,cAAY;AACnC,YAAM,cAAc,KAAK,QAAQ,WAAW,QAAQ;AACpD,kBAAY,UAAU,IAAI,KAAK,QAAQ,WAAW,QAAO,EAAE;AAC3D,eAAS,YAAY,WAAW;AAChC,WAAK,MAAM,KAAK,WAAW;AAAA,IAC/B,CAAC;AAED,SAAK,UAAU,YAAY,QAAQ;AACnC,SAAK,aAAa;AAAA,EACtB;AAAA,EAEQ,sBAAsB;AAC1B,SAAK,iBAAiB,IAAI,eAAe,MAAM;AAC3C,UAAI,KAAK,MAAO,sBAAqB,KAAK,KAAK;AAC/C,WAAK,QAAQ,sBAAsB,MAAM,KAAK,aAAa,CAAC;AAAA,IAChE,CAAC;AACD,SAAK,eAAe,QAAQ,KAAK,SAAS;AAAA,EAC9C;AAAA,EAEQ,eAAe;AACnB,UAAM,EAAE,QAAQ,aAAa,SAAS,mBAAmB,IAAI,KAAK;AAClE,UAAM,iBAAiB,KAAK,UAAU;AAGtC,UAAM,UAAU,KAAK,IAAI,GAAG,KAAK,OAAO,iBAAiB,WAAW,cAAc,OAAO,CAAC;AAC1F,UAAM,YAAY,kBAAkB,UAAU,KAAK,UAAU;AAG7D,SAAK,gBAAgB,IAAI,MAAM,OAAO,EAAE,KAAK,CAAC;AAG9C,SAAK,MAAM,QAAQ,CAAC,SAAS;AACzB,YAAM,SAAS,KAAK;AACpB,YAAM,SAAS,KAAK,mBAAmB;AACvC,YAAM,IAAI,UAAU,WAAW;AAC/B,YAAM,IAAI,KAAK,cAAc,MAAM;AAGnC,WAAK,MAAM,QAAQ,GAAG,QAAQ;AAC9B,WAAK,MAAM,YAAY,eAAe,CAAC,OAAO,CAAC;AAC/C,WAAK,MAAM,aAAa,UAClB,aAAa,kBAAkB,YAC/B;AAGN,WAAK,cAAc,MAAM,KAAK,SAAS;AAAA,IAC3C,CAAC;AAGD,UAAM,YAAY,KAAK,IAAI,GAAG,KAAK,aAAa;AAChD,SAAK,UAAU,MAAM,SAAS,GAAG,SAAS;AAAA,EAC9C;AAAA,EAEQ,qBAA6B;AACjC,WAAO,KAAK,cAAc,QAAQ,KAAK,IAAI,GAAG,KAAK,aAAa,CAAC;AAAA,EACrE;AAAA,EAEO,YAAY,UAAe;AAC9B,SAAK,QAAQ,QAAQ;AACrB,SAAK,YAAY;AAAA,EACrB;AAAA,EAEO,UAAU;AACb,SAAK,gBAAgB,WAAW;AAChC,QAAI,KAAK,MAAO,sBAAqB,KAAK,KAAK;AAC/C,SAAK,UAAU,YAAY;AAC3B,SAAK,UAAU,gBAAgB,OAAO;AACtC,SAAK,UAAU,UAAU,OAAO,KAAK,QAAQ,WAAW,aAAa,EAAE;AAAA,EAC3E;AACJ;;;AC1GA,IAAO,gBAAQ;","names":[]}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// src/react.tsx
|
|
2
|
+
import {
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
forwardRef,
|
|
6
|
+
useImperativeHandle
|
|
7
|
+
} from "react";
|
|
8
|
+
import ReactDOM from "react-dom/client";
|
|
9
|
+
|
|
10
|
+
// src/MasonrySnapGridLayout.ts
|
|
11
|
+
var MasonrySnapGridLayout = class {
|
|
12
|
+
constructor(container, options) {
|
|
13
|
+
this.items = [];
|
|
14
|
+
this.columnHeights = [];
|
|
15
|
+
this.rafId = null;
|
|
16
|
+
this.container = container;
|
|
17
|
+
this.options = {
|
|
18
|
+
gutter: 16,
|
|
19
|
+
minColWidth: 250,
|
|
20
|
+
animate: true,
|
|
21
|
+
transitionDuration: 400,
|
|
22
|
+
classNames: {
|
|
23
|
+
container: "masonry-snap-grid-container",
|
|
24
|
+
item: "masonry-snap-grid-item"
|
|
25
|
+
},
|
|
26
|
+
...options
|
|
27
|
+
};
|
|
28
|
+
this.container.classList.add(this.options.classNames.container || "");
|
|
29
|
+
this.renderItems();
|
|
30
|
+
this.setupResizeObserver();
|
|
31
|
+
}
|
|
32
|
+
renderItems() {
|
|
33
|
+
this.items.forEach((item) => item.remove());
|
|
34
|
+
this.items = [];
|
|
35
|
+
const fragment = document.createDocumentFragment();
|
|
36
|
+
this.options.items.forEach((itemData) => {
|
|
37
|
+
const itemElement = this.options.renderItem(itemData);
|
|
38
|
+
itemElement.classList.add(this.options.classNames.item || "");
|
|
39
|
+
fragment.appendChild(itemElement);
|
|
40
|
+
this.items.push(itemElement);
|
|
41
|
+
});
|
|
42
|
+
this.container.appendChild(fragment);
|
|
43
|
+
this.updateLayout();
|
|
44
|
+
}
|
|
45
|
+
setupResizeObserver() {
|
|
46
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
47
|
+
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
48
|
+
this.rafId = requestAnimationFrame(() => this.updateLayout());
|
|
49
|
+
});
|
|
50
|
+
this.resizeObserver.observe(this.container);
|
|
51
|
+
}
|
|
52
|
+
updateLayout() {
|
|
53
|
+
const { gutter, minColWidth, animate, transitionDuration } = this.options;
|
|
54
|
+
const containerWidth = this.container.clientWidth;
|
|
55
|
+
const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));
|
|
56
|
+
const colWidth = (containerWidth - (columns - 1) * gutter) / columns;
|
|
57
|
+
this.columnHeights = new Array(columns).fill(0);
|
|
58
|
+
this.items.forEach((item) => {
|
|
59
|
+
const height = item.offsetHeight;
|
|
60
|
+
const minCol = this.findShortestColumn();
|
|
61
|
+
const x = minCol * (colWidth + gutter);
|
|
62
|
+
const y = this.columnHeights[minCol];
|
|
63
|
+
item.style.width = `${colWidth}px`;
|
|
64
|
+
item.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
|
65
|
+
item.style.transition = animate ? `transform ${transitionDuration}ms ease` : "none";
|
|
66
|
+
this.columnHeights[minCol] += height + gutter;
|
|
67
|
+
});
|
|
68
|
+
const maxHeight = Math.max(...this.columnHeights);
|
|
69
|
+
this.container.style.height = `${maxHeight}px`;
|
|
70
|
+
}
|
|
71
|
+
findShortestColumn() {
|
|
72
|
+
return this.columnHeights.indexOf(Math.min(...this.columnHeights));
|
|
73
|
+
}
|
|
74
|
+
updateItems(newItems) {
|
|
75
|
+
this.options.items = newItems;
|
|
76
|
+
this.renderItems();
|
|
77
|
+
}
|
|
78
|
+
destroy() {
|
|
79
|
+
this.resizeObserver?.disconnect();
|
|
80
|
+
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
81
|
+
this.container.innerHTML = "";
|
|
82
|
+
this.container.removeAttribute("style");
|
|
83
|
+
this.container.classList.remove(this.options.classNames.container || "");
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// src/react.tsx
|
|
88
|
+
import { jsx } from "react/jsx-runtime";
|
|
89
|
+
function MasonrySnapGridInner({
|
|
90
|
+
items,
|
|
91
|
+
renderItem,
|
|
92
|
+
className,
|
|
93
|
+
style,
|
|
94
|
+
...options
|
|
95
|
+
}, ref) {
|
|
96
|
+
const containerRef = useRef(null);
|
|
97
|
+
const masonryRef = useRef(null);
|
|
98
|
+
const rootsRef = useRef(/* @__PURE__ */ new Map());
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (!containerRef.current) return;
|
|
101
|
+
masonryRef.current = new MasonrySnapGridLayout(containerRef.current, {
|
|
102
|
+
...options,
|
|
103
|
+
items,
|
|
104
|
+
renderItem: (item) => {
|
|
105
|
+
const div = document.createElement("div");
|
|
106
|
+
const root = ReactDOM.createRoot(div);
|
|
107
|
+
root.render(renderItem(item));
|
|
108
|
+
rootsRef.current.set(div, root);
|
|
109
|
+
return div;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
return () => {
|
|
113
|
+
rootsRef.current.forEach((root) => root.unmount());
|
|
114
|
+
rootsRef.current.clear();
|
|
115
|
+
masonryRef.current?.destroy();
|
|
116
|
+
masonryRef.current = null;
|
|
117
|
+
};
|
|
118
|
+
}, [options, renderItem]);
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (masonryRef.current) {
|
|
121
|
+
masonryRef.current.updateItems(items);
|
|
122
|
+
}
|
|
123
|
+
}, [items]);
|
|
124
|
+
useImperativeHandle(ref, () => ({
|
|
125
|
+
layout: masonryRef.current
|
|
126
|
+
}));
|
|
127
|
+
return /* @__PURE__ */ jsx(
|
|
128
|
+
"div",
|
|
129
|
+
{
|
|
130
|
+
ref: containerRef,
|
|
131
|
+
className,
|
|
132
|
+
style: { position: "relative", width: "100%", ...style }
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
var MasonrySnapGrid = forwardRef(MasonrySnapGridInner);
|
|
137
|
+
var react_default = MasonrySnapGridInner;
|
|
138
|
+
export {
|
|
139
|
+
react_default as default
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react.tsx","../../src/MasonrySnapGridLayout.ts"],"sourcesContent":["import React, {\r\n useEffect,\r\n useRef,\r\n forwardRef,\r\n useImperativeHandle,\r\n} from 'react';\r\nimport ReactDOM from 'react-dom/client';\r\nimport MasonrySnapGridLayout from './MasonrySnapGridLayout';\r\nimport {\r\n MasonrySnapGridLayoutOptions,\r\n MasonrySnapGridRef,\r\n} from './types';\r\n\r\ninterface MasonrySnapGridProps<T>\r\n extends Omit<MasonrySnapGridLayoutOptions<T>, 'items' | 'renderItem'> {\r\n items: T[];\r\n renderItem: (item: T) => React.ReactNode;\r\n className?: string;\r\n style?: React.CSSProperties;\r\n}\r\n\r\nfunction MasonrySnapGridInner<T>(\r\n {\r\n items,\r\n renderItem,\r\n className,\r\n style,\r\n ...options\r\n }: MasonrySnapGridProps<T>,\r\n ref: React.Ref<MasonrySnapGridRef>\r\n) {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const masonryRef = useRef<MasonrySnapGridLayout<T> | null>(null);\r\n\r\n // React roots storage to prevent memory leaks\r\n const rootsRef = useRef<Map<HTMLElement, ReactDOM.Root>>(new Map());\r\n\r\n // Initialize masonry layout\r\n useEffect(() => {\r\n if (!containerRef.current) return;\r\n\r\n masonryRef.current = new MasonrySnapGridLayout(containerRef.current, {\r\n ...options,\r\n items,\r\n renderItem: (item) => {\r\n const div = document.createElement('div');\r\n const root = ReactDOM.createRoot(div);\r\n root.render(renderItem(item));\r\n rootsRef.current.set(div, root);\r\n return div;\r\n },\r\n });\r\n\r\n return () => {\r\n // Unmount all React roots to avoid memory leaks\r\n rootsRef.current.forEach((root) => root.unmount());\r\n rootsRef.current.clear();\r\n\r\n masonryRef.current?.destroy();\r\n masonryRef.current = null;\r\n };\r\n }, [options, renderItem]); // include renderItem if it's not memoized\r\n\r\n // Update items on change\r\n useEffect(() => {\r\n if (masonryRef.current) {\r\n masonryRef.current.updateItems(items);\r\n }\r\n }, [items]);\r\n\r\n // Expose layout instance through ref\r\n useImperativeHandle(ref, () => ({\r\n layout: masonryRef.current!,\r\n }));\r\n\r\n return (\r\n <div\r\n ref={containerRef}\r\n className={className}\r\n style={{ position: 'relative', width: '100%', ...style }}\r\n />\r\n );\r\n}\r\n\r\n// Apply generic type correctly to forwardRef\r\nconst MasonrySnapGrid = forwardRef(MasonrySnapGridInner) as <T>(\r\n props: MasonrySnapGridProps<T> & { ref?: React.Ref<MasonrySnapGridRef> }\r\n) => ReturnType<typeof MasonrySnapGridInner>;\r\n\r\nexport default MasonrySnapGridInner\r\n","import { MasonrySnapGridLayoutOptions } from './types';\r\n\r\nexport default class MasonrySnapGridLayout<T = any> {\r\n private readonly container: HTMLElement;\r\n private readonly options: Required<MasonrySnapGridLayoutOptions<T>>;\r\n private items: HTMLElement[] = [];\r\n private columnHeights: number[] = [];\r\n private resizeObserver: ResizeObserver | undefined;\r\n private rafId: number | null = null;\r\n\r\n constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>) {\r\n this.container = container;\r\n this.options = {\r\n gutter: 16,\r\n minColWidth: 250,\r\n animate: true,\r\n transitionDuration: 400,\r\n classNames: {\r\n container: 'masonry-snap-grid-container',\r\n item: 'masonry-snap-grid-item',\r\n },\r\n ...options,\r\n };\r\n\r\n // Apply container class\r\n this.container.classList.add(this.options.classNames.container || \"\");\r\n\r\n // Initialize\r\n this.renderItems();\r\n this.setupResizeObserver();\r\n }\r\n\r\n private renderItems() {\r\n // Clear existing items\r\n this.items.forEach(item => item.remove());\r\n this.items = [];\r\n\r\n // Create fragment for batch DOM insertion\r\n const fragment = document.createDocumentFragment();\r\n\r\n // Create and append items\r\n this.options.items.forEach(itemData => {\r\n const itemElement = this.options.renderItem(itemData);\r\n itemElement.classList.add(this.options.classNames.item|| \"\");\r\n fragment.appendChild(itemElement);\r\n this.items.push(itemElement);\r\n });\r\n\r\n this.container.appendChild(fragment);\r\n this.updateLayout();\r\n }\r\n\r\n private setupResizeObserver() {\r\n this.resizeObserver = new ResizeObserver(() => {\r\n if (this.rafId) cancelAnimationFrame(this.rafId);\r\n this.rafId = requestAnimationFrame(() => this.updateLayout());\r\n });\r\n this.resizeObserver.observe(this.container);\r\n }\r\n\r\n private updateLayout() {\r\n const { gutter, minColWidth, animate, transitionDuration } = this.options;\r\n const containerWidth = this.container.clientWidth;\r\n\r\n // Calculate columns\r\n const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));\r\n const colWidth = (containerWidth - (columns - 1) * gutter) / columns;\r\n\r\n // Reset column heights\r\n this.columnHeights = new Array(columns).fill(0);\r\n\r\n // Position items\r\n this.items.forEach((item) => {\r\n const height = item.offsetHeight;\r\n const minCol = this.findShortestColumn();\r\n const x = minCol * (colWidth + gutter);\r\n const y = this.columnHeights[minCol];\r\n\r\n // Apply position and size\r\n item.style.width = `${colWidth}px`;\r\n item.style.transform = `translate3d(${x}px, ${y}px, 0)`;\r\n item.style.transition = animate\r\n ? `transform ${transitionDuration}ms ease`\r\n : 'none';\r\n\r\n // Update column height\r\n this.columnHeights[minCol] += height + gutter;\r\n });\r\n\r\n // Set container height\r\n const maxHeight = Math.max(...this.columnHeights);\r\n this.container.style.height = `${maxHeight}px`;\r\n }\r\n\r\n private findShortestColumn(): number {\r\n return this.columnHeights.indexOf(Math.min(...this.columnHeights));\r\n }\r\n\r\n public updateItems(newItems: T[]) {\r\n this.options.items = newItems;\r\n this.renderItems();\r\n }\r\n\r\n public destroy() {\r\n this.resizeObserver?.disconnect();\r\n if (this.rafId) cancelAnimationFrame(this.rafId);\r\n this.container.innerHTML = '';\r\n this.container.removeAttribute('style');\r\n this.container.classList.remove(this.options.classNames.container || \"\");\r\n }\r\n}"],"mappings":"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","names":[]}
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* src/index.css */
|
|
2
|
+
.masonry-snap-grid-container {
|
|
3
|
+
position: relative;
|
|
4
|
+
width: 100%;
|
|
5
|
+
transition: height 0.4s ease-out;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
}
|
|
8
|
+
.masonry-snap-grid-item {
|
|
9
|
+
position: absolute;
|
|
10
|
+
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
11
|
+
will-change: transform;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
transform-origin: top left;
|
|
14
|
+
backface-visibility: hidden;
|
|
15
|
+
perspective: 1000px;
|
|
16
|
+
}
|
|
17
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.css"],"sourcesContent":[".masonry-snap-grid-container {\r\n position: relative;\r\n width: 100%;\r\n transition: height 0.4s ease-out;\r\n overflow: hidden;\r\n}\r\n\r\n.masonry-snap-grid-item {\r\n position: absolute;\r\n transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);\r\n will-change: transform;\r\n box-sizing: border-box;\r\n transform-origin: top left;\r\n backface-visibility: hidden;\r\n perspective: 1000px;\r\n}"],"mappings":";AAAA,CAAC;AACG,YAAU;AACV,SAAO;AACP,cAAY,OAAO,KAAK;AACxB,YAAU;AACd;AAEA,CAAC;AACG,YAAU;AACV,cAAY,UAAU,KAAK,aAAa,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE;AACrD,eAAa;AACb,cAAY;AACZ,oBAAkB,IAAI;AACtB,uBAAqB;AACrB,eAAa;AACjB;","names":[]}
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
MasonrySnapGridLayout: () => MasonrySnapGridLayout,
|
|
24
|
+
default: () => index_default
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/MasonrySnapGridLayout.ts
|
|
29
|
+
var MasonrySnapGridLayout = class {
|
|
30
|
+
constructor(container, options) {
|
|
31
|
+
this.items = [];
|
|
32
|
+
this.columnHeights = [];
|
|
33
|
+
this.rafId = null;
|
|
34
|
+
this.container = container;
|
|
35
|
+
this.options = {
|
|
36
|
+
gutter: 16,
|
|
37
|
+
minColWidth: 250,
|
|
38
|
+
animate: true,
|
|
39
|
+
transitionDuration: 400,
|
|
40
|
+
classNames: {
|
|
41
|
+
container: "masonry-snap-grid-container",
|
|
42
|
+
item: "masonry-snap-grid-item"
|
|
43
|
+
},
|
|
44
|
+
...options
|
|
45
|
+
};
|
|
46
|
+
this.container.classList.add(this.options.classNames.container || "");
|
|
47
|
+
this.renderItems();
|
|
48
|
+
this.setupResizeObserver();
|
|
49
|
+
}
|
|
50
|
+
renderItems() {
|
|
51
|
+
this.items.forEach((item) => item.remove());
|
|
52
|
+
this.items = [];
|
|
53
|
+
const fragment = document.createDocumentFragment();
|
|
54
|
+
this.options.items.forEach((itemData) => {
|
|
55
|
+
const itemElement = this.options.renderItem(itemData);
|
|
56
|
+
itemElement.classList.add(this.options.classNames.item || "");
|
|
57
|
+
fragment.appendChild(itemElement);
|
|
58
|
+
this.items.push(itemElement);
|
|
59
|
+
});
|
|
60
|
+
this.container.appendChild(fragment);
|
|
61
|
+
this.updateLayout();
|
|
62
|
+
}
|
|
63
|
+
setupResizeObserver() {
|
|
64
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
65
|
+
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
66
|
+
this.rafId = requestAnimationFrame(() => this.updateLayout());
|
|
67
|
+
});
|
|
68
|
+
this.resizeObserver.observe(this.container);
|
|
69
|
+
}
|
|
70
|
+
updateLayout() {
|
|
71
|
+
const { gutter, minColWidth, animate, transitionDuration } = this.options;
|
|
72
|
+
const containerWidth = this.container.clientWidth;
|
|
73
|
+
const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));
|
|
74
|
+
const colWidth = (containerWidth - (columns - 1) * gutter) / columns;
|
|
75
|
+
this.columnHeights = new Array(columns).fill(0);
|
|
76
|
+
this.items.forEach((item) => {
|
|
77
|
+
const height = item.offsetHeight;
|
|
78
|
+
const minCol = this.findShortestColumn();
|
|
79
|
+
const x = minCol * (colWidth + gutter);
|
|
80
|
+
const y = this.columnHeights[minCol];
|
|
81
|
+
item.style.width = `${colWidth}px`;
|
|
82
|
+
item.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
|
83
|
+
item.style.transition = animate ? `transform ${transitionDuration}ms ease` : "none";
|
|
84
|
+
this.columnHeights[minCol] += height + gutter;
|
|
85
|
+
});
|
|
86
|
+
const maxHeight = Math.max(...this.columnHeights);
|
|
87
|
+
this.container.style.height = `${maxHeight}px`;
|
|
88
|
+
}
|
|
89
|
+
findShortestColumn() {
|
|
90
|
+
return this.columnHeights.indexOf(Math.min(...this.columnHeights));
|
|
91
|
+
}
|
|
92
|
+
updateItems(newItems) {
|
|
93
|
+
this.options.items = newItems;
|
|
94
|
+
this.renderItems();
|
|
95
|
+
}
|
|
96
|
+
destroy() {
|
|
97
|
+
this.resizeObserver?.disconnect();
|
|
98
|
+
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
99
|
+
this.container.innerHTML = "";
|
|
100
|
+
this.container.removeAttribute("style");
|
|
101
|
+
this.container.classList.remove(this.options.classNames.container || "");
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// src/index.ts
|
|
106
|
+
var index_default = MasonrySnapGridLayout;
|
|
107
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
108
|
+
0 && (module.exports = {
|
|
109
|
+
MasonrySnapGridLayout
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/MasonrySnapGridLayout.ts"],"sourcesContent":["import './index.css';\r\nimport MasonrySnapGridLayout from './MasonrySnapGridLayout';\r\nimport {MasonrySnapGridLayoutOptions} from './types';\r\n\r\nexport default MasonrySnapGridLayout;\r\nexport { MasonrySnapGridLayout, MasonrySnapGridLayoutOptions };","import { MasonrySnapGridLayoutOptions } from './types';\r\n\r\nexport default class MasonrySnapGridLayout<T = any> {\r\n private readonly container: HTMLElement;\r\n private readonly options: Required<MasonrySnapGridLayoutOptions<T>>;\r\n private items: HTMLElement[] = [];\r\n private columnHeights: number[] = [];\r\n private resizeObserver: ResizeObserver | undefined;\r\n private rafId: number | null = null;\r\n\r\n constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>) {\r\n this.container = container;\r\n this.options = {\r\n gutter: 16,\r\n minColWidth: 250,\r\n animate: true,\r\n transitionDuration: 400,\r\n classNames: {\r\n container: 'masonry-snap-grid-container',\r\n item: 'masonry-snap-grid-item',\r\n },\r\n ...options,\r\n };\r\n\r\n // Apply container class\r\n this.container.classList.add(this.options.classNames.container || \"\");\r\n\r\n // Initialize\r\n this.renderItems();\r\n this.setupResizeObserver();\r\n }\r\n\r\n private renderItems() {\r\n // Clear existing items\r\n this.items.forEach(item => item.remove());\r\n this.items = [];\r\n\r\n // Create fragment for batch DOM insertion\r\n const fragment = document.createDocumentFragment();\r\n\r\n // Create and append items\r\n this.options.items.forEach(itemData => {\r\n const itemElement = this.options.renderItem(itemData);\r\n itemElement.classList.add(this.options.classNames.item|| \"\");\r\n fragment.appendChild(itemElement);\r\n this.items.push(itemElement);\r\n });\r\n\r\n this.container.appendChild(fragment);\r\n this.updateLayout();\r\n }\r\n\r\n private setupResizeObserver() {\r\n this.resizeObserver = new ResizeObserver(() => {\r\n if (this.rafId) cancelAnimationFrame(this.rafId);\r\n this.rafId = requestAnimationFrame(() => this.updateLayout());\r\n });\r\n this.resizeObserver.observe(this.container);\r\n }\r\n\r\n private updateLayout() {\r\n const { gutter, minColWidth, animate, transitionDuration } = this.options;\r\n const containerWidth = this.container.clientWidth;\r\n\r\n // Calculate columns\r\n const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));\r\n const colWidth = (containerWidth - (columns - 1) * gutter) / columns;\r\n\r\n // Reset column heights\r\n this.columnHeights = new Array(columns).fill(0);\r\n\r\n // Position items\r\n this.items.forEach((item) => {\r\n const height = item.offsetHeight;\r\n const minCol = this.findShortestColumn();\r\n const x = minCol * (colWidth + gutter);\r\n const y = this.columnHeights[minCol];\r\n\r\n // Apply position and size\r\n item.style.width = `${colWidth}px`;\r\n item.style.transform = `translate3d(${x}px, ${y}px, 0)`;\r\n item.style.transition = animate\r\n ? `transform ${transitionDuration}ms ease`\r\n : 'none';\r\n\r\n // Update column height\r\n this.columnHeights[minCol] += height + gutter;\r\n });\r\n\r\n // Set container height\r\n const maxHeight = Math.max(...this.columnHeights);\r\n this.container.style.height = `${maxHeight}px`;\r\n }\r\n\r\n private findShortestColumn(): number {\r\n return this.columnHeights.indexOf(Math.min(...this.columnHeights));\r\n }\r\n\r\n public updateItems(newItems: T[]) {\r\n this.options.items = newItems;\r\n this.renderItems();\r\n }\r\n\r\n public destroy() {\r\n this.resizeObserver?.disconnect();\r\n if (this.rafId) cancelAnimationFrame(this.rafId);\r\n this.container.innerHTML = '';\r\n this.container.removeAttribute('style');\r\n this.container.classList.remove(this.options.classNames.container || \"\");\r\n }\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,IAAqB,wBAArB,MAAoD;AAAA,EAQhD,YAAY,WAAwB,SAA0C;AAL9E,SAAQ,QAAuB,CAAC;AAChC,SAAQ,gBAA0B,CAAC;AAEnC,SAAQ,QAAuB;AAG3B,SAAK,YAAY;AACjB,SAAK,UAAU;AAAA,MACX,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,SAAS;AAAA,MACT,oBAAoB;AAAA,MACpB,YAAY;AAAA,QACR,WAAW;AAAA,QACX,MAAM;AAAA,MACV;AAAA,MACA,GAAG;AAAA,IACP;AAGA,SAAK,UAAU,UAAU,IAAI,KAAK,QAAQ,WAAW,aAAa,EAAE;AAGpE,SAAK,YAAY;AACjB,SAAK,oBAAoB;AAAA,EAC7B;AAAA,EAEQ,cAAc;AAElB,SAAK,MAAM,QAAQ,UAAQ,KAAK,OAAO,CAAC;AACxC,SAAK,QAAQ,CAAC;AAGd,UAAM,WAAW,SAAS,uBAAuB;AAGjD,SAAK,QAAQ,MAAM,QAAQ,cAAY;AACnC,YAAM,cAAc,KAAK,QAAQ,WAAW,QAAQ;AACpD,kBAAY,UAAU,IAAI,KAAK,QAAQ,WAAW,QAAO,EAAE;AAC3D,eAAS,YAAY,WAAW;AAChC,WAAK,MAAM,KAAK,WAAW;AAAA,IAC/B,CAAC;AAED,SAAK,UAAU,YAAY,QAAQ;AACnC,SAAK,aAAa;AAAA,EACtB;AAAA,EAEQ,sBAAsB;AAC1B,SAAK,iBAAiB,IAAI,eAAe,MAAM;AAC3C,UAAI,KAAK,MAAO,sBAAqB,KAAK,KAAK;AAC/C,WAAK,QAAQ,sBAAsB,MAAM,KAAK,aAAa,CAAC;AAAA,IAChE,CAAC;AACD,SAAK,eAAe,QAAQ,KAAK,SAAS;AAAA,EAC9C;AAAA,EAEQ,eAAe;AACnB,UAAM,EAAE,QAAQ,aAAa,SAAS,mBAAmB,IAAI,KAAK;AAClE,UAAM,iBAAiB,KAAK,UAAU;AAGtC,UAAM,UAAU,KAAK,IAAI,GAAG,KAAK,OAAO,iBAAiB,WAAW,cAAc,OAAO,CAAC;AAC1F,UAAM,YAAY,kBAAkB,UAAU,KAAK,UAAU;AAG7D,SAAK,gBAAgB,IAAI,MAAM,OAAO,EAAE,KAAK,CAAC;AAG9C,SAAK,MAAM,QAAQ,CAAC,SAAS;AACzB,YAAM,SAAS,KAAK;AACpB,YAAM,SAAS,KAAK,mBAAmB;AACvC,YAAM,IAAI,UAAU,WAAW;AAC/B,YAAM,IAAI,KAAK,cAAc,MAAM;AAGnC,WAAK,MAAM,QAAQ,GAAG,QAAQ;AAC9B,WAAK,MAAM,YAAY,eAAe,CAAC,OAAO,CAAC;AAC/C,WAAK,MAAM,aAAa,UAClB,aAAa,kBAAkB,YAC/B;AAGN,WAAK,cAAc,MAAM,KAAK,SAAS;AAAA,IAC3C,CAAC;AAGD,UAAM,YAAY,KAAK,IAAI,GAAG,KAAK,aAAa;AAChD,SAAK,UAAU,MAAM,SAAS,GAAG,SAAS;AAAA,EAC9C;AAAA,EAEQ,qBAA6B;AACjC,WAAO,KAAK,cAAc,QAAQ,KAAK,IAAI,GAAG,KAAK,aAAa,CAAC;AAAA,EACrE;AAAA,EAEO,YAAY,UAAe;AAC9B,SAAK,QAAQ,QAAQ;AACrB,SAAK,YAAY;AAAA,EACrB;AAAA,EAEO,UAAU;AACb,SAAK,gBAAgB,WAAW;AAChC,QAAI,KAAK,MAAO,sBAAqB,KAAK,KAAK;AAC/C,SAAK,UAAU,YAAY;AAC3B,SAAK,UAAU,gBAAgB,OAAO;AACtC,SAAK,UAAU,UAAU,OAAO,KAAK,QAAQ,WAAW,aAAa,EAAE;AAAA,EAC3E;AACJ;;;AD1GA,IAAO,gBAAQ;","names":[]}
|
package/dist/react.d.cts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { a as MasonrySnapGridLayoutOptions, b as MasonrySnapGridRef } from './MasonrySnapGridLayout-D7ctVJaF.cjs';
|
|
4
|
+
|
|
5
|
+
interface MasonrySnapGridProps<T> extends Omit<MasonrySnapGridLayoutOptions<T>, 'items' | 'renderItem'> {
|
|
6
|
+
items: T[];
|
|
7
|
+
renderItem: (item: T) => React.ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
style?: React.CSSProperties;
|
|
10
|
+
}
|
|
11
|
+
declare function MasonrySnapGridInner<T>({ items, renderItem, className, style, ...options }: MasonrySnapGridProps<T>, ref: React.Ref<MasonrySnapGridRef>): react_jsx_runtime.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { MasonrySnapGridInner as default };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { a as MasonrySnapGridLayoutOptions, b as MasonrySnapGridRef } from './MasonrySnapGridLayout-D7ctVJaF.js';
|
|
4
|
+
|
|
5
|
+
interface MasonrySnapGridProps<T> extends Omit<MasonrySnapGridLayoutOptions<T>, 'items' | 'renderItem'> {
|
|
6
|
+
items: T[];
|
|
7
|
+
renderItem: (item: T) => React.ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
style?: React.CSSProperties;
|
|
10
|
+
}
|
|
11
|
+
declare function MasonrySnapGridInner<T>({ items, renderItem, className, style, ...options }: MasonrySnapGridProps<T>, ref: React.Ref<MasonrySnapGridRef>): react_jsx_runtime.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { MasonrySnapGridInner as default };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/react.tsx
|
|
31
|
+
var react_exports = {};
|
|
32
|
+
__export(react_exports, {
|
|
33
|
+
default: () => react_default
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(react_exports);
|
|
36
|
+
var import_react = require("react");
|
|
37
|
+
var import_client = __toESM(require("react-dom/client"), 1);
|
|
38
|
+
|
|
39
|
+
// src/MasonrySnapGridLayout.ts
|
|
40
|
+
var MasonrySnapGridLayout = class {
|
|
41
|
+
constructor(container, options) {
|
|
42
|
+
this.items = [];
|
|
43
|
+
this.columnHeights = [];
|
|
44
|
+
this.rafId = null;
|
|
45
|
+
this.container = container;
|
|
46
|
+
this.options = {
|
|
47
|
+
gutter: 16,
|
|
48
|
+
minColWidth: 250,
|
|
49
|
+
animate: true,
|
|
50
|
+
transitionDuration: 400,
|
|
51
|
+
classNames: {
|
|
52
|
+
container: "masonry-snap-grid-container",
|
|
53
|
+
item: "masonry-snap-grid-item"
|
|
54
|
+
},
|
|
55
|
+
...options
|
|
56
|
+
};
|
|
57
|
+
this.container.classList.add(this.options.classNames.container || "");
|
|
58
|
+
this.renderItems();
|
|
59
|
+
this.setupResizeObserver();
|
|
60
|
+
}
|
|
61
|
+
renderItems() {
|
|
62
|
+
this.items.forEach((item) => item.remove());
|
|
63
|
+
this.items = [];
|
|
64
|
+
const fragment = document.createDocumentFragment();
|
|
65
|
+
this.options.items.forEach((itemData) => {
|
|
66
|
+
const itemElement = this.options.renderItem(itemData);
|
|
67
|
+
itemElement.classList.add(this.options.classNames.item || "");
|
|
68
|
+
fragment.appendChild(itemElement);
|
|
69
|
+
this.items.push(itemElement);
|
|
70
|
+
});
|
|
71
|
+
this.container.appendChild(fragment);
|
|
72
|
+
this.updateLayout();
|
|
73
|
+
}
|
|
74
|
+
setupResizeObserver() {
|
|
75
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
76
|
+
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
77
|
+
this.rafId = requestAnimationFrame(() => this.updateLayout());
|
|
78
|
+
});
|
|
79
|
+
this.resizeObserver.observe(this.container);
|
|
80
|
+
}
|
|
81
|
+
updateLayout() {
|
|
82
|
+
const { gutter, minColWidth, animate, transitionDuration } = this.options;
|
|
83
|
+
const containerWidth = this.container.clientWidth;
|
|
84
|
+
const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));
|
|
85
|
+
const colWidth = (containerWidth - (columns - 1) * gutter) / columns;
|
|
86
|
+
this.columnHeights = new Array(columns).fill(0);
|
|
87
|
+
this.items.forEach((item) => {
|
|
88
|
+
const height = item.offsetHeight;
|
|
89
|
+
const minCol = this.findShortestColumn();
|
|
90
|
+
const x = minCol * (colWidth + gutter);
|
|
91
|
+
const y = this.columnHeights[minCol];
|
|
92
|
+
item.style.width = `${colWidth}px`;
|
|
93
|
+
item.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
|
94
|
+
item.style.transition = animate ? `transform ${transitionDuration}ms ease` : "none";
|
|
95
|
+
this.columnHeights[minCol] += height + gutter;
|
|
96
|
+
});
|
|
97
|
+
const maxHeight = Math.max(...this.columnHeights);
|
|
98
|
+
this.container.style.height = `${maxHeight}px`;
|
|
99
|
+
}
|
|
100
|
+
findShortestColumn() {
|
|
101
|
+
return this.columnHeights.indexOf(Math.min(...this.columnHeights));
|
|
102
|
+
}
|
|
103
|
+
updateItems(newItems) {
|
|
104
|
+
this.options.items = newItems;
|
|
105
|
+
this.renderItems();
|
|
106
|
+
}
|
|
107
|
+
destroy() {
|
|
108
|
+
this.resizeObserver?.disconnect();
|
|
109
|
+
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
110
|
+
this.container.innerHTML = "";
|
|
111
|
+
this.container.removeAttribute("style");
|
|
112
|
+
this.container.classList.remove(this.options.classNames.container || "");
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// src/react.tsx
|
|
117
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
118
|
+
function MasonrySnapGridInner({
|
|
119
|
+
items,
|
|
120
|
+
renderItem,
|
|
121
|
+
className,
|
|
122
|
+
style,
|
|
123
|
+
...options
|
|
124
|
+
}, ref) {
|
|
125
|
+
const containerRef = (0, import_react.useRef)(null);
|
|
126
|
+
const masonryRef = (0, import_react.useRef)(null);
|
|
127
|
+
const rootsRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
128
|
+
(0, import_react.useEffect)(() => {
|
|
129
|
+
if (!containerRef.current) return;
|
|
130
|
+
masonryRef.current = new MasonrySnapGridLayout(containerRef.current, {
|
|
131
|
+
...options,
|
|
132
|
+
items,
|
|
133
|
+
renderItem: (item) => {
|
|
134
|
+
const div = document.createElement("div");
|
|
135
|
+
const root = import_client.default.createRoot(div);
|
|
136
|
+
root.render(renderItem(item));
|
|
137
|
+
rootsRef.current.set(div, root);
|
|
138
|
+
return div;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
return () => {
|
|
142
|
+
rootsRef.current.forEach((root) => root.unmount());
|
|
143
|
+
rootsRef.current.clear();
|
|
144
|
+
masonryRef.current?.destroy();
|
|
145
|
+
masonryRef.current = null;
|
|
146
|
+
};
|
|
147
|
+
}, [options, renderItem]);
|
|
148
|
+
(0, import_react.useEffect)(() => {
|
|
149
|
+
if (masonryRef.current) {
|
|
150
|
+
masonryRef.current.updateItems(items);
|
|
151
|
+
}
|
|
152
|
+
}, [items]);
|
|
153
|
+
(0, import_react.useImperativeHandle)(ref, () => ({
|
|
154
|
+
layout: masonryRef.current
|
|
155
|
+
}));
|
|
156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
157
|
+
"div",
|
|
158
|
+
{
|
|
159
|
+
ref: containerRef,
|
|
160
|
+
className,
|
|
161
|
+
style: { position: "relative", width: "100%", ...style }
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
var MasonrySnapGrid = (0, import_react.forwardRef)(MasonrySnapGridInner);
|
|
166
|
+
var react_default = MasonrySnapGridInner;
|
|
167
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx","../src/MasonrySnapGridLayout.ts"],"sourcesContent":["import React, {\r\n useEffect,\r\n useRef,\r\n forwardRef,\r\n useImperativeHandle,\r\n} from 'react';\r\nimport ReactDOM from 'react-dom/client';\r\nimport MasonrySnapGridLayout from './MasonrySnapGridLayout';\r\nimport {\r\n MasonrySnapGridLayoutOptions,\r\n MasonrySnapGridRef,\r\n} from './types';\r\n\r\ninterface MasonrySnapGridProps<T>\r\n extends Omit<MasonrySnapGridLayoutOptions<T>, 'items' | 'renderItem'> {\r\n items: T[];\r\n renderItem: (item: T) => React.ReactNode;\r\n className?: string;\r\n style?: React.CSSProperties;\r\n}\r\n\r\nfunction MasonrySnapGridInner<T>(\r\n {\r\n items,\r\n renderItem,\r\n className,\r\n style,\r\n ...options\r\n }: MasonrySnapGridProps<T>,\r\n ref: React.Ref<MasonrySnapGridRef>\r\n) {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const masonryRef = useRef<MasonrySnapGridLayout<T> | null>(null);\r\n\r\n // React roots storage to prevent memory leaks\r\n const rootsRef = useRef<Map<HTMLElement, ReactDOM.Root>>(new Map());\r\n\r\n // Initialize masonry layout\r\n useEffect(() => {\r\n if (!containerRef.current) return;\r\n\r\n masonryRef.current = new MasonrySnapGridLayout(containerRef.current, {\r\n ...options,\r\n items,\r\n renderItem: (item) => {\r\n const div = document.createElement('div');\r\n const root = ReactDOM.createRoot(div);\r\n root.render(renderItem(item));\r\n rootsRef.current.set(div, root);\r\n return div;\r\n },\r\n });\r\n\r\n return () => {\r\n // Unmount all React roots to avoid memory leaks\r\n rootsRef.current.forEach((root) => root.unmount());\r\n rootsRef.current.clear();\r\n\r\n masonryRef.current?.destroy();\r\n masonryRef.current = null;\r\n };\r\n }, [options, renderItem]); // include renderItem if it's not memoized\r\n\r\n // Update items on change\r\n useEffect(() => {\r\n if (masonryRef.current) {\r\n masonryRef.current.updateItems(items);\r\n }\r\n }, [items]);\r\n\r\n // Expose layout instance through ref\r\n useImperativeHandle(ref, () => ({\r\n layout: masonryRef.current!,\r\n }));\r\n\r\n return (\r\n <div\r\n ref={containerRef}\r\n className={className}\r\n style={{ position: 'relative', width: '100%', ...style }}\r\n />\r\n );\r\n}\r\n\r\n// Apply generic type correctly to forwardRef\r\nconst MasonrySnapGrid = forwardRef(MasonrySnapGridInner) as <T>(\r\n props: MasonrySnapGridProps<T> & { ref?: React.Ref<MasonrySnapGridRef> }\r\n) => ReturnType<typeof MasonrySnapGridInner>;\r\n\r\nexport default MasonrySnapGridInner\r\n","import { MasonrySnapGridLayoutOptions } from './types';\r\n\r\nexport default class MasonrySnapGridLayout<T = any> {\r\n private readonly container: HTMLElement;\r\n private readonly options: Required<MasonrySnapGridLayoutOptions<T>>;\r\n private items: HTMLElement[] = [];\r\n private columnHeights: number[] = [];\r\n private resizeObserver: ResizeObserver | undefined;\r\n private rafId: number | null = null;\r\n\r\n constructor(container: HTMLElement, options: MasonrySnapGridLayoutOptions<T>) {\r\n this.container = container;\r\n this.options = {\r\n gutter: 16,\r\n minColWidth: 250,\r\n animate: true,\r\n transitionDuration: 400,\r\n classNames: {\r\n container: 'masonry-snap-grid-container',\r\n item: 'masonry-snap-grid-item',\r\n },\r\n ...options,\r\n };\r\n\r\n // Apply container class\r\n this.container.classList.add(this.options.classNames.container || \"\");\r\n\r\n // Initialize\r\n this.renderItems();\r\n this.setupResizeObserver();\r\n }\r\n\r\n private renderItems() {\r\n // Clear existing items\r\n this.items.forEach(item => item.remove());\r\n this.items = [];\r\n\r\n // Create fragment for batch DOM insertion\r\n const fragment = document.createDocumentFragment();\r\n\r\n // Create and append items\r\n this.options.items.forEach(itemData => {\r\n const itemElement = this.options.renderItem(itemData);\r\n itemElement.classList.add(this.options.classNames.item|| \"\");\r\n fragment.appendChild(itemElement);\r\n this.items.push(itemElement);\r\n });\r\n\r\n this.container.appendChild(fragment);\r\n this.updateLayout();\r\n }\r\n\r\n private setupResizeObserver() {\r\n this.resizeObserver = new ResizeObserver(() => {\r\n if (this.rafId) cancelAnimationFrame(this.rafId);\r\n this.rafId = requestAnimationFrame(() => this.updateLayout());\r\n });\r\n this.resizeObserver.observe(this.container);\r\n }\r\n\r\n private updateLayout() {\r\n const { gutter, minColWidth, animate, transitionDuration } = this.options;\r\n const containerWidth = this.container.clientWidth;\r\n\r\n // Calculate columns\r\n const columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));\r\n const colWidth = (containerWidth - (columns - 1) * gutter) / columns;\r\n\r\n // Reset column heights\r\n this.columnHeights = new Array(columns).fill(0);\r\n\r\n // Position items\r\n this.items.forEach((item) => {\r\n const height = item.offsetHeight;\r\n const minCol = this.findShortestColumn();\r\n const x = minCol * (colWidth + gutter);\r\n const y = this.columnHeights[minCol];\r\n\r\n // Apply position and size\r\n item.style.width = `${colWidth}px`;\r\n item.style.transform = `translate3d(${x}px, ${y}px, 0)`;\r\n item.style.transition = animate\r\n ? `transform ${transitionDuration}ms ease`\r\n : 'none';\r\n\r\n // Update column height\r\n this.columnHeights[minCol] += height + gutter;\r\n });\r\n\r\n // Set container height\r\n const maxHeight = Math.max(...this.columnHeights);\r\n this.container.style.height = `${maxHeight}px`;\r\n }\r\n\r\n private findShortestColumn(): number {\r\n return this.columnHeights.indexOf(Math.min(...this.columnHeights));\r\n }\r\n\r\n public updateItems(newItems: T[]) {\r\n this.options.items = newItems;\r\n this.renderItems();\r\n }\r\n\r\n public destroy() {\r\n this.resizeObserver?.disconnect();\r\n if (this.rafId) cancelAnimationFrame(this.rafId);\r\n this.container.innerHTML = '';\r\n this.container.removeAttribute('style');\r\n this.container.classList.remove(this.options.classNames.container || \"\");\r\n }\r\n}"],"mappings":"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","names":["ReactDOM"]}
|
package/package.json
CHANGED