masonry-snap-grid-layout 0.0.5 → 1.0.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "masonry-snap-grid-layout",
3
- "version": "0.0.5",
3
+ "version": "1.0.0",
4
4
  "description": "A performant, responsive masonry layout library with smooth animations, dynamic columns, and zero dependencies.",
5
5
  "keywords": [
6
6
  "masonry",
@@ -65,7 +65,8 @@
65
65
  "tsup": "8.3.5",
66
66
  "typescript": "^5.0.0"
67
67
  },
68
- "dependencies": {
69
- "react": "^19.1.1"
68
+ "peerDependencies": {
69
+ "react": "^18 || ^19",
70
+ "react-dom": "^18 || ^19"
70
71
  }
71
72
  }
@@ -1,71 +0,0 @@
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 };
@@ -1,71 +0,0 @@
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 };
@@ -1,45 +0,0 @@
1
- /* src/masonry-snap-grid-layout.css */
2
- :root {
3
- --gutter: 16px;
4
- --columns: 4;
5
- --min-col-width: 250px;
6
- --transition-duration: 0.4s;
7
- --primary-color: #6b73ff;
8
- --secondary-color: #000dff;
9
- --text-color: #fff;
10
- --shadow-color: rgba(0, 0, 0, 0.1);
11
- --item-radius: 8px;
12
- }
13
- .masonry-snap-grid-layout-container {
14
- position: relative;
15
- width: 100%;
16
- max-width: 1200px;
17
- margin: 0 auto;
18
- transition: height var(--transition-duration) ease-out;
19
- }
20
- .masonry-snap-grid-layout-item {
21
- position: absolute;
22
- color: var(--text-color);
23
- border-radius: var(--item-radius);
24
- font-size: 1rem;
25
- box-shadow: 0 6px 12px var(--shadow-color);
26
- user-select: none;
27
- cursor: grab;
28
- will-change: transform;
29
- transition:
30
- transform var(--transition-duration) cubic-bezier(0.4, 0, 0.2, 1),
31
- box-shadow 0.3s ease,
32
- background 0.4s ease;
33
- overflow: hidden;
34
- z-index: 1;
35
- }
36
- .masonry-snap-grid-layout-item:hover {
37
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
38
- transform: translateY(-2px);
39
- }
40
- .masonry-snap-grid-layout-item:active {
41
- cursor: grabbing;
42
- box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
43
- z-index: 10;
44
- }
45
- /*# sourceMappingURL=index.css.map */
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/masonry-snap-grid-layout.css"],"sourcesContent":["/*\r\n Masonry Snap Grid Layout Styles\r\n\r\n CSS variables for easy customization:\r\n --gutter: spacing between items\r\n --columns: number of columns (calculated dynamically)\r\n --min-col-width: minimum width of columns\r\n --transition-duration: animation timing\r\n --primary-color, --secondary-color: gradient colors for items\r\n --text-color: color for text inside items\r\n --shadow-color: shadow color for item depth\r\n --item-radius: border-radius for rounded corners\r\n*/\r\n\r\n/* Root CSS variables for theming */\r\n:root {\r\n --gutter: 16px;\r\n --columns: 4;\r\n --min-col-width: 250px;\r\n --transition-duration: 0.4s;\r\n --primary-color: #6b73ff;\r\n --secondary-color: #000dff;\r\n --text-color: #fff;\r\n --shadow-color: rgba(0, 0, 0, 0.1);\r\n --item-radius: 8px;\r\n}\r\n\r\n/* Container for masonry grid — relative positioning for absolute item placement */\r\n.masonry-snap-grid-layout-container {\r\n position: relative;\r\n width: 100%;\r\n max-width: 1200px;\r\n margin: 0 auto;\r\n transition: height var(--transition-duration) ease-out;\r\n}\r\n\r\n/* Each item absolutely positioned within container */\r\n.masonry-snap-grid-layout-item {\r\n position: absolute;\r\n color: var(--text-color);\r\n border-radius: var(--item-radius);\r\n font-size: 1rem;\r\n box-shadow: 0 6px 12px var(--shadow-color);\r\n user-select: none;\r\n cursor: grab;\r\n will-change: transform;\r\n transition:\r\n transform var(--transition-duration) cubic-bezier(0.4, 0, 0.2, 1),\r\n box-shadow 0.3s ease,\r\n background 0.4s ease;\r\n overflow: hidden;\r\n z-index: 1;\r\n}\r\n\r\n/* Hover state for better UX */\r\n.masonry-snap-grid-layout-item:hover {\r\n box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);\r\n transform: translateY(-2px);\r\n}\r\n\r\n/* Active / grabbing state */\r\n.masonry-snap-grid-layout-item:active {\r\n cursor: grabbing;\r\n box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);\r\n z-index: 10;\r\n}\r\n\r\n"],"mappings":";AAeA;AACI,YAAU;AACV,aAAW;AACX,mBAAiB;AACjB,yBAAuB;AACvB,mBAAiB;AACjB,qBAAmB;AACnB,gBAAc;AACd,kBAAgB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAC9B,iBAAe;AACnB;AAGA,CAAC;AACG,YAAU;AACV,SAAO;AACP,aAAW;AACX,UAAQ,EAAE;AACV,cAAY,OAAO,IAAI,uBAAuB;AAClD;AAGA,CAAC;AACG,YAAU;AACV,SAAO,IAAI;AACX,iBAAe,IAAI;AACnB,aAAW;AACX,cAAY,EAAE,IAAI,KAAK,IAAI;AAC3B,eAAa;AACb,UAAQ;AACR,eAAa;AACb;AAAA,IACQ,UAAU,IAAI,uBAAuB,aAAa,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE;AAAA,IACjE,WAAW,KAAK,IAAI;AAAA,IACpB,WAAW,KAAK;AACxB,YAAU;AACV,WAAS;AACb;AAGA,CAlBC,6BAkB6B;AAC1B,cAAY,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACrC,aAAW,WAAW;AAC1B;AAGA,CAxBC,6BAwB6B;AAC1B,UAAQ;AACR,cAAY,EAAE,KAAK,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACtC,WAAS;AACb;","names":[]}
package/dist/esm/index.js DELETED
@@ -1,206 +0,0 @@
1
- // src/MasonrySnapGridLayout.ts
2
- var MasonrySnapGridLayout = class {
3
- constructor(container2, options = {}) {
4
- this.items = [];
5
- this.positions = [];
6
- this.columnHeights = [];
7
- this.columns = 0;
8
- this.lastPositions = [];
9
- this.resizeRaf = null;
10
- this.resizeObserver = null;
11
- this.boundResizeHandler = this.handleResize.bind(this);
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
- };
26
- this.options = {
27
- gutter: 16,
28
- minColWidth: 250,
29
- animate: true,
30
- transitionDuration: 400,
31
- initialItems: 0,
32
- itemContent: null,
33
- classNames: this.classNames,
34
- ...options
35
- };
36
- this.container.classList.add(this.classNames.container);
37
- this.init();
38
- }
39
- init() {
40
- this.setupEventListeners();
41
- this.generateItems(this.options.initialItems);
42
- this.calculateLayout();
43
- this.applyLayout(false);
44
- }
45
- setupEventListeners() {
46
- this.resizeObserver = new ResizeObserver(() => this.handleResize());
47
- this.resizeObserver.observe(this.container);
48
- window.addEventListener("resize", this.boundResizeHandler);
49
- }
50
- generateItems(count) {
51
- if (count < this.items.length) {
52
- this.items.slice(count).forEach((item) => item.remove());
53
- this.items = this.items.slice(0, count);
54
- return;
55
- }
56
- const startIndex = this.items.length;
57
- const fragment = document.createDocumentFragment();
58
- for (let i = startIndex; i < count; i++) {
59
- const item = this.createItem(i);
60
- fragment.appendChild(item);
61
- this.items.push(item);
62
- }
63
- this.container.appendChild(fragment);
64
- }
65
- createItem(index) {
66
- const div = document.createElement("div");
67
- div.className = this.classNames.item;
68
- const contentOption = this.options.itemContent;
69
- let content;
70
- if (typeof contentOption === "function") {
71
- content = contentOption(index);
72
- } else if (contentOption instanceof HTMLElement || typeof contentOption === "string") {
73
- content = contentOption;
74
- } else {
75
- content = `Item ${index + 1}`;
76
- }
77
- if (typeof content === "string") {
78
- div.textContent = content;
79
- } else {
80
- div.appendChild(content);
81
- }
82
- const height = 120 + Math.floor(Math.random() * 100);
83
- div.style.height = `${height}px`;
84
- return div;
85
- }
86
- calculateLayout() {
87
- const { gutter, minColWidth } = this.options;
88
- const containerWidth = this.container.clientWidth;
89
- this.columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));
90
- const colWidth = (containerWidth - (this.columns - 1) * gutter) / this.columns;
91
- this.lastPositions = [...this.positions];
92
- this.columnHeights = new Array(this.columns).fill(0);
93
- this.positions = [];
94
- this.items.forEach((item, i) => {
95
- const height = item.offsetHeight;
96
- let minCol = 0;
97
- for (let c = 1; c < this.columns; c++) {
98
- if (this.columnHeights[c] < this.columnHeights[minCol]) {
99
- minCol = c;
100
- }
101
- }
102
- const x = minCol * (colWidth + gutter);
103
- const y = this.columnHeights[minCol];
104
- this.positions[i] = { x, y, width: colWidth, height };
105
- this.columnHeights[minCol] += height + gutter;
106
- });
107
- const maxHeight = Math.max(...this.columnHeights);
108
- this.container.style.height = `${maxHeight}px`;
109
- }
110
- applyLayout(animate = false) {
111
- const duration = this.options.transitionDuration;
112
- this.items.forEach((item, i) => {
113
- const pos = this.positions[i] || { x: 0, y: 0, width: 0 };
114
- const lastPos = this.lastPositions[i] || { x: 0, y: 0 };
115
- item.style.width = `${pos.width}px`;
116
- if (animate) {
117
- const dx = lastPos.x - pos.x;
118
- const dy = lastPos.y - pos.y;
119
- item.style.transition = "none";
120
- item.style.transform = `translate3d(${pos.x + dx}px, ${pos.y + dy}px, 0)`;
121
- void item.offsetHeight;
122
- item.style.transition = `transform ${duration}ms ease`;
123
- item.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;
124
- } else {
125
- item.style.transition = "none";
126
- item.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;
127
- }
128
- });
129
- }
130
- handleResize() {
131
- if (this.resizeRaf) cancelAnimationFrame(this.resizeRaf);
132
- this.resizeRaf = requestAnimationFrame(() => {
133
- this.calculateLayout();
134
- this.applyLayout(true);
135
- });
136
- }
137
- shuffleItems() {
138
- for (let i = this.items.length - 1; i > 0; i--) {
139
- const j = Math.floor(Math.random() * (i + 1));
140
- [this.items[i], this.items[j]] = [this.items[j], this.items[i]];
141
- }
142
- const fragment = document.createDocumentFragment();
143
- this.items.forEach((item) => fragment.appendChild(item));
144
- this.container.appendChild(fragment);
145
- this.calculateLayout();
146
- this.applyLayout(true);
147
- }
148
- addItems(count) {
149
- const newCount = this.items.length + count;
150
- this.generateItems(newCount);
151
- this.calculateLayout();
152
- this.applyLayout(true);
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
- }
173
- };
174
-
175
- // src/index.ts
176
- var container = document.getElementById("masonry");
177
- if (container) {
178
- const masonry = new MasonrySnapGridLayout(container, {
179
- gutter: 20,
180
- minColWidth: 200,
181
- animate: true,
182
- initialItems: 40,
183
- // Custom item content example
184
- itemContent: (index) => {
185
- const div = document.createElement("div");
186
- div.textContent = `Custom Item ${index + 1}`;
187
- div.style.color = "#fff";
188
- div.style.fontWeight = "bold";
189
- div.style.textAlign = "center";
190
- div.style.padding = "1rem";
191
- return div;
192
- },
193
- classNames: {
194
- container: "masonry-snap-grid-layout-container",
195
- item: "masonry-snap-grid-layout-item"
196
- }
197
- });
198
- masonry.addItems(5);
199
- masonry.shuffleItems();
200
- }
201
- var index_default = MasonrySnapGridLayout;
202
- export {
203
- MasonrySnapGridLayout,
204
- index_default as default
205
- };
206
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
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"]}
package/dist/esm/react.js DELETED
@@ -1,237 +0,0 @@
1
- "use client";
2
-
3
- // src/react.tsx
4
- import {
5
- useEffect,
6
- useRef,
7
- useImperativeHandle,
8
- forwardRef
9
- } from "react";
10
-
11
- // src/MasonrySnapGridLayout.ts
12
- var MasonrySnapGridLayout = class {
13
- constructor(container, options = {}) {
14
- this.items = [];
15
- this.positions = [];
16
- this.columnHeights = [];
17
- this.columns = 0;
18
- this.lastPositions = [];
19
- this.resizeRaf = null;
20
- this.resizeObserver = null;
21
- this.boundResizeHandler = this.handleResize.bind(this);
22
- this.container = container;
23
- this.classNames = {
24
- container: "masonry-snap-grid-container",
25
- item: "masonry-snap-grid-item",
26
- itemContent: "masonry-snap-grid-item-content",
27
- itemHeader: "masonry-snap-grid-item-header",
28
- itemTitle: "masonry-snap-grid-item-title",
29
- itemId: "masonry-snap-grid-item-id",
30
- itemBody: "masonry-snap-grid-item-body",
31
- progressBar: "masonry-snap-grid-progress-bar",
32
- progress: "masonry-snap-grid-progress",
33
- itemFooter: "masonry-snap-grid-item-footer",
34
- ...options.classNames || {}
35
- };
36
- this.options = {
37
- gutter: 16,
38
- minColWidth: 250,
39
- animate: true,
40
- transitionDuration: 400,
41
- initialItems: 0,
42
- itemContent: null,
43
- classNames: this.classNames,
44
- ...options
45
- };
46
- this.container.classList.add(this.classNames.container);
47
- this.init();
48
- }
49
- init() {
50
- this.setupEventListeners();
51
- this.generateItems(this.options.initialItems);
52
- this.calculateLayout();
53
- this.applyLayout(false);
54
- }
55
- setupEventListeners() {
56
- this.resizeObserver = new ResizeObserver(() => this.handleResize());
57
- this.resizeObserver.observe(this.container);
58
- window.addEventListener("resize", this.boundResizeHandler);
59
- }
60
- generateItems(count) {
61
- if (count < this.items.length) {
62
- this.items.slice(count).forEach((item) => item.remove());
63
- this.items = this.items.slice(0, count);
64
- return;
65
- }
66
- const startIndex = this.items.length;
67
- const fragment = document.createDocumentFragment();
68
- for (let i = startIndex; i < count; i++) {
69
- const item = this.createItem(i);
70
- fragment.appendChild(item);
71
- this.items.push(item);
72
- }
73
- this.container.appendChild(fragment);
74
- }
75
- createItem(index) {
76
- const div = document.createElement("div");
77
- div.className = this.classNames.item;
78
- const contentOption = this.options.itemContent;
79
- let content;
80
- if (typeof contentOption === "function") {
81
- content = contentOption(index);
82
- } else if (contentOption instanceof HTMLElement || typeof contentOption === "string") {
83
- content = contentOption;
84
- } else {
85
- content = `Item ${index + 1}`;
86
- }
87
- if (typeof content === "string") {
88
- div.textContent = content;
89
- } else {
90
- div.appendChild(content);
91
- }
92
- const height = 120 + Math.floor(Math.random() * 100);
93
- div.style.height = `${height}px`;
94
- return div;
95
- }
96
- calculateLayout() {
97
- const { gutter, minColWidth } = this.options;
98
- const containerWidth = this.container.clientWidth;
99
- this.columns = Math.max(1, Math.floor((containerWidth + gutter) / (minColWidth + gutter)));
100
- const colWidth = (containerWidth - (this.columns - 1) * gutter) / this.columns;
101
- this.lastPositions = [...this.positions];
102
- this.columnHeights = new Array(this.columns).fill(0);
103
- this.positions = [];
104
- this.items.forEach((item, i) => {
105
- const height = item.offsetHeight;
106
- let minCol = 0;
107
- for (let c = 1; c < this.columns; c++) {
108
- if (this.columnHeights[c] < this.columnHeights[minCol]) {
109
- minCol = c;
110
- }
111
- }
112
- const x = minCol * (colWidth + gutter);
113
- const y = this.columnHeights[minCol];
114
- this.positions[i] = { x, y, width: colWidth, height };
115
- this.columnHeights[minCol] += height + gutter;
116
- });
117
- const maxHeight = Math.max(...this.columnHeights);
118
- this.container.style.height = `${maxHeight}px`;
119
- }
120
- applyLayout(animate = false) {
121
- const duration = this.options.transitionDuration;
122
- this.items.forEach((item, i) => {
123
- const pos = this.positions[i] || { x: 0, y: 0, width: 0 };
124
- const lastPos = this.lastPositions[i] || { x: 0, y: 0 };
125
- item.style.width = `${pos.width}px`;
126
- if (animate) {
127
- const dx = lastPos.x - pos.x;
128
- const dy = lastPos.y - pos.y;
129
- item.style.transition = "none";
130
- item.style.transform = `translate3d(${pos.x + dx}px, ${pos.y + dy}px, 0)`;
131
- void item.offsetHeight;
132
- item.style.transition = `transform ${duration}ms ease`;
133
- item.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;
134
- } else {
135
- item.style.transition = "none";
136
- item.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;
137
- }
138
- });
139
- }
140
- handleResize() {
141
- if (this.resizeRaf) cancelAnimationFrame(this.resizeRaf);
142
- this.resizeRaf = requestAnimationFrame(() => {
143
- this.calculateLayout();
144
- this.applyLayout(true);
145
- });
146
- }
147
- shuffleItems() {
148
- for (let i = this.items.length - 1; i > 0; i--) {
149
- const j = Math.floor(Math.random() * (i + 1));
150
- [this.items[i], this.items[j]] = [this.items[j], this.items[i]];
151
- }
152
- const fragment = document.createDocumentFragment();
153
- this.items.forEach((item) => fragment.appendChild(item));
154
- this.container.appendChild(fragment);
155
- this.calculateLayout();
156
- this.applyLayout(true);
157
- }
158
- addItems(count) {
159
- const newCount = this.items.length + count;
160
- this.generateItems(newCount);
161
- this.calculateLayout();
162
- this.applyLayout(true);
163
- }
164
- destroy() {
165
- if (this.resizeObserver) {
166
- this.resizeObserver.disconnect();
167
- this.resizeObserver = null;
168
- }
169
- window.removeEventListener("resize", this.boundResizeHandler);
170
- if (this.resizeRaf) {
171
- cancelAnimationFrame(this.resizeRaf);
172
- this.resizeRaf = null;
173
- }
174
- this.container.innerHTML = "";
175
- this.container.removeAttribute("style");
176
- this.container.classList.remove(this.classNames.container);
177
- this.items = [];
178
- this.positions = [];
179
- this.columnHeights = [];
180
- this.columns = 0;
181
- this.lastPositions = [];
182
- }
183
- };
184
-
185
- // src/react.tsx
186
- import { jsx } from "react/jsx-runtime";
187
- var MasonrySnapGrid = forwardRef(function MasonrySnapGrid2({ items, options = {}, renderItem, className, style }, ref) {
188
- const containerRef = useRef(null);
189
- const layoutRef = useRef(null);
190
- useImperativeHandle(ref, () => ({
191
- layout: layoutRef.current
192
- }));
193
- useEffect(() => {
194
- if (!containerRef.current) return;
195
- layoutRef.current = new MasonrySnapGridLayout(containerRef.current, {
196
- ...options,
197
- // We disable internal item generation because React controls content
198
- initialItems: 0,
199
- itemContent: null
200
- });
201
- return () => {
202
- layoutRef.current?.destroy();
203
- layoutRef.current = null;
204
- };
205
- }, [options]);
206
- useEffect(() => {
207
- if (!containerRef.current || !layoutRef.current) return;
208
- containerRef.current.innerHTML = "";
209
- }, [items, renderItem]);
210
- return /* @__PURE__ */ jsx(
211
- "div",
212
- {
213
- ref: containerRef,
214
- className,
215
- style: { position: "relative", width: "100%", ...style },
216
- "aria-label": "Masonry grid",
217
- role: "list",
218
- children: items.map((item, index) => /* @__PURE__ */ jsx(
219
- "div",
220
- {
221
- className: "masonry-snap-grid-item",
222
- style: { breakInside: "avoid" },
223
- role: "listitem",
224
- "aria-posinset": index + 1,
225
- "aria-setsize": items.length,
226
- children: renderItem(item, index)
227
- },
228
- index
229
- ))
230
- }
231
- );
232
- });
233
- var react_default = MasonrySnapGrid;
234
- export {
235
- react_default as default
236
- };
237
- //# sourceMappingURL=react.js.map