@behold/widget 0.5.65 → 0.5.67

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.
@@ -1,339 +0,0 @@
1
- import { c as createElement, a as setClasses, f as forceLayout, s as setCssVars } from './index-zGq7k0wG.js';
2
- import { B as BaseWidget, I as ImagePost, V as VideoPost, A as AlbumPost, b as baseGridStyles, g as getMedianHSL } from './base-D7ZLOWQ5.js';
3
-
4
- var css_248z = ".post--placeholder{background-color:#dedede;height:0;padding-bottom:calc(100%/var(--post-aspect-ratio))}";
5
- var styles = css_248z;
6
-
7
- /*
8
- * Grid
9
- */
10
- class Grid extends BaseWidget {
11
- label = 'Grid';
12
- // Provided
13
- widgetSettings;
14
- feedMetadata;
15
- posts;
16
- previewLoadingColors = null;
17
- // Internal
18
- containerEl;
19
- postEls;
20
- popoverGalleryEl;
21
- appliedBreakpoint;
22
- medianPaletteHSL;
23
- constructor() {
24
- super();
25
- // Define which props will trigger _handlePropChange
26
- this.onPropChange(this._handlePropChange, ['widgetSettings', 'feedMetadata', 'posts', 'previewLoadingColors'], ['widgetSettings', 'feedMetadata', 'posts'], this.setup);
27
- // Register child components
28
- ImagePost.register();
29
- VideoPost.register();
30
- AlbumPost.register();
31
- // Bind event handlers
32
- this._handlePostClick = this._handlePostClick.bind(this);
33
- // Listen to focus
34
- this.addEventListener('post-focus-next', this._handleFocusNextPost);
35
- this.addEventListener('post-focus-previous', this._handleFocusPreviousPost);
36
- // Connect
37
- this.onConnect(() => {
38
- this.containerEl = createElement({
39
- type: 'figure',
40
- classes: 'posts',
41
- });
42
- this.renderWidget(this.containerEl, [baseGridStyles, styles]);
43
- // A11y stuff
44
- this.setAttribute('tabindex', '0');
45
- this.setAttribute('aria-label', 'Gallery of Instagram posts. Shift + arrow keys to navigate');
46
- // listen to resize
47
- this.onResize(this, this, this._handleResize);
48
- });
49
- }
50
- /*
51
- * Run initial setup that requires data to be fully loaded
52
- */
53
- setup() {
54
- // Calculate medianPaletteHSL
55
- if (this.widgetSettings.loadingColor !== 'transparent') {
56
- const palettes = this.posts
57
- .filter((post) => post.colorPalette)
58
- .map((post) => post.colorPalette);
59
- this.medianPaletteHSL = getMedianHSL(palettes, this.widgetSettings.loadingColor);
60
- }
61
- // Find initial breakpoint
62
- const matchingBreakpoint = this.getMatchingBreakpoint(this.offsetWidth, this.widgetSettings.breakpoints);
63
- // Render posts with current breakpoint styles
64
- this.renderBreakpoint(matchingBreakpoint);
65
- // Pass settings and metadata to posts
66
- if (this.postEls) {
67
- this.postEls.forEach((postEl) => {
68
- postEl.widgetSettings = this.widgetSettings;
69
- postEl.feedMetadata = this.feedMetadata;
70
- });
71
- }
72
- // Enable popoverGallery if necessary
73
- if (this.widgetSettings.onPostClick === 'openPopupGallery') {
74
- this.enablePopoverGallery();
75
- }
76
- }
77
- /*
78
- * Handle prop change
79
- */
80
- _handlePropChange({ changedProp, oldValue, newValue }) {
81
- switch (changedProp) {
82
- case 'posts':
83
- this._handlePostsChange({ oldValue, newValue });
84
- break;
85
- case 'widgetSettings':
86
- this._handleSettingsChange(oldValue, newValue);
87
- break;
88
- case 'feedMetadata':
89
- this._handleMetadataChange();
90
- break;
91
- case 'previewLoadingColors':
92
- this.postEls.forEach((postEl) => {
93
- postEl.previewLoadingColors = this.previewLoadingColors;
94
- });
95
- setClasses(this, {
96
- 'is-previewing-loading-colors': !!this.previewLoadingColors,
97
- });
98
- break;
99
- }
100
- }
101
- /*
102
- * Handle posts change
103
- */
104
- _handlePostsChange({ oldValue, newValue }) {
105
- this.postEls = [];
106
- this.renderBreakpoint(this.getMatchingBreakpoint(this.offsetWidth, this.widgetSettings.breakpoints), true);
107
- if (this.popoverGalleryEl) {
108
- this.popoverGalleryEl.posts = this.posts;
109
- }
110
- }
111
- /*
112
- * Handle settings change
113
- */
114
- _handleSettingsChange(oldValue, newValue) {
115
- if (oldValue?.onPostClick !== 'openPopupGallery' &&
116
- newValue?.onPostClick === 'openPopupGallery') {
117
- this.enablePopoverGallery();
118
- }
119
- const matchingBreakpoint = this.getMatchingBreakpoint(this.offsetWidth, this.widgetSettings.breakpoints);
120
- this.renderBreakpoint(matchingBreakpoint);
121
- this.postEls.forEach((postEl) => {
122
- postEl.widgetSettings = this.widgetSettings;
123
- });
124
- if (this.popoverGalleryEl) {
125
- this.popoverGalleryEl.widgetSettings = this.widgetSettings;
126
- }
127
- }
128
- /*
129
- * Handle settings change
130
- */
131
- _handleMetadataChange() {
132
- let description = `from @${this.feedMetadata.username}`;
133
- if (this.feedMetadata.hashtags?.length) {
134
- description = `from hashtag${this.feedMetadata.hashtags.length > 1 ? 's' : ''} ${this.feedMetadata.hashtags.join(', ')}`;
135
- }
136
- this.setAttribute('aria-label', `Gallery of Instagram posts ${description}. Shift + arrow keys to navigate`);
137
- this.postEls.forEach((postEl) => {
138
- postEl.feedMetadata = this.feedMetadata;
139
- });
140
- if (this.popoverGalleryEl) {
141
- this.popoverGalleryEl.feedMetadata = this.feedMetadata;
142
- }
143
- }
144
- /**
145
- * Enable popup carousel
146
- */
147
- async enablePopoverGallery() {
148
- if (this.popoverGalleryEl)
149
- return;
150
- const { default: PopoverGallery } = await import('./PopoverGallery-oswhxqGK.js');
151
- PopoverGallery.register();
152
- this.popoverGalleryEl = document.createElement('behold-popover-gallery');
153
- Object.assign(this.popoverGalleryEl, {
154
- widgetSettings: this.widgetSettings,
155
- feedMetadata: this.feedMetadata,
156
- posts: this.posts,
157
- closeFocusEl: this,
158
- onSlideChange: (slideIndex) => {
159
- this.popoverGalleryEl.closeFocusEl = this.postEls[slideIndex];
160
- },
161
- });
162
- }
163
- /*
164
- * Handle resize
165
- */
166
- _handleResize(entry) {
167
- const widgetWidth = entry.contentBoxSize?.[0]?.inlineSize;
168
- const breakpoints = this.widgetSettings.breakpoints;
169
- const matchingBreakpoint = this.getMatchingBreakpoint(widgetWidth, breakpoints);
170
- this.renderBreakpoint(matchingBreakpoint);
171
- }
172
- /**
173
- * Handle prev post focus
174
- */
175
- _handleFocusPreviousPost() {
176
- if (!this.postEls.length)
177
- return;
178
- let currentFocusIndex = [...this.containerEl.children].indexOf(this.shadow.activeElement?.parentElement);
179
- if (currentFocusIndex > 0) {
180
- currentFocusIndex = currentFocusIndex - 1;
181
- }
182
- else {
183
- currentFocusIndex = this.postEls.length - 1;
184
- }
185
- this.postEls[currentFocusIndex].focus();
186
- }
187
- /**
188
- * Handle next post focus
189
- */
190
- _handleFocusNextPost() {
191
- if (!this.postEls.length)
192
- return;
193
- let currentFocusIndex = [...this.containerEl.children].indexOf(this.shadow.activeElement?.parentElement);
194
- if (currentFocusIndex > -1 && currentFocusIndex < this.postEls.length - 1) {
195
- currentFocusIndex = currentFocusIndex + 1;
196
- }
197
- else {
198
- currentFocusIndex = 0;
199
- }
200
- this.postEls[currentFocusIndex].focus();
201
- }
202
- /**
203
- * Handle post click
204
- */
205
- _handlePostClick(post) {
206
- this.popoverGalleryEl.open(this.postEls.indexOf(post), post);
207
- }
208
- /*
209
- * Update postEls
210
- */
211
- renderPosts(breakpoint) {
212
- this.postEls = this.createPostEls(breakpoint);
213
- this.raf(() => {
214
- this.containerEl.beholdReplaceChildren(...this.postEls);
215
- }, 'renderPosts');
216
- }
217
- /*
218
- * Get breakpoint that matches a width
219
- */
220
- getMatchingBreakpoint(width, breakpoints) {
221
- const matchingBreakpoint = Object.entries(breakpoints)
222
- .map(([key, value]) => ({ width: key, ...value }))
223
- .filter((bp) => bp.width !== 'default')
224
- .sort((a, b) => parseInt(b.width) - parseInt(a.width))
225
- .reduce((acc, curr) => {
226
- return width <= parseInt(curr.width) ? curr : acc;
227
- },
228
- // numPosts doesn't exist on default breakpoint for some reason
229
- // @todo fix that
230
- { numPosts: this.posts.length, ...breakpoints.default });
231
- return { numPosts: this.posts.length, ...breakpoints.default, ...matchingBreakpoint };
232
- }
233
- /*
234
- * Render posts with breakpoint
235
- */
236
- renderBreakpoint(breakpoint, forceRender = false) {
237
- if (!this.posts || !this.containerEl)
238
- return;
239
- if (!this.postEls?.length)
240
- forceRender = true;
241
- const applied = this.appliedBreakpoint;
242
- const { numColumns, gap, borderRadius, numPosts, postAspectRatio } = breakpoint;
243
- // If we're reducing the # of posts, first remove them all to prevent fouc
244
- if (applied?.numPosts > numPosts) {
245
- this.containerEl.beholdReplaceChildren();
246
- forceLayout();
247
- }
248
- // First apply container styles to prevent FOUC
249
- switch (this.widgetSettings.alignment) {
250
- case 'left':
251
- this.style.justifyContent = 'flex-start';
252
- break;
253
- case 'right':
254
- this.style.justifyContent = 'flex-end';
255
- break;
256
- default:
257
- this.style.justifyContent = 'center';
258
- break;
259
- }
260
- if (this.widgetSettings.maxWidth &&
261
- this.widgetSettings.constrainWidth &&
262
- this.containerEl.style.maxWidth !== `${this.widgetSettings.maxWidth}px`) {
263
- this.containerEl.style.maxWidth = `${this.widgetSettings.maxWidth}px`;
264
- }
265
- if (!this.widgetSettings.constrainWidth) {
266
- this.containerEl.style.maxWidth = '';
267
- }
268
- this.containerEl.style.gridTemplateColumns = `repeat(${numColumns}, 1fr)`;
269
- this.containerEl.style.gap = `${gap.y}px ${gap.x}px`;
270
- this.containerEl.setAttribute('data-hover-effect', this.widgetSettings.hoverEffect);
271
- let aspectRatio = (postAspectRatio || [1, 1]).reduce((w, h) => w / h);
272
- setCssVars(this.containerEl, {
273
- '--post-border-radius': `${borderRadius}%`,
274
- '--post-aspect-ratio': `${aspectRatio}`,
275
- });
276
- // Then render posts into updated container
277
- if (applied?.numPosts !== numPosts || forceRender) {
278
- this.renderPosts(breakpoint);
279
- }
280
- this.postEls.forEach((el, i) => {
281
- el.hasRowGap = `${breakpoint.gap.y}` !== '0';
282
- });
283
- this.appliedBreakpoint = breakpoint;
284
- }
285
- /*
286
- * Create post Els based on a breakpoint
287
- */
288
- createPostEls(breakpoint) {
289
- const { numPosts, numColumns, gap, postAspectRatio } = breakpoint;
290
- const filteredArray = this.posts.filter((p, i) => {
291
- const numPosts = breakpoint?.numPosts || this.posts?.length || 200;
292
- return i < numPosts;
293
- });
294
- const postEls = filteredArray.map((post, i) => {
295
- const numRows = Math.ceil((numPosts || this.posts.length) / numColumns);
296
- let postType = 'behold-image-post';
297
- if (post.mediaType === 'VIDEO') {
298
- postType = 'behold-video-post';
299
- }
300
- if (post.mediaType === 'CAROUSEL_ALBUM') {
301
- postType = 'behold-album-post';
302
- }
303
- const postEl = createElement({
304
- type: postType,
305
- props: {
306
- post,
307
- widgetSettings: this.widgetSettings,
308
- feedMetadata: this.feedMetadata,
309
- medianPaletteHSL: this.medianPaletteHSL,
310
- onClick: this._handlePostClick,
311
- hasRowGap: `${gap.y}` !== '0',
312
- isLastRow: Math.ceil((i + 1) / numColumns) === numRows,
313
- index: i,
314
- aspectRatio: postAspectRatio || [1, 1],
315
- totalPosts: filteredArray?.length || numPosts || this.posts?.length || 0,
316
- },
317
- });
318
- return postEl;
319
- });
320
- while (postEls.length < breakpoint.numPosts) {
321
- const placeholderEl = createElement({
322
- classes: 'post post--placeholder',
323
- });
324
- postEls.push(placeholderEl);
325
- }
326
- return postEls;
327
- }
328
- /*
329
- * Register
330
- */
331
- static register(name = 'behold-grid') {
332
- if (!customElements.get(name)) {
333
- customElements.define(name, Grid);
334
- }
335
- return name;
336
- }
337
- }
338
-
339
- export { Grid as default };