masonry-simple 4.4.0 → 5.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/README.md CHANGED
@@ -1,68 +1,126 @@
1
- <div align="center">
2
- <br>
1
+ # masonry-simple
3
2
 
4
- <h1>masonry-simple</h1>
3
+ A lightweight masonry layout helper built on top of CSS Grid.
5
4
 
6
- <p><sup>MasonrySimple implements a simple system for placing masonry style elements using CSS Grid. Masonry placement is used for dynamic grids where elements may have different heights and need to be placed neatly without gaps.</sup></p>
5
+ - Idempotent lifecycle: `init()`, `refresh()`, `destroy()`.
6
+ - Unified layout scheduling for resize, mutations, image load, and manual refresh.
7
+ - Safe teardown with observer/listener cleanup and inline-style restoration.
8
+ - Supports dynamic image content and responsive CSS changes.
7
9
 
8
- [![npm](https://img.shields.io/npm/v/masonry-simple.svg?colorB=brightgreen)](https://www.npmjs.com/package/masonry-simple)
9
- [![GitHub package version](https://img.shields.io/github/package-json/v/ux-ui-pro/masonry-simple.svg)](https://github.com/ux-ui-pro/masonry-simple)
10
- [![NPM Downloads](https://img.shields.io/npm/dm/masonry-simple.svg?style=flat)](https://www.npmjs.org/package/masonry-simple)
10
+ ## Install
11
11
 
12
- <sup>1kB gzipped</sup>
13
-
14
- <a href="https://codepen.io/ux-ui/pen/poxGEqX">Demo</a>
15
-
16
- </div>
17
- <br>
18
-
19
- &#10148; **Install**
20
- ```console
21
- $ yarn add masonry-simple
12
+ ```bash
13
+ npm install masonry-simple
22
14
  ```
23
- <br>
24
15
 
25
- &#10148; **Import**
26
- ```javascript
16
+ ## Usage (TypeScript)
17
+
18
+ ```ts
27
19
  import MasonrySimple from 'masonry-simple';
28
- ```
29
- <br>
30
20
 
31
- &#10148; **Usage**
32
- ```javascript
33
21
  const masonry = new MasonrySimple({
34
22
  container: '.masonry',
35
23
  });
36
24
 
37
25
  masonry.init();
38
26
  ```
39
- ```HTML
27
+
28
+ ## Usage (Vue 3)
29
+
30
+ ```vue
31
+ <script setup lang="ts">
32
+ import { onBeforeUnmount, onMounted, ref, shallowRef } from 'vue';
33
+ import MasonrySimple from 'masonry-simple';
34
+
35
+ const masonryRef = ref<HTMLElement | null>(null);
36
+ const masonry = shallowRef<MasonrySimple | null>(null);
37
+
38
+ onMounted(() => {
39
+ if (!masonryRef.value) return;
40
+ masonry.value = new MasonrySimple({ container: masonryRef.value });
41
+ masonry.value.init();
42
+ });
43
+
44
+ onBeforeUnmount(() => {
45
+ masonry.value?.destroy();
46
+ masonry.value = null;
47
+ });
48
+ </script>
49
+
50
+ <template>
51
+ <div ref="masonryRef" class="masonry">
52
+ <div class="masonry__item">...</div>
53
+ <div class="masonry__item">...</div>
54
+ </div>
55
+ </template>
56
+ ```
57
+
58
+ ## HTML Layout
59
+
60
+ ```html
40
61
  <div class="masonry">
41
62
  <div class="masonry__item">
42
- ...
63
+ <img src="/img/1.jpg" alt="">
43
64
  </div>
44
65
  <div class="masonry__item">
45
- ...
66
+ Lorem ipsum
46
67
  </div>
47
- ...
48
68
  </div>
49
69
  ```
50
- ```SCSS
70
+
71
+ ## CSS Contract
72
+
73
+ ```css
51
74
  .masonry {
52
75
  display: grid;
53
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
54
- grid-auto-flow: dense;
55
- grid-gap: 10px;
76
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
77
+ gap: 12px;
56
78
  }
57
79
  ```
58
- <br>
59
80
 
60
- &#10148; **Destroy**
61
- ```javascript
81
+ - Container must use CSS Grid.
82
+ - Item heights are measured from rendered content.
83
+ - Library temporarily applies inline styles while active:
84
+ - `gridAutoRows: 1px`
85
+ - `contain: layout`
86
+ - `alignItems: start`
87
+ - These inline styles are restored on `destroy()`.
88
+
89
+ ## Options
90
+
91
+ | Option | Type | Default | Description |
92
+ |:------------|:-------------------------|:------------:|:--------------------------------------------------|
93
+ | `container` | `HTMLElement \| string` | `'.masonry'` | Target container element or selector. |
94
+
95
+ ## Methods
96
+
97
+ ```ts
98
+ masonry.init();
99
+ masonry.refresh();
62
100
  masonry.destroy();
63
101
  ```
64
- <br>
65
102
 
66
- &#10148; **License**
103
+ ## Lifecycle Behavior
104
+
105
+ - `init()` is idempotent and does not duplicate observers/listeners.
106
+ - `refresh()` re-collects grid items and schedules a single layout pass.
107
+ - `destroy()` cancels scheduled animation frame work, disconnects observers, clears listeners, and resets item styles (`gridRowEnd`).
108
+ - `refresh()` after `destroy()` is a safe no-op.
109
+
110
+ ## Edge Cases and Limitations
111
+
112
+ - Missing container: methods do nothing.
113
+ - SSR / non-DOM environments: graceful no-op behavior when `document` is unavailable.
114
+ - Missing `ResizeObserver` / `MutationObserver`: layout still works via manual `refresh()`.
115
+ - Hidden container (`display: none`) or zero-size state may produce temporary fallback spans.
116
+ - If size changes happen without child mutations, call `refresh()` manually.
117
+
118
+ ## Performance Notes
119
+
120
+ - Batch DOM changes and call one `refresh()`.
121
+ - Avoid frequent style mutations during animation loops.
122
+ - Prefer known image dimensions to reduce post-load relayout work.
123
+
124
+ ## License
67
125
 
68
- masonry-simple is released under MIT license
126
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,198 @@
1
+
2
+ //#region src/index.ts
3
+ var MasonrySimple = class {
4
+ containerOption;
5
+ container = null;
6
+ gridItems = [];
7
+ rowHeight = 1;
8
+ rowGap = 0;
9
+ resizeScheduled = false;
10
+ rafId = null;
11
+ resizeObserver = null;
12
+ mutationObserver = null;
13
+ observerAbortController = null;
14
+ imageAbortController = null;
15
+ observedImages = /* @__PURE__ */ new WeakSet();
16
+ isInitialized = false;
17
+ isDestroyed = false;
18
+ originalContainerStyles = {
19
+ gridAutoRows: "",
20
+ contain: "",
21
+ alignItems: ""
22
+ };
23
+ constructor(options = {}) {
24
+ this.containerOption = options.container;
25
+ }
26
+ init() {
27
+ if (this.isInitialized) return;
28
+ this.container = this.resolveContainer();
29
+ if (!this.container) return;
30
+ this.isInitialized = true;
31
+ this.isDestroyed = false;
32
+ this.setupAbortControllers();
33
+ this.storeContainerStyles();
34
+ this.initializeContainerStyles();
35
+ this.initializeGridItems();
36
+ this.setupResizeObserver();
37
+ this.setupMutationObserver();
38
+ this.scheduleLayout();
39
+ }
40
+ refresh() {
41
+ if (!this.isInitialized || this.isDestroyed || !this.container) return;
42
+ this.initializeGridItems();
43
+ this.scheduleLayout();
44
+ }
45
+ destroy() {
46
+ if (!this.isInitialized && !this.container) return;
47
+ this.isDestroyed = true;
48
+ this.isInitialized = false;
49
+ this.cancelScheduledLayout();
50
+ this.observerAbortController?.abort();
51
+ this.imageAbortController?.abort();
52
+ this.resizeObserver?.disconnect();
53
+ this.mutationObserver?.disconnect();
54
+ this.gridItems.forEach((item) => {
55
+ item.style.gridRowEnd = "";
56
+ });
57
+ if (this.container) {
58
+ this.container.style.gridAutoRows = this.originalContainerStyles.gridAutoRows;
59
+ this.container.style.contain = this.originalContainerStyles.contain;
60
+ this.container.style.alignItems = this.originalContainerStyles.alignItems;
61
+ }
62
+ this.gridItems = [];
63
+ this.rowHeight = 1;
64
+ this.rowGap = 0;
65
+ this.resizeObserver = null;
66
+ this.mutationObserver = null;
67
+ this.observerAbortController = null;
68
+ this.imageAbortController = null;
69
+ this.observedImages = /* @__PURE__ */ new WeakSet();
70
+ this.resizeScheduled = false;
71
+ this.container = null;
72
+ this.originalContainerStyles = {
73
+ gridAutoRows: "",
74
+ contain: "",
75
+ alignItems: ""
76
+ };
77
+ }
78
+ resolveContainer() {
79
+ if (this.containerOption instanceof HTMLElement) return this.containerOption;
80
+ if (typeof document === "undefined") return null;
81
+ if (typeof this.containerOption === "string") return document.querySelector(this.containerOption);
82
+ return document.querySelector(".masonry");
83
+ }
84
+ scheduleLayout() {
85
+ if (!this.isInitialized || this.isDestroyed || !this.container || this.resizeScheduled) return;
86
+ this.resizeScheduled = true;
87
+ if (typeof requestAnimationFrame === "function") {
88
+ this.rafId = requestAnimationFrame(() => {
89
+ this.rafId = null;
90
+ this.resizeScheduled = false;
91
+ this.performLayout();
92
+ });
93
+ return;
94
+ }
95
+ this.rafId = null;
96
+ this.resizeScheduled = false;
97
+ this.performLayout();
98
+ }
99
+ cancelScheduledLayout() {
100
+ if (this.rafId !== null && typeof cancelAnimationFrame === "function") cancelAnimationFrame(this.rafId);
101
+ this.rafId = null;
102
+ this.resizeScheduled = false;
103
+ }
104
+ performLayout() {
105
+ if (!this.isInitialized || this.isDestroyed || !this.container) return;
106
+ this.measureContainerMetrics();
107
+ this.resizeAllItems();
108
+ }
109
+ resizeAllItems() {
110
+ const denominator = this.rowHeight + this.rowGap;
111
+ this.gridItems.forEach((item) => {
112
+ if (denominator <= 0 || !Number.isFinite(denominator)) {
113
+ item.style.gridRowEnd = "span 1";
114
+ return;
115
+ }
116
+ const rowSpan = Math.max(1, Math.ceil((item.getBoundingClientRect().height + this.rowGap) / denominator));
117
+ item.style.gridRowEnd = `span ${rowSpan}`;
118
+ });
119
+ }
120
+ setupAbortControllers() {
121
+ this.observerAbortController = new AbortController();
122
+ this.imageAbortController = new AbortController();
123
+ this.observerAbortController.signal.addEventListener("abort", () => {
124
+ this.resizeObserver?.disconnect();
125
+ this.mutationObserver?.disconnect();
126
+ });
127
+ }
128
+ setupResizeObserver() {
129
+ if (!this.container || typeof ResizeObserver === "undefined") return;
130
+ this.resizeObserver = new ResizeObserver(() => this.handleResize());
131
+ this.resizeObserver.observe(this.container);
132
+ }
133
+ setupMutationObserver() {
134
+ if (!this.container || typeof MutationObserver === "undefined") return;
135
+ this.mutationObserver = new MutationObserver(() => {
136
+ this.initializeGridItems();
137
+ this.scheduleLayout();
138
+ });
139
+ this.mutationObserver.observe(this.container, {
140
+ childList: true,
141
+ subtree: false
142
+ });
143
+ }
144
+ handleResize() {
145
+ this.scheduleLayout();
146
+ }
147
+ initializeContainerStyles() {
148
+ if (!this.container) return;
149
+ this.container.style.gridAutoRows = "1px";
150
+ this.container.style.contain = "layout";
151
+ this.container.style.alignItems = "start";
152
+ }
153
+ measureContainerMetrics() {
154
+ if (!this.container) return;
155
+ const cs = getComputedStyle(this.container);
156
+ this.rowGap = this.parseCssPixelValue(cs.rowGap);
157
+ const parsedRowHeight = this.parseCssPixelValue(cs.gridAutoRows);
158
+ this.rowHeight = parsedRowHeight > 0 ? parsedRowHeight : 1;
159
+ }
160
+ initializeGridItems() {
161
+ if (!this.container) return;
162
+ this.gridItems = Array.from(this.container.children).filter((item) => item instanceof HTMLElement);
163
+ this.gridItems.forEach((item) => {
164
+ item.querySelectorAll("img").forEach((image) => {
165
+ this.observeImage(image);
166
+ });
167
+ });
168
+ }
169
+ observeImage(image) {
170
+ if (image.complete || this.observedImages.has(image)) return;
171
+ this.observedImages.add(image);
172
+ const onLoad = () => this.scheduleLayout();
173
+ if (this.imageAbortController) {
174
+ image.addEventListener("load", onLoad, {
175
+ once: true,
176
+ signal: this.imageAbortController.signal
177
+ });
178
+ return;
179
+ }
180
+ image.addEventListener("load", onLoad, { once: true });
181
+ }
182
+ parseCssPixelValue(value) {
183
+ const parsedValue = Number.parseFloat(value);
184
+ return Number.isFinite(parsedValue) ? parsedValue : 0;
185
+ }
186
+ storeContainerStyles() {
187
+ if (!this.container) return;
188
+ this.originalContainerStyles = {
189
+ gridAutoRows: this.container.style.gridAutoRows || "",
190
+ contain: this.container.style.contain || "",
191
+ alignItems: this.container.style.alignItems || ""
192
+ };
193
+ }
194
+ };
195
+
196
+ //#endregion
197
+ module.exports = MasonrySimple;
198
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["interface MasonrySimpleOptions {\n container?: HTMLElement | string;\n}\n\nexport default class MasonrySimple {\n private readonly containerOption: HTMLElement | string | undefined;\n private container: HTMLElement | null = null;\n private gridItems: HTMLElement[] = [];\n private rowHeight = 1;\n private rowGap = 0;\n private resizeScheduled = false;\n private rafId: number | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private mutationObserver: MutationObserver | null = null;\n private observerAbortController: AbortController | null = null;\n private imageAbortController: AbortController | null = null;\n private observedImages = new WeakSet<HTMLImageElement>();\n private isInitialized = false;\n private isDestroyed = false;\n private originalContainerStyles = {\n gridAutoRows: '',\n contain: '',\n alignItems: '',\n };\n\n constructor(options: MasonrySimpleOptions = {}) {\n this.containerOption = options.container;\n }\n\n public init(): void {\n if (this.isInitialized) return;\n\n this.container = this.resolveContainer();\n\n if (!this.container) return;\n\n this.isInitialized = true;\n this.isDestroyed = false;\n\n this.setupAbortControllers();\n this.storeContainerStyles();\n this.initializeContainerStyles();\n this.initializeGridItems();\n this.setupResizeObserver();\n this.setupMutationObserver();\n this.scheduleLayout();\n }\n\n public refresh(): void {\n if (!this.isInitialized || this.isDestroyed || !this.container) return;\n\n this.initializeGridItems();\n this.scheduleLayout();\n }\n\n public destroy(): void {\n if (!this.isInitialized && !this.container) return;\n\n this.isDestroyed = true;\n this.isInitialized = false;\n this.cancelScheduledLayout();\n this.observerAbortController?.abort();\n this.imageAbortController?.abort();\n this.resizeObserver?.disconnect();\n this.mutationObserver?.disconnect();\n this.gridItems.forEach((item) => {\n item.style.gridRowEnd = '';\n });\n\n if (this.container) {\n this.container.style.gridAutoRows = this.originalContainerStyles.gridAutoRows;\n this.container.style.contain = this.originalContainerStyles.contain;\n this.container.style.alignItems = this.originalContainerStyles.alignItems;\n }\n\n this.gridItems = [];\n this.rowHeight = 1;\n this.rowGap = 0;\n this.resizeObserver = null;\n this.mutationObserver = null;\n this.observerAbortController = null;\n this.imageAbortController = null;\n this.observedImages = new WeakSet<HTMLImageElement>();\n this.resizeScheduled = false;\n this.container = null;\n this.originalContainerStyles = {\n gridAutoRows: '',\n contain: '',\n alignItems: '',\n };\n }\n\n private resolveContainer(): HTMLElement | null {\n if (this.containerOption instanceof HTMLElement) {\n return this.containerOption;\n }\n\n if (typeof document === 'undefined') return null;\n\n if (typeof this.containerOption === 'string') {\n return document.querySelector<HTMLElement>(this.containerOption);\n }\n\n return document.querySelector<HTMLElement>('.masonry');\n }\n\n private scheduleLayout(): void {\n if (!this.isInitialized || this.isDestroyed || !this.container || this.resizeScheduled) return;\n\n this.resizeScheduled = true;\n\n if (typeof requestAnimationFrame === 'function') {\n this.rafId = requestAnimationFrame(() => {\n this.rafId = null;\n this.resizeScheduled = false;\n this.performLayout();\n });\n\n return;\n }\n\n this.rafId = null;\n this.resizeScheduled = false;\n this.performLayout();\n }\n\n private cancelScheduledLayout(): void {\n if (this.rafId !== null && typeof cancelAnimationFrame === 'function') {\n cancelAnimationFrame(this.rafId);\n }\n\n this.rafId = null;\n this.resizeScheduled = false;\n }\n\n private performLayout(): void {\n if (!this.isInitialized || this.isDestroyed || !this.container) return;\n\n this.measureContainerMetrics();\n this.resizeAllItems();\n }\n\n private resizeAllItems(): void {\n const denominator = this.rowHeight + this.rowGap;\n\n this.gridItems.forEach((item) => {\n if (denominator <= 0 || !Number.isFinite(denominator)) {\n item.style.gridRowEnd = 'span 1';\n\n return;\n }\n\n const rowSpan = Math.max(\n 1,\n Math.ceil((item.getBoundingClientRect().height + this.rowGap) / denominator),\n );\n item.style.gridRowEnd = `span ${rowSpan}`;\n });\n }\n\n private setupAbortControllers(): void {\n this.observerAbortController = new AbortController();\n this.imageAbortController = new AbortController();\n\n this.observerAbortController.signal.addEventListener('abort', () => {\n this.resizeObserver?.disconnect();\n this.mutationObserver?.disconnect();\n });\n }\n\n private setupResizeObserver(): void {\n if (!this.container || typeof ResizeObserver === 'undefined') return;\n\n this.resizeObserver = new ResizeObserver(() => this.handleResize());\n\n this.resizeObserver.observe(this.container);\n }\n\n private setupMutationObserver(): void {\n if (!this.container || typeof MutationObserver === 'undefined') return;\n\n this.mutationObserver = new MutationObserver(() => {\n this.initializeGridItems();\n this.scheduleLayout();\n });\n\n this.mutationObserver.observe(this.container, {\n childList: true,\n subtree: false,\n });\n }\n\n private handleResize(): void {\n this.scheduleLayout();\n }\n\n private initializeContainerStyles(): void {\n if (!this.container) return;\n\n this.container.style.gridAutoRows = '1px';\n this.container.style.contain = 'layout';\n this.container.style.alignItems = 'start';\n }\n\n private measureContainerMetrics(): void {\n if (!this.container) return;\n\n const cs = getComputedStyle(this.container);\n\n this.rowGap = this.parseCssPixelValue(cs.rowGap);\n\n const parsedRowHeight = this.parseCssPixelValue(cs.gridAutoRows);\n\n this.rowHeight = parsedRowHeight > 0 ? parsedRowHeight : 1;\n }\n\n private initializeGridItems(): void {\n if (!this.container) return;\n\n this.gridItems = Array.from(this.container.children).filter(\n (item): item is HTMLElement => item instanceof HTMLElement,\n );\n\n this.gridItems.forEach((item) => {\n const images = item.querySelectorAll('img');\n\n images.forEach((image) => {\n this.observeImage(image);\n });\n });\n }\n\n private observeImage(image: HTMLImageElement): void {\n if (image.complete || this.observedImages.has(image)) return;\n\n this.observedImages.add(image);\n\n const onLoad = (): void => this.scheduleLayout();\n\n if (this.imageAbortController) {\n image.addEventListener('load', onLoad, {\n once: true,\n signal: this.imageAbortController.signal,\n });\n\n return;\n }\n\n image.addEventListener('load', onLoad, { once: true });\n }\n\n private parseCssPixelValue(value: string): number {\n const parsedValue = Number.parseFloat(value);\n\n return Number.isFinite(parsedValue) ? parsedValue : 0;\n }\n\n private storeContainerStyles(): void {\n if (!this.container) return;\n\n this.originalContainerStyles = {\n gridAutoRows: this.container.style.gridAutoRows || '',\n contain: this.container.style.contain || '',\n alignItems: this.container.style.alignItems || '',\n };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ //#region src/index.d.ts
2
+ interface MasonrySimpleOptions {
3
+ container?: HTMLElement | string;
4
+ }
5
+ declare class MasonrySimple {
6
+ private readonly containerOption;
7
+ private container;
8
+ private gridItems;
9
+ private rowHeight;
10
+ private rowGap;
11
+ private resizeScheduled;
12
+ private rafId;
13
+ private resizeObserver;
14
+ private mutationObserver;
15
+ private observerAbortController;
16
+ private imageAbortController;
17
+ private observedImages;
18
+ private isInitialized;
19
+ private isDestroyed;
20
+ private originalContainerStyles;
21
+ constructor(options?: MasonrySimpleOptions);
22
+ init(): void;
23
+ refresh(): void;
24
+ destroy(): void;
25
+ private resolveContainer;
26
+ private scheduleLayout;
27
+ private cancelScheduledLayout;
28
+ private performLayout;
29
+ private resizeAllItems;
30
+ private setupAbortControllers;
31
+ private setupResizeObserver;
32
+ private setupMutationObserver;
33
+ private handleResize;
34
+ private initializeContainerStyles;
35
+ private measureContainerMetrics;
36
+ private initializeGridItems;
37
+ private observeImage;
38
+ private parseCssPixelValue;
39
+ private storeContainerStyles;
40
+ }
41
+ export = MasonrySimple;
42
+ //# sourceMappingURL=index.d.cts.map
package/dist/index.d.ts CHANGED
@@ -1,27 +1,43 @@
1
+ //#region src/index.d.ts
1
2
  interface MasonrySimpleOptions {
2
- container?: HTMLElement | string;
3
+ container?: HTMLElement | string;
3
4
  }
4
- export default class MasonrySimple {
5
- private readonly container;
6
- private gridItems;
7
- private rowHeight;
8
- private rowGap;
9
- private resizeScheduled;
10
- private resizeObserver;
11
- private mutationObserver;
12
- private abortController;
13
- private originalAutoRows;
14
- constructor(options?: MasonrySimpleOptions);
15
- init(): void;
16
- refresh(): void;
17
- destroy(): void;
18
- private handleResize;
19
- private resizeAllItems;
20
- private setupAbortController;
21
- private setupResizeObserver;
22
- private setupMutationObserver;
23
- private initializeContainerStyles;
24
- private initializeGridItems;
25
- private storeAndSetAutoRows;
5
+ declare class MasonrySimple {
6
+ private readonly containerOption;
7
+ private container;
8
+ private gridItems;
9
+ private rowHeight;
10
+ private rowGap;
11
+ private resizeScheduled;
12
+ private rafId;
13
+ private resizeObserver;
14
+ private mutationObserver;
15
+ private observerAbortController;
16
+ private imageAbortController;
17
+ private observedImages;
18
+ private isInitialized;
19
+ private isDestroyed;
20
+ private originalContainerStyles;
21
+ constructor(options?: MasonrySimpleOptions);
22
+ init(): void;
23
+ refresh(): void;
24
+ destroy(): void;
25
+ private resolveContainer;
26
+ private scheduleLayout;
27
+ private cancelScheduledLayout;
28
+ private performLayout;
29
+ private resizeAllItems;
30
+ private setupAbortControllers;
31
+ private setupResizeObserver;
32
+ private setupMutationObserver;
33
+ private handleResize;
34
+ private initializeContainerStyles;
35
+ private measureContainerMetrics;
36
+ private initializeGridItems;
37
+ private observeImage;
38
+ private parseCssPixelValue;
39
+ private storeContainerStyles;
26
40
  }
27
- export {};
41
+ //#endregion
42
+ export { MasonrySimple as default };
43
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,197 @@
1
+ //#region src/index.ts
2
+ var MasonrySimple = class {
3
+ containerOption;
4
+ container = null;
5
+ gridItems = [];
6
+ rowHeight = 1;
7
+ rowGap = 0;
8
+ resizeScheduled = false;
9
+ rafId = null;
10
+ resizeObserver = null;
11
+ mutationObserver = null;
12
+ observerAbortController = null;
13
+ imageAbortController = null;
14
+ observedImages = /* @__PURE__ */ new WeakSet();
15
+ isInitialized = false;
16
+ isDestroyed = false;
17
+ originalContainerStyles = {
18
+ gridAutoRows: "",
19
+ contain: "",
20
+ alignItems: ""
21
+ };
22
+ constructor(options = {}) {
23
+ this.containerOption = options.container;
24
+ }
25
+ init() {
26
+ if (this.isInitialized) return;
27
+ this.container = this.resolveContainer();
28
+ if (!this.container) return;
29
+ this.isInitialized = true;
30
+ this.isDestroyed = false;
31
+ this.setupAbortControllers();
32
+ this.storeContainerStyles();
33
+ this.initializeContainerStyles();
34
+ this.initializeGridItems();
35
+ this.setupResizeObserver();
36
+ this.setupMutationObserver();
37
+ this.scheduleLayout();
38
+ }
39
+ refresh() {
40
+ if (!this.isInitialized || this.isDestroyed || !this.container) return;
41
+ this.initializeGridItems();
42
+ this.scheduleLayout();
43
+ }
44
+ destroy() {
45
+ if (!this.isInitialized && !this.container) return;
46
+ this.isDestroyed = true;
47
+ this.isInitialized = false;
48
+ this.cancelScheduledLayout();
49
+ this.observerAbortController?.abort();
50
+ this.imageAbortController?.abort();
51
+ this.resizeObserver?.disconnect();
52
+ this.mutationObserver?.disconnect();
53
+ this.gridItems.forEach((item) => {
54
+ item.style.gridRowEnd = "";
55
+ });
56
+ if (this.container) {
57
+ this.container.style.gridAutoRows = this.originalContainerStyles.gridAutoRows;
58
+ this.container.style.contain = this.originalContainerStyles.contain;
59
+ this.container.style.alignItems = this.originalContainerStyles.alignItems;
60
+ }
61
+ this.gridItems = [];
62
+ this.rowHeight = 1;
63
+ this.rowGap = 0;
64
+ this.resizeObserver = null;
65
+ this.mutationObserver = null;
66
+ this.observerAbortController = null;
67
+ this.imageAbortController = null;
68
+ this.observedImages = /* @__PURE__ */ new WeakSet();
69
+ this.resizeScheduled = false;
70
+ this.container = null;
71
+ this.originalContainerStyles = {
72
+ gridAutoRows: "",
73
+ contain: "",
74
+ alignItems: ""
75
+ };
76
+ }
77
+ resolveContainer() {
78
+ if (this.containerOption instanceof HTMLElement) return this.containerOption;
79
+ if (typeof document === "undefined") return null;
80
+ if (typeof this.containerOption === "string") return document.querySelector(this.containerOption);
81
+ return document.querySelector(".masonry");
82
+ }
83
+ scheduleLayout() {
84
+ if (!this.isInitialized || this.isDestroyed || !this.container || this.resizeScheduled) return;
85
+ this.resizeScheduled = true;
86
+ if (typeof requestAnimationFrame === "function") {
87
+ this.rafId = requestAnimationFrame(() => {
88
+ this.rafId = null;
89
+ this.resizeScheduled = false;
90
+ this.performLayout();
91
+ });
92
+ return;
93
+ }
94
+ this.rafId = null;
95
+ this.resizeScheduled = false;
96
+ this.performLayout();
97
+ }
98
+ cancelScheduledLayout() {
99
+ if (this.rafId !== null && typeof cancelAnimationFrame === "function") cancelAnimationFrame(this.rafId);
100
+ this.rafId = null;
101
+ this.resizeScheduled = false;
102
+ }
103
+ performLayout() {
104
+ if (!this.isInitialized || this.isDestroyed || !this.container) return;
105
+ this.measureContainerMetrics();
106
+ this.resizeAllItems();
107
+ }
108
+ resizeAllItems() {
109
+ const denominator = this.rowHeight + this.rowGap;
110
+ this.gridItems.forEach((item) => {
111
+ if (denominator <= 0 || !Number.isFinite(denominator)) {
112
+ item.style.gridRowEnd = "span 1";
113
+ return;
114
+ }
115
+ const rowSpan = Math.max(1, Math.ceil((item.getBoundingClientRect().height + this.rowGap) / denominator));
116
+ item.style.gridRowEnd = `span ${rowSpan}`;
117
+ });
118
+ }
119
+ setupAbortControllers() {
120
+ this.observerAbortController = new AbortController();
121
+ this.imageAbortController = new AbortController();
122
+ this.observerAbortController.signal.addEventListener("abort", () => {
123
+ this.resizeObserver?.disconnect();
124
+ this.mutationObserver?.disconnect();
125
+ });
126
+ }
127
+ setupResizeObserver() {
128
+ if (!this.container || typeof ResizeObserver === "undefined") return;
129
+ this.resizeObserver = new ResizeObserver(() => this.handleResize());
130
+ this.resizeObserver.observe(this.container);
131
+ }
132
+ setupMutationObserver() {
133
+ if (!this.container || typeof MutationObserver === "undefined") return;
134
+ this.mutationObserver = new MutationObserver(() => {
135
+ this.initializeGridItems();
136
+ this.scheduleLayout();
137
+ });
138
+ this.mutationObserver.observe(this.container, {
139
+ childList: true,
140
+ subtree: false
141
+ });
142
+ }
143
+ handleResize() {
144
+ this.scheduleLayout();
145
+ }
146
+ initializeContainerStyles() {
147
+ if (!this.container) return;
148
+ this.container.style.gridAutoRows = "1px";
149
+ this.container.style.contain = "layout";
150
+ this.container.style.alignItems = "start";
151
+ }
152
+ measureContainerMetrics() {
153
+ if (!this.container) return;
154
+ const cs = getComputedStyle(this.container);
155
+ this.rowGap = this.parseCssPixelValue(cs.rowGap);
156
+ const parsedRowHeight = this.parseCssPixelValue(cs.gridAutoRows);
157
+ this.rowHeight = parsedRowHeight > 0 ? parsedRowHeight : 1;
158
+ }
159
+ initializeGridItems() {
160
+ if (!this.container) return;
161
+ this.gridItems = Array.from(this.container.children).filter((item) => item instanceof HTMLElement);
162
+ this.gridItems.forEach((item) => {
163
+ item.querySelectorAll("img").forEach((image) => {
164
+ this.observeImage(image);
165
+ });
166
+ });
167
+ }
168
+ observeImage(image) {
169
+ if (image.complete || this.observedImages.has(image)) return;
170
+ this.observedImages.add(image);
171
+ const onLoad = () => this.scheduleLayout();
172
+ if (this.imageAbortController) {
173
+ image.addEventListener("load", onLoad, {
174
+ once: true,
175
+ signal: this.imageAbortController.signal
176
+ });
177
+ return;
178
+ }
179
+ image.addEventListener("load", onLoad, { once: true });
180
+ }
181
+ parseCssPixelValue(value) {
182
+ const parsedValue = Number.parseFloat(value);
183
+ return Number.isFinite(parsedValue) ? parsedValue : 0;
184
+ }
185
+ storeContainerStyles() {
186
+ if (!this.container) return;
187
+ this.originalContainerStyles = {
188
+ gridAutoRows: this.container.style.gridAutoRows || "",
189
+ contain: this.container.style.contain || "",
190
+ alignItems: this.container.style.alignItems || ""
191
+ };
192
+ }
193
+ };
194
+
195
+ //#endregion
196
+ export { MasonrySimple as default };
197
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["interface MasonrySimpleOptions {\n container?: HTMLElement | string;\n}\n\nexport default class MasonrySimple {\n private readonly containerOption: HTMLElement | string | undefined;\n private container: HTMLElement | null = null;\n private gridItems: HTMLElement[] = [];\n private rowHeight = 1;\n private rowGap = 0;\n private resizeScheduled = false;\n private rafId: number | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private mutationObserver: MutationObserver | null = null;\n private observerAbortController: AbortController | null = null;\n private imageAbortController: AbortController | null = null;\n private observedImages = new WeakSet<HTMLImageElement>();\n private isInitialized = false;\n private isDestroyed = false;\n private originalContainerStyles = {\n gridAutoRows: '',\n contain: '',\n alignItems: '',\n };\n\n constructor(options: MasonrySimpleOptions = {}) {\n this.containerOption = options.container;\n }\n\n public init(): void {\n if (this.isInitialized) return;\n\n this.container = this.resolveContainer();\n\n if (!this.container) return;\n\n this.isInitialized = true;\n this.isDestroyed = false;\n\n this.setupAbortControllers();\n this.storeContainerStyles();\n this.initializeContainerStyles();\n this.initializeGridItems();\n this.setupResizeObserver();\n this.setupMutationObserver();\n this.scheduleLayout();\n }\n\n public refresh(): void {\n if (!this.isInitialized || this.isDestroyed || !this.container) return;\n\n this.initializeGridItems();\n this.scheduleLayout();\n }\n\n public destroy(): void {\n if (!this.isInitialized && !this.container) return;\n\n this.isDestroyed = true;\n this.isInitialized = false;\n this.cancelScheduledLayout();\n this.observerAbortController?.abort();\n this.imageAbortController?.abort();\n this.resizeObserver?.disconnect();\n this.mutationObserver?.disconnect();\n this.gridItems.forEach((item) => {\n item.style.gridRowEnd = '';\n });\n\n if (this.container) {\n this.container.style.gridAutoRows = this.originalContainerStyles.gridAutoRows;\n this.container.style.contain = this.originalContainerStyles.contain;\n this.container.style.alignItems = this.originalContainerStyles.alignItems;\n }\n\n this.gridItems = [];\n this.rowHeight = 1;\n this.rowGap = 0;\n this.resizeObserver = null;\n this.mutationObserver = null;\n this.observerAbortController = null;\n this.imageAbortController = null;\n this.observedImages = new WeakSet<HTMLImageElement>();\n this.resizeScheduled = false;\n this.container = null;\n this.originalContainerStyles = {\n gridAutoRows: '',\n contain: '',\n alignItems: '',\n };\n }\n\n private resolveContainer(): HTMLElement | null {\n if (this.containerOption instanceof HTMLElement) {\n return this.containerOption;\n }\n\n if (typeof document === 'undefined') return null;\n\n if (typeof this.containerOption === 'string') {\n return document.querySelector<HTMLElement>(this.containerOption);\n }\n\n return document.querySelector<HTMLElement>('.masonry');\n }\n\n private scheduleLayout(): void {\n if (!this.isInitialized || this.isDestroyed || !this.container || this.resizeScheduled) return;\n\n this.resizeScheduled = true;\n\n if (typeof requestAnimationFrame === 'function') {\n this.rafId = requestAnimationFrame(() => {\n this.rafId = null;\n this.resizeScheduled = false;\n this.performLayout();\n });\n\n return;\n }\n\n this.rafId = null;\n this.resizeScheduled = false;\n this.performLayout();\n }\n\n private cancelScheduledLayout(): void {\n if (this.rafId !== null && typeof cancelAnimationFrame === 'function') {\n cancelAnimationFrame(this.rafId);\n }\n\n this.rafId = null;\n this.resizeScheduled = false;\n }\n\n private performLayout(): void {\n if (!this.isInitialized || this.isDestroyed || !this.container) return;\n\n this.measureContainerMetrics();\n this.resizeAllItems();\n }\n\n private resizeAllItems(): void {\n const denominator = this.rowHeight + this.rowGap;\n\n this.gridItems.forEach((item) => {\n if (denominator <= 0 || !Number.isFinite(denominator)) {\n item.style.gridRowEnd = 'span 1';\n\n return;\n }\n\n const rowSpan = Math.max(\n 1,\n Math.ceil((item.getBoundingClientRect().height + this.rowGap) / denominator),\n );\n item.style.gridRowEnd = `span ${rowSpan}`;\n });\n }\n\n private setupAbortControllers(): void {\n this.observerAbortController = new AbortController();\n this.imageAbortController = new AbortController();\n\n this.observerAbortController.signal.addEventListener('abort', () => {\n this.resizeObserver?.disconnect();\n this.mutationObserver?.disconnect();\n });\n }\n\n private setupResizeObserver(): void {\n if (!this.container || typeof ResizeObserver === 'undefined') return;\n\n this.resizeObserver = new ResizeObserver(() => this.handleResize());\n\n this.resizeObserver.observe(this.container);\n }\n\n private setupMutationObserver(): void {\n if (!this.container || typeof MutationObserver === 'undefined') return;\n\n this.mutationObserver = new MutationObserver(() => {\n this.initializeGridItems();\n this.scheduleLayout();\n });\n\n this.mutationObserver.observe(this.container, {\n childList: true,\n subtree: false,\n });\n }\n\n private handleResize(): void {\n this.scheduleLayout();\n }\n\n private initializeContainerStyles(): void {\n if (!this.container) return;\n\n this.container.style.gridAutoRows = '1px';\n this.container.style.contain = 'layout';\n this.container.style.alignItems = 'start';\n }\n\n private measureContainerMetrics(): void {\n if (!this.container) return;\n\n const cs = getComputedStyle(this.container);\n\n this.rowGap = this.parseCssPixelValue(cs.rowGap);\n\n const parsedRowHeight = this.parseCssPixelValue(cs.gridAutoRows);\n\n this.rowHeight = parsedRowHeight > 0 ? parsedRowHeight : 1;\n }\n\n private initializeGridItems(): void {\n if (!this.container) return;\n\n this.gridItems = Array.from(this.container.children).filter(\n (item): item is HTMLElement => item instanceof HTMLElement,\n );\n\n this.gridItems.forEach((item) => {\n const images = item.querySelectorAll('img');\n\n images.forEach((image) => {\n this.observeImage(image);\n });\n });\n }\n\n private observeImage(image: HTMLImageElement): void {\n if (image.complete || this.observedImages.has(image)) return;\n\n this.observedImages.add(image);\n\n const onLoad = (): void => this.scheduleLayout();\n\n if (this.imageAbortController) {\n image.addEventListener('load', onLoad, {\n once: true,\n signal: this.imageAbortController.signal,\n });\n\n return;\n }\n\n image.addEventListener('load', onLoad, { once: true });\n }\n\n private parseCssPixelValue(value: string): number {\n const parsedValue = Number.parseFloat(value);\n\n return Number.isFinite(parsedValue) ? parsedValue : 0;\n }\n\n private storeContainerStyles(): void {\n if (!this.container) return;\n\n this.originalContainerStyles = {\n gridAutoRows: this.container.style.gridAutoRows || '',\n contain: this.container.style.contain || '',\n alignItems: this.container.style.alignItems || '',\n };\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "masonry-simple",
3
- "version": "4.4.0",
3
+ "version": "5.0.0",
4
4
  "description": "MasonrySimple implements a simple system for placing masonry style elements using CSS Grid. Masonry placement is used for dynamic grids where elements may have different heights and need to be placed neatly without gaps.",
5
5
  "author": "ux-ui.pro",
6
6
  "license": "MIT",
@@ -13,44 +13,58 @@
13
13
  },
14
14
  "homepage": "https://github.com/ux-ui-pro/masonry-simple",
15
15
  "sideEffects": false,
16
+ "type": "module",
17
+ "packageManager": "npm@10.9.3",
18
+ "engines": {
19
+ "node": ">=20.19.0"
20
+ },
16
21
  "scripts": {
17
22
  "clean": "rimraf dist",
18
- "build": "vite build",
19
- "lint:js": "eslint src/**/*.{ts,js}",
20
- "lint:fix:js": "eslint src/**/*.{ts,js} --fix",
21
- "format:js": "prettier --write src/**/*.{ts,js}",
22
- "lint:fix": "yarn lint:fix:js && yarn format:js"
23
+ "build": "tsdown",
24
+ "verify": "npm run lint && npm run typecheck && npm run build && npm run test:smoke && npm run pack:check",
25
+ "lint": "biome check src tests tsdown.config.ts",
26
+ "lint:fix": "biome check --write src tests tsdown.config.ts",
27
+ "format": "biome format --write src tests tsdown.config.ts",
28
+ "typecheck": "tsc -p tsconfig.json --noEmit",
29
+ "test:smoke": "node --test \"tests/**/*.js\"",
30
+ "pack:check": "npm pack --dry-run && publint && attw --pack . --profile node16 --ignore-rules false-export-default",
31
+ "prepublishOnly": "npm run clean && npm run verify"
23
32
  },
24
33
  "source": "src/index.ts",
25
- "main": "dist/index.cjs.js",
26
- "module": "dist/index.es.js",
27
- "types": "dist/index.d.ts",
34
+ "main": "./dist/index.cjs",
35
+ "module": "./dist/index.js",
36
+ "types": "./dist/index.d.ts",
28
37
  "exports": {
29
38
  ".": {
30
- "require": "./dist/index.cjs.js",
31
- "import": "./dist/index.es.js"
32
- },
33
- "./dist/*": "./dist/*"
39
+ "import": {
40
+ "types": "./dist/index.d.ts",
41
+ "default": "./dist/index.js"
42
+ },
43
+ "require": {
44
+ "types": "./dist/index.d.cts",
45
+ "default": "./dist/index.cjs"
46
+ }
47
+ }
34
48
  },
35
49
  "files": [
36
- "dist/"
50
+ "dist",
51
+ "README.md",
52
+ "LICENSE"
37
53
  ],
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
38
57
  "devDependencies": {
39
- "@eslint/js": "9.31.0",
40
- "@rollup/plugin-terser": "0.4.4",
41
- "@types/node": "24.0.14",
42
- "@typescript-eslint/eslint-plugin": "8.37.0",
43
- "@typescript-eslint/parser": "8.37.0",
44
- "eslint": "9.31.0",
45
- "eslint-config-prettier": "10.1.5",
46
- "eslint-import-resolver-typescript": "4.4.4",
47
- "eslint-plugin-import": "2.32.0",
48
- "globals": "16.3.0",
49
- "prettier": "3.6.2",
50
- "rimraf": "6.0.1",
51
- "typescript": "5.8.3",
52
- "vite": "7.0.4",
53
- "vite-plugin-dts": "4.5.4"
58
+ "@arethetypeswrong/cli": "^0.18.3",
59
+ "@biomejs/biome": "2.4.16",
60
+ "@types/node": "25.9.2",
61
+ "@ux-ui/biome-config": "^0.1.0",
62
+ "@ux-ui/tsconfig-base": "^0.1.0",
63
+ "@ux-ui/tsdown-config": "^0.1.0",
64
+ "publint": "^0.3.21",
65
+ "rimraf": "6.1.3",
66
+ "tsdown": "0.22.3",
67
+ "typescript": "6.0.3"
54
68
  },
55
69
  "keywords": [
56
70
  "masonry",
package/dist/index.cjs.js DELETED
@@ -1 +0,0 @@
1
- "use strict";module.exports=class{container;gridItems=[];rowHeight=1;rowGap=0;resizeScheduled=!1;resizeObserver=null;mutationObserver=null;abortController=null;originalAutoRows="";constructor(t={}){this.container=t.container instanceof HTMLElement?t.container:typeof t.container=="string"?document.querySelector(t.container):document.querySelector(".masonry")}init(){this.container&&(this.setupAbortController(),this.storeAndSetAutoRows(),this.initializeContainerStyles(),this.initializeGridItems(),this.setupResizeObserver(),this.setupMutationObserver(),this.resizeAllItems())}refresh(){this.initializeGridItems(),this.resizeAllItems()}destroy(){this.container&&(this.abortController?.abort(),this.container.style.gridAutoRows=this.originalAutoRows,this.container.style.contain="",this.container.style.alignItems="",this.gridItems.forEach(t=>t.style.gridRowEnd=""),this.gridItems=[],this.resizeObserver=null,this.mutationObserver=null,this.abortController=null,this.originalAutoRows="")}handleResize(){this.resizeScheduled||(this.resizeScheduled=!0,requestAnimationFrame(()=>{this.resizeAllItems(),this.resizeScheduled=!1}))}resizeAllItems(){this.container&&(this.container.style.alignItems="start",this.gridItems.forEach(t=>{const e=Math.ceil((t.clientHeight+this.rowGap)/(this.rowHeight+this.rowGap));t.style.gridRowEnd=`span ${e}`}))}setupAbortController(){this.abortController=new AbortController,this.abortController.signal.addEventListener("abort",()=>{this.resizeObserver?.disconnect(),this.mutationObserver?.disconnect()})}setupResizeObserver(){this.resizeObserver=new ResizeObserver(()=>this.handleResize()),this.resizeObserver.observe(this.container)}setupMutationObserver(){this.mutationObserver=new MutationObserver(()=>{this.initializeGridItems(),this.resizeAllItems()}),this.mutationObserver.observe(this.container,{childList:!0,subtree:!1})}initializeContainerStyles(){if(!this.container)return;const t=getComputedStyle(this.container);this.rowGap=parseInt(t.rowGap,10)||0;const e=parseInt(t.gridAutoRows,10);this.rowHeight=Number.isNaN(e)?this.rowHeight:e,this.container.style.contain="layout"}initializeGridItems(){this.container&&(this.gridItems=Array.from(this.container.children),this.gridItems.forEach(t=>{const e=t.querySelector("img");e&&!e.complete&&e.addEventListener("load",()=>this.resizeAllItems(),{once:!0})}))}storeAndSetAutoRows(){this.container&&(this.originalAutoRows||(this.originalAutoRows=this.container.style.gridAutoRows||""),this.container.style.gridAutoRows="1px")}};
package/dist/index.es.js DELETED
@@ -1,66 +0,0 @@
1
- class s {
2
- container;
3
- gridItems = [];
4
- rowHeight = 1;
5
- rowGap = 0;
6
- resizeScheduled = !1;
7
- resizeObserver = null;
8
- mutationObserver = null;
9
- abortController = null;
10
- originalAutoRows = "";
11
- constructor(t = {}) {
12
- this.container = t.container instanceof HTMLElement ? t.container : typeof t.container == "string" ? document.querySelector(t.container) : document.querySelector(".masonry");
13
- }
14
- init() {
15
- this.container && (this.setupAbortController(), this.storeAndSetAutoRows(), this.initializeContainerStyles(), this.initializeGridItems(), this.setupResizeObserver(), this.setupMutationObserver(), this.resizeAllItems());
16
- }
17
- refresh() {
18
- this.initializeGridItems(), this.resizeAllItems();
19
- }
20
- destroy() {
21
- this.container && (this.abortController?.abort(), this.container.style.gridAutoRows = this.originalAutoRows, this.container.style.contain = "", this.container.style.alignItems = "", this.gridItems.forEach((t) => t.style.gridRowEnd = ""), this.gridItems = [], this.resizeObserver = null, this.mutationObserver = null, this.abortController = null, this.originalAutoRows = "");
22
- }
23
- handleResize() {
24
- this.resizeScheduled || (this.resizeScheduled = !0, requestAnimationFrame(() => {
25
- this.resizeAllItems(), this.resizeScheduled = !1;
26
- }));
27
- }
28
- resizeAllItems() {
29
- this.container && (this.container.style.alignItems = "start", this.gridItems.forEach((t) => {
30
- const e = Math.ceil((t.clientHeight + this.rowGap) / (this.rowHeight + this.rowGap));
31
- t.style.gridRowEnd = `span ${e}`;
32
- }));
33
- }
34
- setupAbortController() {
35
- this.abortController = new AbortController(), this.abortController.signal.addEventListener("abort", () => {
36
- this.resizeObserver?.disconnect(), this.mutationObserver?.disconnect();
37
- });
38
- }
39
- setupResizeObserver() {
40
- this.resizeObserver = new ResizeObserver(() => this.handleResize()), this.resizeObserver.observe(this.container);
41
- }
42
- setupMutationObserver() {
43
- this.mutationObserver = new MutationObserver(() => {
44
- this.initializeGridItems(), this.resizeAllItems();
45
- }), this.mutationObserver.observe(this.container, { childList: !0, subtree: !1 });
46
- }
47
- initializeContainerStyles() {
48
- if (!this.container) return;
49
- const t = getComputedStyle(this.container);
50
- this.rowGap = parseInt(t.rowGap, 10) || 0;
51
- const e = parseInt(t.gridAutoRows, 10);
52
- this.rowHeight = Number.isNaN(e) ? this.rowHeight : e, this.container.style.contain = "layout";
53
- }
54
- initializeGridItems() {
55
- this.container && (this.gridItems = Array.from(this.container.children), this.gridItems.forEach((t) => {
56
- const e = t.querySelector("img");
57
- e && !e.complete && e.addEventListener("load", () => this.resizeAllItems(), { once: !0 });
58
- }));
59
- }
60
- storeAndSetAutoRows() {
61
- this.container && (this.originalAutoRows || (this.originalAutoRows = this.container.style.gridAutoRows || ""), this.container.style.gridAutoRows = "1px");
62
- }
63
- }
64
- export {
65
- s as default
66
- };