@a-type/ui 2.1.6 → 2.1.8
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/cjs/components/icon/generated/IconSpritesheet.js +1 -1
- package/dist/cjs/components/icon/generated/IconSpritesheet.js.map +1 -1
- package/dist/cjs/components/icon/generated/iconNames.d.ts +1 -1
- package/dist/cjs/components/icon/generated/iconNames.js +4 -0
- package/dist/cjs/components/icon/generated/iconNames.js.map +1 -1
- package/dist/cjs/components/masonry/masonry.js +104 -79
- package/dist/cjs/components/masonry/masonry.js.map +1 -1
- package/dist/esm/components/icon/generated/IconSpritesheet.js +1 -1
- package/dist/esm/components/icon/generated/IconSpritesheet.js.map +1 -1
- package/dist/esm/components/icon/generated/iconNames.d.ts +1 -1
- package/dist/esm/components/icon/generated/iconNames.js +4 -0
- package/dist/esm/components/icon/generated/iconNames.js.map +1 -1
- package/dist/esm/components/masonry/masonry.js +105 -80
- package/dist/esm/components/masonry/masonry.js.map +1 -1
- package/package.json +1 -1
- package/src/components/icon/generated/IconSpritesheet.tsx +28 -0
- package/src/components/icon/generated/iconNames.ts +4 -0
- package/src/components/masonry/masonry.tsx +135 -99
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { debounce } from '@a-type/utils';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CSSProperties,
|
|
5
|
+
ReactNode,
|
|
6
|
+
useEffect,
|
|
7
|
+
useLayoutEffect,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from 'react';
|
|
4
11
|
|
|
5
12
|
interface Layout {
|
|
6
13
|
attach(container: HTMLElement): () => void;
|
|
@@ -12,70 +19,19 @@ interface MasonryLayoutConfig {
|
|
|
12
19
|
gap: number;
|
|
13
20
|
}
|
|
14
21
|
|
|
15
|
-
class MasonryLayout implements Layout {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
private childSizeObserver: ResizeObserver;
|
|
19
|
-
private childMutationObserver: MutationObserver;
|
|
20
|
-
|
|
21
|
-
private container: HTMLElement | null = null;
|
|
22
|
-
|
|
23
|
-
private columns: number = 0;
|
|
22
|
+
abstract class MasonryLayout implements Layout {
|
|
23
|
+
protected container: HTMLElement | null = null;
|
|
24
|
+
protected columns: number = 0;
|
|
24
25
|
|
|
25
26
|
constructor(private config: MasonryLayoutConfig) {
|
|
26
27
|
this.columns =
|
|
27
28
|
typeof config.columns === 'function' ? config.columns(0) : config.columns;
|
|
28
|
-
this.childSizeObserver = new ResizeObserver(this.handleChildResize);
|
|
29
|
-
this.childMutationObserver = new MutationObserver(this.relayout);
|
|
30
29
|
this.relayout();
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
attach
|
|
34
|
-
this.containerResizeObserver?.disconnect();
|
|
35
|
-
this.containerMutationObserver?.disconnect();
|
|
36
|
-
|
|
37
|
-
this.container = container;
|
|
38
|
-
|
|
39
|
-
this.containerResizeObserver = new ResizeObserver(
|
|
40
|
-
this.handleContainerResize,
|
|
41
|
-
);
|
|
42
|
-
this.containerMutationObserver = new MutationObserver(
|
|
43
|
-
this.handleContainerMutation,
|
|
44
|
-
);
|
|
45
|
-
this.containerResizeObserver.observe(container);
|
|
46
|
-
this.containerMutationObserver.observe(container, { childList: true });
|
|
47
|
-
|
|
48
|
-
container.style.setProperty('position', 'relative');
|
|
49
|
-
container.style.setProperty('overflow', 'hidden');
|
|
50
|
-
container.style.setProperty('visibility', 'visible');
|
|
51
|
-
container.childNodes.forEach((node) => {
|
|
52
|
-
if (node instanceof HTMLElement) {
|
|
53
|
-
this.setupChild(node);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
this.updateFromContainerSize(container.offsetWidth);
|
|
58
|
-
|
|
59
|
-
this.relayout();
|
|
32
|
+
abstract attach(container: HTMLElement): () => void;
|
|
60
33
|
|
|
61
|
-
|
|
62
|
-
this.containerResizeObserver?.disconnect();
|
|
63
|
-
this.containerMutationObserver?.disconnect();
|
|
64
|
-
container.style.removeProperty('position');
|
|
65
|
-
container.style.removeProperty('overflow');
|
|
66
|
-
this.container = null;
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
private setupChild = (child: HTMLElement) => {
|
|
71
|
-
child.style.setProperty('position', 'absolute');
|
|
72
|
-
// hide until laid out
|
|
73
|
-
child.style.setProperty('visibility', 'hidden');
|
|
74
|
-
this.childSizeObserver.observe(child);
|
|
75
|
-
this.childMutationObserver.observe(child, {
|
|
76
|
-
attributeFilter: ['data-span'],
|
|
77
|
-
});
|
|
78
|
-
};
|
|
34
|
+
abstract setupChild(child: HTMLElement): void;
|
|
79
35
|
|
|
80
36
|
updateConfig = (config: MasonryLayoutConfig) => {
|
|
81
37
|
const gapChanged = config.gap !== this.config.gap;
|
|
@@ -89,12 +45,7 @@ class MasonryLayout implements Layout {
|
|
|
89
45
|
}
|
|
90
46
|
};
|
|
91
47
|
|
|
92
|
-
|
|
93
|
-
const containerWidth = entries[0].contentRect.width;
|
|
94
|
-
this.updateFromContainerSize(containerWidth);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
private updateFromContainerSize = (containerWidth: number) => {
|
|
48
|
+
protected updateFromContainerSize = (containerWidth: number) => {
|
|
98
49
|
if (typeof this.config.columns === 'function') {
|
|
99
50
|
const newValue = this.config.columns(containerWidth);
|
|
100
51
|
if (newValue !== this.columns) {
|
|
@@ -106,35 +57,7 @@ class MasonryLayout implements Layout {
|
|
|
106
57
|
return false;
|
|
107
58
|
};
|
|
108
59
|
|
|
109
|
-
|
|
110
|
-
for (const entry of entries) {
|
|
111
|
-
entry.addedNodes.forEach((node) => {
|
|
112
|
-
if (node instanceof HTMLElement) {
|
|
113
|
-
this.setupChild(node);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
entry.removedNodes.forEach((node) => {
|
|
117
|
-
if (node instanceof HTMLElement) {
|
|
118
|
-
this.childSizeObserver?.unobserve(node);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
this.relayout();
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
private handleChildResize = (entries: ResizeObserverEntry[]) => {
|
|
126
|
-
// only worry about height changes
|
|
127
|
-
for (const entry of entries) {
|
|
128
|
-
const lastSeenHeight = entry.target.getAttribute('data-last-height');
|
|
129
|
-
const currentHeight = entry.contentRect.height;
|
|
130
|
-
entry.target.setAttribute('data-last-height', currentHeight.toString());
|
|
131
|
-
if (lastSeenHeight && lastSeenHeight !== currentHeight.toString()) {
|
|
132
|
-
this.relayout();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
private relayout = debounce(() => {
|
|
60
|
+
protected relayout = debounce(() => {
|
|
138
61
|
if (!this.container) {
|
|
139
62
|
return;
|
|
140
63
|
}
|
|
@@ -187,11 +110,115 @@ class MasonryLayout implements Layout {
|
|
|
187
110
|
}, 100);
|
|
188
111
|
}
|
|
189
112
|
|
|
190
|
-
class
|
|
191
|
-
|
|
113
|
+
class ClientLayout extends MasonryLayout {
|
|
114
|
+
private containerResizeObserver: ResizeObserver | null = null;
|
|
115
|
+
private containerMutationObserver: MutationObserver | null = null;
|
|
116
|
+
private childSizeObserver: ResizeObserver;
|
|
117
|
+
private childMutationObserver: MutationObserver;
|
|
118
|
+
|
|
119
|
+
constructor(config: MasonryLayoutConfig) {
|
|
120
|
+
super(config);
|
|
121
|
+
this.childSizeObserver = new ResizeObserver(this.handleChildResize);
|
|
122
|
+
this.childMutationObserver = new MutationObserver(this.relayout);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private handleContainerMutation = (entries: MutationRecord[]) => {
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
entry.addedNodes.forEach((node) => {
|
|
128
|
+
if (node instanceof HTMLElement) {
|
|
129
|
+
this.setupChild(node);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
entry.removedNodes.forEach((node) => {
|
|
133
|
+
if (node instanceof HTMLElement) {
|
|
134
|
+
this.childSizeObserver?.unobserve(node);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
this.relayout();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
private handleChildResize = (entries: ResizeObserverEntry[]) => {
|
|
142
|
+
// only worry about height changes
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
const lastSeenHeight = entry.target.getAttribute('data-last-height');
|
|
145
|
+
const currentHeight = entry.contentRect.height;
|
|
146
|
+
entry.target.setAttribute('data-last-height', currentHeight.toString());
|
|
147
|
+
if (lastSeenHeight && lastSeenHeight !== currentHeight.toString()) {
|
|
148
|
+
this.relayout();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
attach = (container: HTMLElement) => {
|
|
154
|
+
this.containerResizeObserver?.disconnect();
|
|
155
|
+
this.containerMutationObserver?.disconnect();
|
|
156
|
+
|
|
157
|
+
this.container = container;
|
|
158
|
+
|
|
159
|
+
this.containerResizeObserver = new ResizeObserver(
|
|
160
|
+
this.handleContainerResize,
|
|
161
|
+
);
|
|
162
|
+
this.containerMutationObserver = new MutationObserver(
|
|
163
|
+
this.handleContainerMutation,
|
|
164
|
+
);
|
|
165
|
+
this.containerResizeObserver.observe(container);
|
|
166
|
+
this.containerMutationObserver.observe(container, { childList: true });
|
|
167
|
+
|
|
168
|
+
container.style.setProperty('position', 'relative');
|
|
169
|
+
container.style.setProperty('overflow', 'hidden');
|
|
170
|
+
container.style.setProperty('visibility', 'visible');
|
|
171
|
+
container.childNodes.forEach((node) => {
|
|
172
|
+
if (node instanceof HTMLElement) {
|
|
173
|
+
this.setupChild(node);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
this.updateFromContainerSize(container.offsetWidth);
|
|
178
|
+
|
|
179
|
+
return () => {
|
|
180
|
+
this.containerResizeObserver?.disconnect();
|
|
181
|
+
this.containerMutationObserver?.disconnect();
|
|
182
|
+
container.style.removeProperty('position');
|
|
183
|
+
container.style.removeProperty('overflow');
|
|
184
|
+
this.container = null;
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
setupChild = (child: HTMLElement) => {
|
|
189
|
+
child.style.setProperty('position', 'absolute');
|
|
190
|
+
// hide until laid out
|
|
191
|
+
child.style.setProperty('visibility', 'hidden');
|
|
192
|
+
this.childSizeObserver.observe(child);
|
|
193
|
+
this.childMutationObserver.observe(child, {
|
|
194
|
+
attributeFilter: ['data-span'],
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
private handleContainerResize = (entries: ResizeObserverEntry[]) => {
|
|
199
|
+
const containerWidth = entries[0].contentRect.width;
|
|
200
|
+
this.updateFromContainerSize(containerWidth);
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
class ServerLayout extends MasonryLayout {
|
|
205
|
+
attach = (container: HTMLElement): (() => void) => {
|
|
206
|
+
this.container = container;
|
|
207
|
+
this.container.style.setProperty('position', 'relative');
|
|
208
|
+
this.container.style.setProperty('overflow', 'hidden');
|
|
209
|
+
this.container.style.setProperty('visibility', 'visible');
|
|
210
|
+
container.childNodes.forEach((node) => {
|
|
211
|
+
if (node instanceof HTMLElement) {
|
|
212
|
+
this.setupChild(node);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
this.updateFromContainerSize(container.offsetWidth);
|
|
192
216
|
return () => {};
|
|
217
|
+
};
|
|
218
|
+
setupChild(child: HTMLElement): void {
|
|
219
|
+
child.style.setProperty('position', 'absolute');
|
|
220
|
+
child.style.setProperty('visibility', 'visible');
|
|
193
221
|
}
|
|
194
|
-
updateConfig(config: MasonryLayoutConfig): void {}
|
|
195
222
|
}
|
|
196
223
|
|
|
197
224
|
function pickTrack(tracks: number[], trackSpan: number) {
|
|
@@ -226,15 +253,15 @@ export function Masonry({
|
|
|
226
253
|
}: MasonryProps) {
|
|
227
254
|
const [layout] = useState<Layout>(() => {
|
|
228
255
|
if (typeof window === 'undefined') {
|
|
229
|
-
return new ServerLayout();
|
|
256
|
+
return new ServerLayout({ columns, gap });
|
|
230
257
|
}
|
|
231
|
-
return new
|
|
258
|
+
return new ClientLayout({ columns, gap });
|
|
232
259
|
});
|
|
233
|
-
|
|
260
|
+
useIsomorphicLayoutEffect(() => {
|
|
234
261
|
layout.updateConfig({ columns, gap });
|
|
235
262
|
}, [layout, columns, gap]);
|
|
236
263
|
const ref = useRef<HTMLDivElement>(null);
|
|
237
|
-
|
|
264
|
+
useIsomorphicLayoutEffect(() => {
|
|
238
265
|
if (ref.current) {
|
|
239
266
|
return layout.attach(ref.current);
|
|
240
267
|
}
|
|
@@ -254,3 +281,12 @@ export function Masonry({
|
|
|
254
281
|
export function masonrySpan(span: number) {
|
|
255
282
|
return { 'data-span': span };
|
|
256
283
|
}
|
|
284
|
+
|
|
285
|
+
function useIsomorphicLayoutEffect(
|
|
286
|
+
effect: () => void | (() => void),
|
|
287
|
+
deps: any[] = [],
|
|
288
|
+
) {
|
|
289
|
+
const isBrowser = typeof window !== 'undefined';
|
|
290
|
+
const useIsoEffect = isBrowser ? useLayoutEffect : useEffect;
|
|
291
|
+
return useIsoEffect(effect, deps);
|
|
292
|
+
}
|