@behold/widget 0.5.55 → 0.5.57
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/ElasticCarousel-WWMTzi-V.js +1032 -0
- package/dist/ErrorMessage-tHLrPf_h.js +110 -0
- package/dist/GalleryWall-Jlau7S7U.js +354 -0
- package/dist/Grid-2Aag90e0.js +338 -0
- package/dist/PopoverGallery-pssVDO_O.js +1905 -0
- package/dist/Widget.d.ts.map +1 -1
- package/dist/base-GZO73SkY.js +1220 -0
- package/dist/caret-right-S2XSTDFy.js +5 -0
- package/dist/index-R4lEDZFo.js +985 -0
- package/dist/index.js +1 -1
- package/dist/resizeObserver-OlrW1x9X.js +454 -0
- package/package.json +1 -1
- package/dist/PopoverGallery-8G1aF271.js +0 -1
- package/dist/index-EcHlmDLY.js +0 -1
- package/dist/resizeObserver--rsjm-GV.js +0 -1
@@ -0,0 +1,1032 @@
|
|
1
|
+
import { c as createElement, p as pushWithLimit, g as getAsyncRect, s as setCssVars, a as setClasses, h as hasChanges } from './index-R4lEDZFo.js';
|
2
|
+
import { B as BaseWidget, I as ImagePost, V as VideoPost, A as AlbumPost, b as baseGridStyles, g as getMedianHSL } from './base-GZO73SkY.js';
|
3
|
+
import { c as caretLeftIcon, a as caretRightIcon } from './caret-right-S2XSTDFy.js';
|
4
|
+
|
5
|
+
class Gyre {
|
6
|
+
containerEl;
|
7
|
+
slideEls;
|
8
|
+
height;
|
9
|
+
gap;
|
10
|
+
borderRadius;
|
11
|
+
applyBorderRadiusToContainer;
|
12
|
+
snapToSlide;
|
13
|
+
bindToScrollPos;
|
14
|
+
resizeObserver;
|
15
|
+
onSlideChange;
|
16
|
+
_containerWidth;
|
17
|
+
_contentWidth;
|
18
|
+
_slideContentEls;
|
19
|
+
_slideDimensions;
|
20
|
+
_isInitialized;
|
21
|
+
_isDragging;
|
22
|
+
_isScrolling;
|
23
|
+
_velocity;
|
24
|
+
_settledPosition;
|
25
|
+
_currentPosition;
|
26
|
+
_targetPosition;
|
27
|
+
_points;
|
28
|
+
_momentumArray;
|
29
|
+
_lastLoopRun;
|
30
|
+
_minSlideWidth;
|
31
|
+
_adjustment;
|
32
|
+
_numInitialSlides;
|
33
|
+
_frameRate;
|
34
|
+
_shrinkStartMultiplier;
|
35
|
+
_adjustmentMultiplier;
|
36
|
+
_windowHeight;
|
37
|
+
_containerRect;
|
38
|
+
_scrollProgress;
|
39
|
+
_abortScrollCheck;
|
40
|
+
_virtualize;
|
41
|
+
_raf;
|
42
|
+
anchorSlide;
|
43
|
+
finalAnchorSlide;
|
44
|
+
constructor({ onSlideChange }) {
|
45
|
+
this._slideDimensions = [];
|
46
|
+
this._isInitialized = false;
|
47
|
+
this._isDragging = false;
|
48
|
+
this._isScrolling = false;
|
49
|
+
this._velocity = 0;
|
50
|
+
this._settledPosition = 0;
|
51
|
+
this._currentPosition = 0;
|
52
|
+
this.anchorSlide = 0;
|
53
|
+
this._targetPosition = 0;
|
54
|
+
this._points = { start: 0, previous: 0 };
|
55
|
+
this._momentumArray = [];
|
56
|
+
this._lastLoopRun = 0;
|
57
|
+
this._frameRate = 1 / 60;
|
58
|
+
this._shrinkStartMultiplier = 3;
|
59
|
+
this._adjustmentMultiplier = 2;
|
60
|
+
this.borderRadius = 20;
|
61
|
+
this.applyBorderRadiusToContainer = false;
|
62
|
+
this.gap = 20;
|
63
|
+
this.height = 300;
|
64
|
+
this.snapToSlide = false;
|
65
|
+
this.bindToScrollPos = false;
|
66
|
+
this._minSlideWidth = 40;
|
67
|
+
this._adjustment = 0;
|
68
|
+
this._numInitialSlides = 0;
|
69
|
+
this._windowHeight = window.innerHeight;
|
70
|
+
this._containerRect = null;
|
71
|
+
this._scrollProgress = 0;
|
72
|
+
this._abortScrollCheck = () => { };
|
73
|
+
this._virtualize = false;
|
74
|
+
this.onSlideChange = onSlideChange;
|
75
|
+
let ResizeObserver = window.ResizeObserver;
|
76
|
+
if ('ResizeObserver' in window === false) {
|
77
|
+
// @ts-ignore
|
78
|
+
ResizeObserver = window.BeholdResizeObserver;
|
79
|
+
}
|
80
|
+
this.resizeObserver = new ResizeObserver((entries) => this.handleResize(entries));
|
81
|
+
this.handlePointerdown = this.handlePointerdown.bind(this);
|
82
|
+
this.handlePointermove = this.handlePointermove.bind(this);
|
83
|
+
this.handlePointerup = this.handlePointerup.bind(this);
|
84
|
+
}
|
85
|
+
init({ containerEl, slideEls, height = 300, gap = 20, borderRadius = 40, applyBorderRadiusToContainer = true, snapToSlide = false, bindToScrollPos = false, }) {
|
86
|
+
this.containerEl = containerEl;
|
87
|
+
this.slideEls = slideEls.map((slide) => {
|
88
|
+
return createElement({
|
89
|
+
classes: 'ec-slide',
|
90
|
+
contents: slide,
|
91
|
+
});
|
92
|
+
});
|
93
|
+
this._slideContentEls = slideEls;
|
94
|
+
this._containerWidth = containerEl.offsetWidth;
|
95
|
+
this._contentWidth = containerEl?.scrollWidth || 0;
|
96
|
+
this.updateSettings({
|
97
|
+
height,
|
98
|
+
gap,
|
99
|
+
borderRadius,
|
100
|
+
applyBorderRadiusToContainer,
|
101
|
+
snapToSlide,
|
102
|
+
bindToScrollPos,
|
103
|
+
});
|
104
|
+
this.addEventListeners();
|
105
|
+
this.containerEl.style.setProperty('--ec-container-width', `${this._containerWidth}px`);
|
106
|
+
this.containerEl.beholdReplaceChildren(...this.slideEls);
|
107
|
+
this.updateDimensions();
|
108
|
+
this._isInitialized = true;
|
109
|
+
this.loop();
|
110
|
+
}
|
111
|
+
updateSettings({ height = 300, gap = 20, borderRadius = 40, applyBorderRadiusToContainer = true, snapToSlide = false, bindToScrollPos = false, }) {
|
112
|
+
cancelAnimationFrame(this._raf);
|
113
|
+
this._raf = requestAnimationFrame(() => {
|
114
|
+
this.height = height;
|
115
|
+
this.gap = gap;
|
116
|
+
this.borderRadius = Math.min((borderRadius / 100) * this.height, (this._containerWidth * 0.45) / 2);
|
117
|
+
this._minSlideWidth = Math.max(this.borderRadius, 40);
|
118
|
+
this.applyBorderRadiusToContainer = applyBorderRadiusToContainer;
|
119
|
+
this.snapToSlide = snapToSlide;
|
120
|
+
this.bindToScrollPos = bindToScrollPos;
|
121
|
+
this.containerEl.style.setProperty('--ec-height', `${this.height}px`);
|
122
|
+
this.containerEl.style.setProperty('--ec-border-radius', this.applyBorderRadiusToContainer ? `${this.borderRadius}px` : '0px');
|
123
|
+
if (this.snapToSlide) {
|
124
|
+
this._targetPosition = -this.getClosestSlideToPoint(this._targetPosition).leftEdge;
|
125
|
+
}
|
126
|
+
});
|
127
|
+
}
|
128
|
+
destroy() {
|
129
|
+
this._isInitialized = false;
|
130
|
+
cancelAnimationFrame(this._raf);
|
131
|
+
this.removeEventListeners();
|
132
|
+
if (this.slideEls?.length) {
|
133
|
+
this.slideEls.forEach((slide) => {
|
134
|
+
slide.remove();
|
135
|
+
});
|
136
|
+
}
|
137
|
+
this.slideEls = null;
|
138
|
+
this.containerEl = null;
|
139
|
+
}
|
140
|
+
handleResize(entries) {
|
141
|
+
let containerWidth = null;
|
142
|
+
if (entries) {
|
143
|
+
entries.forEach((entry) => {
|
144
|
+
if (entry.target === this.containerEl) {
|
145
|
+
containerWidth = entry.borderBoxSize
|
146
|
+
? entry.borderBoxSize[0].inlineSize
|
147
|
+
: entry.contentRect.width;
|
148
|
+
}
|
149
|
+
});
|
150
|
+
}
|
151
|
+
this.updateDimensions(containerWidth);
|
152
|
+
this._targetPosition = Math.max(Math.min(0, this._targetPosition), this._containerWidth - this._contentWidth);
|
153
|
+
}
|
154
|
+
addEventListeners() {
|
155
|
+
this.removeEventListeners();
|
156
|
+
if (this.slideEls.length) {
|
157
|
+
this.slideEls.forEach((slide) => {
|
158
|
+
this.resizeObserver.observe(slide);
|
159
|
+
});
|
160
|
+
}
|
161
|
+
if (this.containerEl) {
|
162
|
+
this.resizeObserver.observe(this.containerEl);
|
163
|
+
this.containerEl.addEventListener('pointerdown', this.handlePointerdown);
|
164
|
+
document.addEventListener('pointermove', this.handlePointermove, { passive: true });
|
165
|
+
document.addEventListener('pointerup', this.handlePointerup);
|
166
|
+
document.body.addEventListener('pointerleave', this.handlePointerup);
|
167
|
+
}
|
168
|
+
}
|
169
|
+
removeEventListeners() {
|
170
|
+
this.resizeObserver.disconnect();
|
171
|
+
if (this.containerEl) {
|
172
|
+
this.containerEl.removeEventListener('pointerdown', this.handlePointerdown);
|
173
|
+
document.removeEventListener('pointermove', this.handlePointermove);
|
174
|
+
document.removeEventListener('pointerup', this.handlePointerup);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
handlePointerdown(e) {
|
178
|
+
this._isDragging = true;
|
179
|
+
this._settledPosition = this._currentPosition;
|
180
|
+
this._targetPosition = this._currentPosition;
|
181
|
+
this._points.start = e.clientX;
|
182
|
+
this._points.previous = e.clientX;
|
183
|
+
this.updateDimensions();
|
184
|
+
}
|
185
|
+
handlePointermove(e) {
|
186
|
+
if (!this._isDragging)
|
187
|
+
return;
|
188
|
+
const delta = e.clientX - this._points.start;
|
189
|
+
if (Math.abs(delta) > 10) {
|
190
|
+
this.containerEl.classList.add('ec-is-dragging');
|
191
|
+
}
|
192
|
+
this._targetPosition = this._settledPosition + delta;
|
193
|
+
pushWithLimit(this._momentumArray, e.clientX - this._points.previous, 10);
|
194
|
+
this._points.previous = e.clientX;
|
195
|
+
}
|
196
|
+
handlePointerup() {
|
197
|
+
this.containerEl.classList.remove('ec-is-dragging');
|
198
|
+
this._isDragging = false;
|
199
|
+
this._targetPosition += getArrayAverage(this._momentumArray) * 10;
|
200
|
+
// Keep in bounds
|
201
|
+
this._targetPosition = Math.max(Math.min(0, this._targetPosition), this._containerWidth - this._contentWidth);
|
202
|
+
// Snap to slide
|
203
|
+
if (this.snapToSlide) {
|
204
|
+
this._targetPosition = -this.getClosestSlideToPoint(this._targetPosition)
|
205
|
+
.leftEdge;
|
206
|
+
}
|
207
|
+
}
|
208
|
+
updateDimensions(containerWidth) {
|
209
|
+
this._containerWidth = containerWidth || this.containerEl.offsetWidth;
|
210
|
+
let contentWidth = 0;
|
211
|
+
this._slideDimensions = this.slideEls.map((slide, i) => {
|
212
|
+
let width = slide.offsetWidth;
|
213
|
+
let x = contentWidth;
|
214
|
+
contentWidth += width;
|
215
|
+
if (i < this._slideDimensions.length - 1) {
|
216
|
+
contentWidth += this.gap;
|
217
|
+
}
|
218
|
+
return { width, x };
|
219
|
+
});
|
220
|
+
this._contentWidth = contentWidth;
|
221
|
+
this.finalAnchorSlide = this.getFinalAnchorSlide();
|
222
|
+
this.containerEl.style.setProperty('--ec-container-width', `${this._containerWidth}px`);
|
223
|
+
}
|
224
|
+
getAcceleration({ displacement, _velocity, stiffness = 7, mass = 17, friction = 1.25, }) {
|
225
|
+
let val = (-stiffness * displacement) / mass - friction * _velocity;
|
226
|
+
if (Math.abs(val) < 0.001)
|
227
|
+
val = 0;
|
228
|
+
return val * this._frameRate;
|
229
|
+
}
|
230
|
+
getClosestSlideToPoint(x) {
|
231
|
+
return this._slideDimensions.reduce((acc, curr, i) => {
|
232
|
+
const distanceFromTarget = Math.abs(acc.cumulativeWidth + x);
|
233
|
+
if (distanceFromTarget < acc.distanceFromTarget) {
|
234
|
+
return {
|
235
|
+
index: i,
|
236
|
+
distanceFromTarget,
|
237
|
+
leftEdge: acc.cumulativeWidth,
|
238
|
+
cumulativeWidth: acc.cumulativeWidth + curr.width + this.gap,
|
239
|
+
};
|
240
|
+
}
|
241
|
+
return {
|
242
|
+
...acc,
|
243
|
+
cumulativeWidth: acc.cumulativeWidth + curr.width + this.gap,
|
244
|
+
};
|
245
|
+
}, {
|
246
|
+
index: 0,
|
247
|
+
distanceFromTarget: Infinity,
|
248
|
+
leftEdge: 0,
|
249
|
+
cumulativeWidth: 0,
|
250
|
+
});
|
251
|
+
}
|
252
|
+
getFinalAnchorSlide() {
|
253
|
+
const res = this.slideEls.reduceRight((acc, curr, i) => {
|
254
|
+
if (acc.cumulativeWidth < this._containerWidth) {
|
255
|
+
acc.index = i;
|
256
|
+
acc.cumulativeWidth += this._slideDimensions[i]?.width || 0;
|
257
|
+
}
|
258
|
+
return acc;
|
259
|
+
}, {
|
260
|
+
index: 0,
|
261
|
+
cumulativeWidth: 0,
|
262
|
+
});
|
263
|
+
return res.index;
|
264
|
+
}
|
265
|
+
updateScrollPos() {
|
266
|
+
this._abortScrollCheck();
|
267
|
+
this._abortScrollCheck = getAsyncRect(this.containerEl, (rect) => {
|
268
|
+
this._containerWidth = rect.width;
|
269
|
+
const newProgress = Math.max(Math.min((this._windowHeight - rect.top) /
|
270
|
+
(this._windowHeight + rect.height), 1), 0);
|
271
|
+
if (newProgress !== this._scrollProgress) {
|
272
|
+
this._isScrolling = true;
|
273
|
+
const progressDelta = this._scrollProgress - newProgress;
|
274
|
+
const pixelDelta = this._windowHeight * progressDelta * 0.5;
|
275
|
+
this._targetPosition += pixelDelta;
|
276
|
+
this._targetPosition = Math.max(Math.min(0, this._targetPosition), this._containerWidth - this._contentWidth);
|
277
|
+
this._scrollProgress = newProgress;
|
278
|
+
}
|
279
|
+
this._containerRect = rect;
|
280
|
+
});
|
281
|
+
}
|
282
|
+
updatePosition() {
|
283
|
+
const delta = this._currentPosition - this._targetPosition;
|
284
|
+
let stiffness = 200;
|
285
|
+
let friction = 23;
|
286
|
+
let mass = 80;
|
287
|
+
if (this._isScrolling) {
|
288
|
+
stiffness = 600;
|
289
|
+
friction = 40;
|
290
|
+
mass = 40;
|
291
|
+
}
|
292
|
+
if (this._targetPosition === 0 ||
|
293
|
+
this._targetPosition <= this._containerWidth - this._contentWidth) {
|
294
|
+
stiffness = 100;
|
295
|
+
friction = 16;
|
296
|
+
mass = 45;
|
297
|
+
}
|
298
|
+
const acceleration = this.getAcceleration({
|
299
|
+
displacement: delta,
|
300
|
+
_velocity: this._velocity,
|
301
|
+
stiffness,
|
302
|
+
friction,
|
303
|
+
mass,
|
304
|
+
});
|
305
|
+
this._velocity += acceleration;
|
306
|
+
this._currentPosition += roundToDigits(this._velocity);
|
307
|
+
}
|
308
|
+
getSlidePositions() {
|
309
|
+
const currentPosition = roundToDigits(this._currentPosition);
|
310
|
+
let xPos = currentPosition;
|
311
|
+
let needsAdjustment = false;
|
312
|
+
const values = this.slideEls.map((el, i) => {
|
313
|
+
const contentEl = this._slideContentEls[i];
|
314
|
+
const slideWidth = this._slideDimensions[i]?.width;
|
315
|
+
if (i > 0) {
|
316
|
+
xPos += this._slideDimensions[i - 1]?.width + this.gap;
|
317
|
+
}
|
318
|
+
let innerTranslate = 0;
|
319
|
+
let leftClipInset = 0;
|
320
|
+
let rightClipInset = 0;
|
321
|
+
let xOffset = 0;
|
322
|
+
let shrinkStartPoint = Math.min(slideWidth * this._shrinkStartMultiplier, this._containerWidth);
|
323
|
+
const dimensions = this._slideDimensions[i];
|
324
|
+
const isEndingSlide = dimensions &&
|
325
|
+
dimensions.x + dimensions.width * this._shrinkStartMultiplier >
|
326
|
+
this._contentWidth;
|
327
|
+
// Final slides that would be shrunk when at the end of scroll
|
328
|
+
if (isEndingSlide) {
|
329
|
+
shrinkStartPoint =
|
330
|
+
slideWidth * (this.slideEls.length - i) + Math.round(this._adjustment);
|
331
|
+
}
|
332
|
+
let shrinkProgress = Math.min(Math.max(shrinkStartPoint -
|
333
|
+
Math.max(this._containerWidth - xPos + Math.round(this._adjustment), 0), 0) /
|
334
|
+
(shrinkStartPoint - this._minSlideWidth), 1);
|
335
|
+
const baseOffset = (slideWidth - this._minSlideWidth) * shrinkProgress;
|
336
|
+
rightClipInset = baseOffset;
|
337
|
+
innerTranslate = -baseOffset / 2;
|
338
|
+
xOffset -= baseOffset;
|
339
|
+
const isRightmost = xPos < this._containerWidth &&
|
340
|
+
xPos + slideWidth - rightClipInset + this.gap >= this._containerWidth;
|
341
|
+
if (isRightmost && currentPosition <= 0) {
|
342
|
+
const visualWidth = slideWidth - leftClipInset - rightClipInset;
|
343
|
+
const overflow = this._minSlideWidth - (this._containerWidth - xPos);
|
344
|
+
const visible = overflow <= 0 ? visualWidth : this._containerWidth - xPos;
|
345
|
+
const visibleGap = this._containerWidth - (xPos + visible);
|
346
|
+
// There is a gap between the last slide and the container edge
|
347
|
+
if (visibleGap > 0 && !this._adjustment) {
|
348
|
+
this._adjustment = visibleGap * this._adjustmentMultiplier;
|
349
|
+
this._numInitialSlides = i + 1;
|
350
|
+
needsAdjustment = true;
|
351
|
+
}
|
352
|
+
// The last slide isn't showing enough
|
353
|
+
if (visible < this._minSlideWidth / 2 && !this._adjustment) {
|
354
|
+
this._adjustment = (visible + this.gap) * this._adjustmentMultiplier;
|
355
|
+
this._numInitialSlides = i + 1;
|
356
|
+
needsAdjustment = true;
|
357
|
+
}
|
358
|
+
}
|
359
|
+
// Leftmost visible slide
|
360
|
+
if (Math.round(xPos - this.gap) <= 0 &&
|
361
|
+
Math.round(slideWidth + xPos) > 0) {
|
362
|
+
if (i !== this.anchorSlide && this.onSlideChange) {
|
363
|
+
this.onSlideChange(i);
|
364
|
+
}
|
365
|
+
this.anchorSlide = i;
|
366
|
+
}
|
367
|
+
if (xPos <= 0) {
|
368
|
+
const baseOffset = Math.min(Math.abs(xPos), slideWidth - this._minSlideWidth);
|
369
|
+
leftClipInset = baseOffset;
|
370
|
+
innerTranslate = xPos / -2;
|
371
|
+
}
|
372
|
+
// Virtualize
|
373
|
+
if (this._virtualize) {
|
374
|
+
if (xPos > this._containerWidth + 250 || xPos + slideWidth < -250) {
|
375
|
+
contentEl.remove();
|
376
|
+
}
|
377
|
+
else if (!contentEl.isConnected) {
|
378
|
+
el.append(contentEl);
|
379
|
+
}
|
380
|
+
}
|
381
|
+
const slideValues = {
|
382
|
+
el,
|
383
|
+
contentEl,
|
384
|
+
xPos,
|
385
|
+
rightClipInset,
|
386
|
+
leftClipInset,
|
387
|
+
innerTranslate,
|
388
|
+
};
|
389
|
+
xPos += xOffset;
|
390
|
+
return slideValues;
|
391
|
+
});
|
392
|
+
if (needsAdjustment) {
|
393
|
+
return this.getSlidePositions();
|
394
|
+
}
|
395
|
+
return values;
|
396
|
+
}
|
397
|
+
positionSlides() {
|
398
|
+
let calculatedValues = this.getSlidePositions();
|
399
|
+
calculatedValues.forEach(({ el, contentEl, xPos, rightClipInset, leftClipInset, innerTranslate }, i) => {
|
400
|
+
const borderRadius = Math.min(this.borderRadius, this._slideDimensions[i]?.width / 2);
|
401
|
+
el.style.transform = `translate3d(${xPos}px, 0 ,0)`;
|
402
|
+
el.style.clipPath = `inset(0 ${rightClipInset}px 0 ${leftClipInset}px round ${borderRadius}px)`;
|
403
|
+
contentEl.style.transform = `translate3d(${innerTranslate}px, 0, 0)`;
|
404
|
+
});
|
405
|
+
}
|
406
|
+
goToSlide(slideIndex) {
|
407
|
+
this.updateDimensions();
|
408
|
+
this._targetPosition =
|
409
|
+
-this._slideDimensions[slideIndex]?.x ?? this._targetPosition;
|
410
|
+
this._targetPosition = Math.max(Math.min(0, this._targetPosition), this._containerWidth - this._contentWidth);
|
411
|
+
}
|
412
|
+
advance() {
|
413
|
+
this.goToSlide(Math.min(this.getClosestSlideToPoint(this._targetPosition).index + 1, this.slideEls.length - 1));
|
414
|
+
}
|
415
|
+
retreat() {
|
416
|
+
this.goToSlide(Math.max(this.getClosestSlideToPoint(this._targetPosition).index - 1, 0));
|
417
|
+
}
|
418
|
+
render() {
|
419
|
+
this._momentumArray.shift();
|
420
|
+
if (!this.slideEls)
|
421
|
+
return;
|
422
|
+
if (this.bindToScrollPos) {
|
423
|
+
this.updateScrollPos();
|
424
|
+
}
|
425
|
+
this.updatePosition();
|
426
|
+
this.positionSlides();
|
427
|
+
this._isScrolling = false;
|
428
|
+
}
|
429
|
+
loop() {
|
430
|
+
if (!this._isInitialized)
|
431
|
+
return;
|
432
|
+
const now = performance.now();
|
433
|
+
if (now - this._lastLoopRun > 16) {
|
434
|
+
this.render();
|
435
|
+
this._lastLoopRun = now;
|
436
|
+
}
|
437
|
+
requestAnimationFrame(() => this.loop());
|
438
|
+
}
|
439
|
+
}
|
440
|
+
function getArrayAverage(arr) {
|
441
|
+
if (!arr.length)
|
442
|
+
return 0;
|
443
|
+
return arr.reduce((acc, curr) => acc + curr, 0) / arr.length;
|
444
|
+
}
|
445
|
+
function roundToDigits(num, digits = 3) {
|
446
|
+
return parseFloat(num.toFixed(digits));
|
447
|
+
}
|
448
|
+
|
449
|
+
var instagramIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1000 1000\"><path d=\"M295.42,6c-53.2,2.51-89.53,11-121.29,23.48-32.87,12.81-60.73,30-88.45,57.82S40.89,143,28.17,175.92c-12.31,31.83-20.65,68.19-23,121.42S2.3,367.68,2.56,503.46,3.42,656.26,6,709.6c2.54,53.19,11,89.51,23.48,121.28,12.83,32.87,30,60.72,57.83,88.45S143,964.09,176,976.83c31.8,12.29,68.17,20.67,121.39,23s70.35,2.87,206.09,2.61,152.83-.86,206.16-3.39S799.1,988,830.88,975.58c32.87-12.86,60.74-30,88.45-57.84S964.1,862,976.81,829.06c12.32-31.8,20.69-68.17,23-121.35,2.33-53.37,2.88-70.41,2.62-206.17s-.87-152.78-3.4-206.1-11-89.53-23.47-121.32c-12.85-32.87-30-60.7-57.82-88.45S862,40.87,829.07,28.19c-31.82-12.31-68.17-20.7-121.39-23S637.33,2.3,501.54,2.56,348.75,3.4,295.42,6m5.84,903.88c-48.75-2.12-75.22-10.22-92.86-17-23.36-9-40-19.88-57.58-37.29s-28.38-34.11-37.5-57.42c-6.85-17.64-15.1-44.08-17.38-92.83-2.48-52.69-3-68.51-3.29-202s.22-149.29,2.53-202c2.08-48.71,10.23-75.21,17-92.84,9-23.39,19.84-40,37.29-57.57s34.1-28.39,57.43-37.51c17.62-6.88,44.06-15.06,92.79-17.38,52.73-2.5,68.53-3,202-3.29s149.31.21,202.06,2.53c48.71,2.12,75.22,10.19,92.83,17,23.37,9,40,19.81,57.57,37.29s28.4,34.07,37.52,57.45c6.89,17.57,15.07,44,17.37,92.76,2.51,52.73,3.08,68.54,3.32,202s-.23,149.31-2.54,202c-2.13,48.75-10.21,75.23-17,92.89-9,23.35-19.85,40-37.31,57.56s-34.09,28.38-57.43,37.5c-17.6,6.87-44.07,15.07-92.76,17.39-52.73,2.48-68.53,3-202.05,3.29s-149.27-.25-202-2.53m407.6-674.61a60,60,0,1,0,59.88-60.1,60,60,0,0,0-59.88,60.1M245.77,503c.28,141.8,115.44,256.49,257.21,256.22S759.52,643.8,759.25,502,643.79,245.48,502,245.76,245.5,361.22,245.77,503m90.06-.18a166.67,166.67,0,1,1,167,166.34,166.65,166.65,0,0,1-167-166.34\" transform=\"translate(-2.5 -2.5)\"/><title>Instagram</title></svg>";
|
450
|
+
|
451
|
+
var css_248z = ".ec-carousel{--ec-controls-margin:20px;--ec-button-border-radius:40%;--ec-icon-color:#4a4a4a;--ec-text-color:#4a4a4a;--ec-button-color:#f4f4f4;--ec-button-hover-color:#ececec;--ec-button-icon-color:#4a4a4a;--ec-button-icon-hover-color:#2e2e2e;--ec-button-gap:min(calc(var(--ec-controls-margin)/2),5px);flex-direction:column;height:var(--ec-height);margin:0;overflow:hidden;width:100%}.ec-carousel,.ec-inner{display:flex;justify-content:center}.ec-inner{align-items:center}.ec-slides{border-radius:var(--ec-border-radius);contain:style paint;cursor:grab;height:var(--ec-height);justify-content:flex-start;overflow:hidden;position:relative;touch-action:none;width:100%}.ec-slide,.ec-slides{display:flex;isolation:isolate}.ec-slide{contain:content;flex-shrink:0;height:100%;left:0;max-width:calc(var(--ec-container-width)*.45);position:absolute;top:0;-moz-user-select:none;user-select:none;-webkit-user-select:none;will-change:clip-path,transform}.ec-is-dragging{cursor:grabbing}.ec-is-dragging .ec-slide{pointer-events:none}.ec-footer,.ec-header{display:flex;justify-content:center}.ec-carousel--controls-sides .ec-footer,.ec-carousel--controls-sides .ec-header{padding:0 64px}.ec-header{margin-bottom:var(--ec-controls-margin)}.ec-footer{margin-top:var(--ec-controls-margin)}.ec-label{align-items:center;display:flex;flex-shrink:1;gap:7px 5px;line-height:1.2;min-width:0;overflow:hidden;text-overflow:ellipsis;-moz-user-select:none;user-select:none;-webkit-user-select:none}.ec-carousel--controls-center .ec-button+.ec-label,.ec-carousel--controls-split .ec-button+.ec-label{margin:0 max(calc(var(--ec-controls-margin)*1.5),20px)}.ec-carousel--label-right .ec-label{margin-left:auto}.ec-carousel--label-left .ec-label{margin-right:auto}.ec-label svg{flex-shrink:0;height:24px;margin-right:5px;width:24px}.ec-label svg path{fill:var(--ec-icon-color)}.ec-label a,.ec-label div{color:var(--ec-text-color);display:inline-block;font-family:inherit;font-size:19px;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.ec-button,.ec-carousel--controls-center .ec-footer,.ec-carousel--controls-center .ec-header{justify-content:center}.ec-button{align-items:center;background-color:transparent;border:none;cursor:pointer;display:flex;flex-shrink:0;height:44px;padding:0;transition:all .3s ease;width:44px}.ec-carousel--controls-sides .ec-button{height:var(--ec-height);width:64px}.ec-button:disabled{cursor:default;opacity:.5}.ec-button:before{background-color:var(--ec-button-color);border-radius:var(--ec-button-border-radius);content:\"\";height:44px;position:absolute;transition:all .3s ease;width:44px}.ec-button:not(:disabled):hover:before{background-color:var(--ec-button-hover-color)}.ec-button svg{height:14px;position:relative;width:auto;z-index:1}.ec-button svg path{fill:var(--ec-button-icon-color);transition:all .3s ease}.ec-button:not(:disabled):hover svg path{fill:var(--ec-button-icon-hover-color)}.ec-retreat{padding-right:var(--ec-button-gap);width:calc(44px + var(--ec-button-gap))}.ec-carousel--controls-split .ec-retreat{margin-right:auto}.ec-carousel--controls-right .ec-retreat{margin-left:auto}.ec-advance{padding-left:var(--ec-button-gap);width:calc(44px + var(--ec-button-gap))}.ec-carousel--controls-split .ec-advance{margin-left:auto}.ec-carousel--controls-left .ec-advance{margin-right:auto}.ec-carousel--controls-sides .ec-retreat{padding-right:20px}.ec-carousel--controls-sides .ec-advance{padding-left:20px}.post{height:100%;max-width:calc(var(--ec-container-width)*.45)}.post a{-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.post--placeholder{background-color:#dedede;height:0;padding-bottom:calc(100%/var(--post-aspect-ratio))}";
|
452
|
+
var styles = css_248z;
|
453
|
+
|
454
|
+
/*
|
455
|
+
* Grid
|
456
|
+
*/
|
457
|
+
class ElasticCarousel extends BaseWidget {
|
458
|
+
label = 'ElasticCarousel';
|
459
|
+
// Provided
|
460
|
+
widgetSettings;
|
461
|
+
feedMetadata;
|
462
|
+
posts;
|
463
|
+
previewLoadingColors = null;
|
464
|
+
// Internal
|
465
|
+
containerEl;
|
466
|
+
headerEl;
|
467
|
+
innerEl;
|
468
|
+
footerEl;
|
469
|
+
instagramLogoEl;
|
470
|
+
labelEl;
|
471
|
+
leftArrowEl;
|
472
|
+
rightArrowEl;
|
473
|
+
slidesContainerEl;
|
474
|
+
postEls;
|
475
|
+
popoverGalleryEl;
|
476
|
+
appliedBreakpoint;
|
477
|
+
medianPaletteHSL;
|
478
|
+
gyre;
|
479
|
+
constructor() {
|
480
|
+
super();
|
481
|
+
// Define which props will trigger _handlePropChange
|
482
|
+
this.onPropChange(this._handlePropChange, ['widgetSettings', 'feedMetadata', 'posts', 'previewLoadingColors'], ['widgetSettings', 'feedMetadata', 'posts'], this.setup);
|
483
|
+
// Register child components
|
484
|
+
ImagePost.register();
|
485
|
+
VideoPost.register();
|
486
|
+
AlbumPost.register();
|
487
|
+
// Create Gyre carousel
|
488
|
+
this.gyre = new Gyre({ onSlideChange: (i) => this._handleSlideChange(i) });
|
489
|
+
// Bind event handlers
|
490
|
+
this._handlePostClick = this._handlePostClick.bind(this);
|
491
|
+
// Listen to focus
|
492
|
+
this.addEventListener('post-focus-next', this._handleFocusNextPost);
|
493
|
+
this.addEventListener('post-focus-previous', this._handleFocusPreviousPost);
|
494
|
+
// Connect
|
495
|
+
this.onConnect(() => {
|
496
|
+
this.slidesContainerEl = createElement({
|
497
|
+
classes: 'ec-slides',
|
498
|
+
});
|
499
|
+
this.headerEl = createElement({
|
500
|
+
type: 'header',
|
501
|
+
classes: 'ec-header',
|
502
|
+
});
|
503
|
+
this.innerEl = createElement({
|
504
|
+
classes: 'ec-inner',
|
505
|
+
contents: [this.slidesContainerEl],
|
506
|
+
});
|
507
|
+
this.footerEl = createElement({
|
508
|
+
type: 'footer',
|
509
|
+
classes: 'ec-footer',
|
510
|
+
});
|
511
|
+
this.labelEl = createElement({
|
512
|
+
classes: 'ec-label',
|
513
|
+
});
|
514
|
+
this.leftArrowEl = createElement({
|
515
|
+
type: 'button',
|
516
|
+
classes: 'ec-button ec-retreat',
|
517
|
+
contents: [caretLeftIcon],
|
518
|
+
listeners: {
|
519
|
+
click: () => this._retreat(),
|
520
|
+
},
|
521
|
+
attributes: {
|
522
|
+
tabindex: -1,
|
523
|
+
disabled: true,
|
524
|
+
},
|
525
|
+
});
|
526
|
+
this.rightArrowEl = createElement({
|
527
|
+
type: 'button',
|
528
|
+
classes: 'ec-button ec-advance',
|
529
|
+
contents: [caretRightIcon],
|
530
|
+
listeners: {
|
531
|
+
click: () => this._advance(),
|
532
|
+
},
|
533
|
+
attributes: {
|
534
|
+
tabindex: -1,
|
535
|
+
},
|
536
|
+
});
|
537
|
+
this.containerEl = createElement({
|
538
|
+
type: 'figure',
|
539
|
+
classes: 'ec-carousel',
|
540
|
+
contents: [this.innerEl],
|
541
|
+
});
|
542
|
+
this.renderWidget(this.containerEl, [baseGridStyles, styles]);
|
543
|
+
// A11y stuff
|
544
|
+
this.setAttribute('tabindex', '0');
|
545
|
+
this.setAttribute('aria-label', 'Gallery of Instagram posts. Shift + arrow keys to navigate');
|
546
|
+
// listen to resize
|
547
|
+
this.onResize(this, this, this._handleResize);
|
548
|
+
});
|
549
|
+
}
|
550
|
+
/*
|
551
|
+
* Run initial setup that requires data to be fully loaded
|
552
|
+
*/
|
553
|
+
setup() {
|
554
|
+
// Calculate medianPaletteHSL
|
555
|
+
if (this.widgetSettings.loadingColor !== 'transparent') {
|
556
|
+
const palettes = this.posts
|
557
|
+
.filter((post) => post.colorPalette)
|
558
|
+
.map((post) => post.colorPalette);
|
559
|
+
this.medianPaletteHSL = getMedianHSL(palettes, this.widgetSettings.loadingColor);
|
560
|
+
}
|
561
|
+
// Find initial breakpoint
|
562
|
+
const matchingBreakpoint = this.getMatchingBreakpoint(this.offsetWidth, this.widgetSettings.breakpoints);
|
563
|
+
// Render posts with current breakpoint styles
|
564
|
+
this.renderBreakpoint(matchingBreakpoint);
|
565
|
+
// Add non-breakpoint CSS vars
|
566
|
+
setCssVars(this.containerEl, {
|
567
|
+
'--ec-icon-color': this.widgetSettings.iconColor,
|
568
|
+
'--ec-text-color': this.widgetSettings.textColor,
|
569
|
+
'--ec-button-color': this.widgetSettings.buttonColor,
|
570
|
+
'--ec-button-hover-color': this.widgetSettings.buttonHoverColor,
|
571
|
+
'--ec-button-icon-color': this.widgetSettings.buttonIconColor,
|
572
|
+
'--ec-button-icon-hover-color': this.widgetSettings.buttonIconHoverColor,
|
573
|
+
});
|
574
|
+
// Pass settings and metadata to posts
|
575
|
+
if (this.postEls) {
|
576
|
+
this.postEls.forEach((postEl) => {
|
577
|
+
postEl.widgetSettings = this.widgetSettings;
|
578
|
+
postEl.feedMetadata = this.feedMetadata;
|
579
|
+
});
|
580
|
+
}
|
581
|
+
// Build label
|
582
|
+
this._buildLabel();
|
583
|
+
// Enable popoverGallery if necessary
|
584
|
+
if (this.widgetSettings.onPostClick === 'openPopupGallery') {
|
585
|
+
this.enablePopoverGallery();
|
586
|
+
}
|
587
|
+
}
|
588
|
+
_buildLabel() {
|
589
|
+
const iconTemplate = document.createElement('template');
|
590
|
+
iconTemplate.innerHTML = instagramIcon;
|
591
|
+
if (this.widgetSettings.label) {
|
592
|
+
const customLabelEl = createElement({
|
593
|
+
type: this.widgetSettings.labelLink ? 'a' : 'div',
|
594
|
+
contents: this.widgetSettings.label,
|
595
|
+
attributes: {
|
596
|
+
href: this.widgetSettings.labelLink || null,
|
597
|
+
target: this.widgetSettings.labelLink ? '_blank' : null,
|
598
|
+
},
|
599
|
+
});
|
600
|
+
this.labelEl.beholdReplaceChildren(iconTemplate.content, customLabelEl);
|
601
|
+
}
|
602
|
+
else if (this.feedMetadata.hashtags?.length) {
|
603
|
+
const hashtags = this.feedMetadata.hashtags;
|
604
|
+
const linkEls = hashtags.map((hashtag, i, arr) => {
|
605
|
+
return createElement({
|
606
|
+
type: 'a',
|
607
|
+
contents: ['#', hashtag, i < arr.length - 1 ? ', ' : ''],
|
608
|
+
attributes: {
|
609
|
+
target: '_blank',
|
610
|
+
href: `https://instagram.com/explore/tags/${hashtag}`,
|
611
|
+
},
|
612
|
+
});
|
613
|
+
});
|
614
|
+
this.labelEl.beholdReplaceChildren(iconTemplate.content, ...linkEls);
|
615
|
+
}
|
616
|
+
else {
|
617
|
+
const profileLinkEl = createElement({
|
618
|
+
type: 'a',
|
619
|
+
contents: [this.feedMetadata.username],
|
620
|
+
attributes: {
|
621
|
+
target: '_blank',
|
622
|
+
href: `https://instagram.com/${this.feedMetadata.username}`,
|
623
|
+
},
|
624
|
+
});
|
625
|
+
this.labelEl.beholdReplaceChildren(iconTemplate.content, profileLinkEl);
|
626
|
+
}
|
627
|
+
}
|
628
|
+
/**
|
629
|
+
* Handle a slide change
|
630
|
+
*/
|
631
|
+
_handleSlideChange(index) {
|
632
|
+
if (index === 0 && this.leftArrowEl) {
|
633
|
+
this.leftArrowEl.disabled = true;
|
634
|
+
}
|
635
|
+
else {
|
636
|
+
this.leftArrowEl.disabled = false;
|
637
|
+
}
|
638
|
+
if (index >= this.gyre.finalAnchorSlide) {
|
639
|
+
this.rightArrowEl.disabled = true;
|
640
|
+
}
|
641
|
+
else {
|
642
|
+
this.rightArrowEl.disabled = false;
|
643
|
+
}
|
644
|
+
}
|
645
|
+
/*
|
646
|
+
* Handle prop change
|
647
|
+
*/
|
648
|
+
_handlePropChange({ changedProp, oldValue, newValue }) {
|
649
|
+
switch (changedProp) {
|
650
|
+
case 'posts':
|
651
|
+
this._handlePostsChange({ oldValue, newValue });
|
652
|
+
break;
|
653
|
+
case 'widgetSettings':
|
654
|
+
this._handleSettingsChange(oldValue, newValue);
|
655
|
+
break;
|
656
|
+
case 'feedMetadata':
|
657
|
+
this._handleMetadataChange();
|
658
|
+
break;
|
659
|
+
case 'previewLoadingColors':
|
660
|
+
this.postEls.forEach((postEl) => {
|
661
|
+
postEl.previewLoadingColors = this.previewLoadingColors;
|
662
|
+
});
|
663
|
+
setClasses(this, {
|
664
|
+
'is-previewing-loading-colors': !!this.previewLoadingColors,
|
665
|
+
});
|
666
|
+
break;
|
667
|
+
}
|
668
|
+
}
|
669
|
+
/*
|
670
|
+
* Handle posts change
|
671
|
+
*/
|
672
|
+
_handlePostsChange({ oldValue, newValue }) {
|
673
|
+
this.postEls = [];
|
674
|
+
this.renderBreakpoint(this.getMatchingBreakpoint(this.offsetWidth, this.widgetSettings.breakpoints), true);
|
675
|
+
// Pass settings and metadata to posts
|
676
|
+
this.postEls.forEach((postEl) => {
|
677
|
+
postEl.widgetSettings = this.widgetSettings;
|
678
|
+
postEl.feedMetadata = this.feedMetadata;
|
679
|
+
});
|
680
|
+
if (this.popoverGalleryEl) {
|
681
|
+
this.popoverGalleryEl.posts = this.posts;
|
682
|
+
}
|
683
|
+
}
|
684
|
+
/*
|
685
|
+
* Handle settings change
|
686
|
+
*/
|
687
|
+
_handleSettingsChange(oldValue, newValue) {
|
688
|
+
let forceRender = false;
|
689
|
+
if (!this.postEls?.length ||
|
690
|
+
hasChanges(oldValue.breakpoints, newValue.breakpoints, ['postHeight'], 1)) {
|
691
|
+
forceRender = true;
|
692
|
+
}
|
693
|
+
setCssVars(this.containerEl, {
|
694
|
+
'--ec-icon-color': this.widgetSettings.iconColor,
|
695
|
+
'--ec-text-color': this.widgetSettings.textColor,
|
696
|
+
'--ec-button-color': this.widgetSettings.buttonColor,
|
697
|
+
'--ec-button-hover-color': this.widgetSettings.buttonHoverColor,
|
698
|
+
'--ec-button-icon-color': this.widgetSettings.buttonIconColor,
|
699
|
+
'--ec-button-icon-hover-color': this.widgetSettings.buttonIconHoverColor,
|
700
|
+
});
|
701
|
+
if (hasChanges(oldValue, newValue, ['label', 'labelLink'])) {
|
702
|
+
this._buildLabel();
|
703
|
+
}
|
704
|
+
if (oldValue?.onPostClick !== 'openPopupGallery' &&
|
705
|
+
newValue?.onPostClick === 'openPopupGallery') {
|
706
|
+
this.enablePopoverGallery();
|
707
|
+
}
|
708
|
+
const matchingBreakpoint = this.getMatchingBreakpoint(this.offsetWidth, this.widgetSettings.breakpoints);
|
709
|
+
this.renderBreakpoint(matchingBreakpoint, forceRender);
|
710
|
+
this.postEls.forEach((postEl) => {
|
711
|
+
postEl.widgetSettings = this.widgetSettings;
|
712
|
+
});
|
713
|
+
if (this.popoverGalleryEl) {
|
714
|
+
this.popoverGalleryEl.widgetSettings = this.widgetSettings;
|
715
|
+
}
|
716
|
+
}
|
717
|
+
/*
|
718
|
+
* Handle settings change
|
719
|
+
*/
|
720
|
+
_handleMetadataChange() {
|
721
|
+
let description = `from @${this.feedMetadata.username}`;
|
722
|
+
if (this.feedMetadata.hashtags?.length) {
|
723
|
+
description = `from hashtag${this.feedMetadata.hashtags.length > 1 ? 's' : ''} ${this.feedMetadata.hashtags.join(', ')}`;
|
724
|
+
}
|
725
|
+
this.setAttribute('aria-label', `Gallery of Instagram posts ${description}. Shift + arrow keys to navigate`);
|
726
|
+
this.postEls.forEach((postEl) => {
|
727
|
+
postEl.feedMetadata = this.feedMetadata;
|
728
|
+
});
|
729
|
+
if (this.popoverGalleryEl) {
|
730
|
+
this.popoverGalleryEl.feedMetadata = this.feedMetadata;
|
731
|
+
}
|
732
|
+
}
|
733
|
+
/**
|
734
|
+
* Enable popup carousel
|
735
|
+
*/
|
736
|
+
async enablePopoverGallery() {
|
737
|
+
if (this.popoverGalleryEl)
|
738
|
+
return;
|
739
|
+
const { default: PopoverGallery } = await import('./PopoverGallery-pssVDO_O.js');
|
740
|
+
PopoverGallery.register();
|
741
|
+
this.popoverGalleryEl = document.createElement('behold-popover-gallery');
|
742
|
+
Object.assign(this.popoverGalleryEl, {
|
743
|
+
widgetSettings: this.widgetSettings,
|
744
|
+
feedMetadata: this.feedMetadata,
|
745
|
+
posts: this.posts,
|
746
|
+
closeFocusEl: this,
|
747
|
+
onSlideChange: (slideIndex) => {
|
748
|
+
this.gyre.goToSlide(slideIndex);
|
749
|
+
this.popoverGalleryEl.closeFocusEl = this.postEls[slideIndex];
|
750
|
+
},
|
751
|
+
});
|
752
|
+
}
|
753
|
+
/*
|
754
|
+
* Handle resize
|
755
|
+
*/
|
756
|
+
_handleResize(entry) {
|
757
|
+
const widgetWidth = entry.contentBoxSize?.[0]?.inlineSize;
|
758
|
+
const breakpoints = this.widgetSettings.breakpoints;
|
759
|
+
const matchingBreakpoint = this.getMatchingBreakpoint(widgetWidth, breakpoints);
|
760
|
+
this.renderBreakpoint(matchingBreakpoint);
|
761
|
+
}
|
762
|
+
/**
|
763
|
+
* Handle prev post focus
|
764
|
+
*/
|
765
|
+
_handleFocusPreviousPost() {
|
766
|
+
if (!this.postEls.length)
|
767
|
+
return;
|
768
|
+
let currentFocusIndex = [...this.postEls].indexOf(this.shadow.activeElement?.parentElement);
|
769
|
+
if (currentFocusIndex > 0) {
|
770
|
+
currentFocusIndex = currentFocusIndex - 1;
|
771
|
+
}
|
772
|
+
this.postEls[currentFocusIndex].focus();
|
773
|
+
this.gyre.goToSlide(currentFocusIndex);
|
774
|
+
}
|
775
|
+
/**
|
776
|
+
* Handle next post focus
|
777
|
+
*/
|
778
|
+
_handleFocusNextPost() {
|
779
|
+
if (!this.postEls.length)
|
780
|
+
return;
|
781
|
+
let currentFocusIndex = [...this.postEls].indexOf(this.shadow.activeElement?.parentElement);
|
782
|
+
if (this.gyre.anchorSlide > 0 &&
|
783
|
+
currentFocusIndex < this.gyre.anchorSlide) {
|
784
|
+
currentFocusIndex = this.gyre.anchorSlide;
|
785
|
+
}
|
786
|
+
if (currentFocusIndex > -1 && currentFocusIndex < this.postEls.length - 1) {
|
787
|
+
currentFocusIndex = currentFocusIndex + 1;
|
788
|
+
}
|
789
|
+
if (currentFocusIndex < 0) {
|
790
|
+
currentFocusIndex = 0;
|
791
|
+
}
|
792
|
+
this.postEls[currentFocusIndex].focus();
|
793
|
+
this.gyre.goToSlide(currentFocusIndex);
|
794
|
+
}
|
795
|
+
/**
|
796
|
+
* Handle post click
|
797
|
+
*/
|
798
|
+
_handlePostClick(post) {
|
799
|
+
this.popoverGalleryEl.open(this.postEls.indexOf(post), post);
|
800
|
+
}
|
801
|
+
/**
|
802
|
+
* Advance to next slide
|
803
|
+
*/
|
804
|
+
_advance() {
|
805
|
+
if (this.gyre) {
|
806
|
+
this.gyre.advance();
|
807
|
+
}
|
808
|
+
}
|
809
|
+
/**
|
810
|
+
* Retreat to previous slide
|
811
|
+
*/
|
812
|
+
_retreat() {
|
813
|
+
if (this.gyre) {
|
814
|
+
this.gyre.retreat();
|
815
|
+
}
|
816
|
+
}
|
817
|
+
/*
|
818
|
+
* Update postEls
|
819
|
+
*/
|
820
|
+
renderPosts(breakpoint) {
|
821
|
+
this.postEls = this.createPostEls(breakpoint);
|
822
|
+
this.raf(() => {
|
823
|
+
this.gyre.destroy();
|
824
|
+
this.gyre.init({
|
825
|
+
containerEl: this.slidesContainerEl,
|
826
|
+
slideEls: this.postEls,
|
827
|
+
height: breakpoint.postHeight,
|
828
|
+
gap: breakpoint.gap.x,
|
829
|
+
borderRadius: parseInt(breakpoint.borderRadius),
|
830
|
+
applyBorderRadiusToContainer: breakpoint.applyBorderRadiusToContainer || true,
|
831
|
+
snapToSlide: breakpoint.snapToSlide || false,
|
832
|
+
bindToScrollPos: breakpoint.bindToScrollPos || false,
|
833
|
+
});
|
834
|
+
}, 'renderPosts');
|
835
|
+
}
|
836
|
+
/*
|
837
|
+
* Get breakpoint that matches a width
|
838
|
+
*/
|
839
|
+
getMatchingBreakpoint(width, breakpoints) {
|
840
|
+
return Object.entries(breakpoints)
|
841
|
+
.map(([key, value]) => ({ width: key, ...value }))
|
842
|
+
.filter((bp) => bp.width !== 'default')
|
843
|
+
.sort((a, b) => parseInt(b.width) - parseInt(a.width))
|
844
|
+
.reduce((acc, curr) => {
|
845
|
+
return width <= parseInt(curr.width) ? curr : acc;
|
846
|
+
}, breakpoints.default);
|
847
|
+
}
|
848
|
+
/*
|
849
|
+
* Render posts with breakpoint
|
850
|
+
*/
|
851
|
+
renderBreakpoint(breakpoint, forceRender = false) {
|
852
|
+
if (!this.posts || !this.containerEl)
|
853
|
+
return;
|
854
|
+
const applied = this.appliedBreakpoint;
|
855
|
+
const { showControls, controlsPosition, controlsJustification, showLabel, labelPosition, labelJustification, buttonBorderRadius, controlsMargin, } = breakpoint;
|
856
|
+
if (!this.postEls?.length ||
|
857
|
+
hasChanges(applied, breakpoint, [
|
858
|
+
'numPosts',
|
859
|
+
'forcePostAspectRatio',
|
860
|
+
'postAspectRatio',
|
861
|
+
'postHeight',
|
862
|
+
])) {
|
863
|
+
forceRender = true;
|
864
|
+
}
|
865
|
+
if (this.widgetSettings.maxWidth &&
|
866
|
+
this.widgetSettings.constrainWidth &&
|
867
|
+
this.containerEl.style.maxWidth !== `${this.widgetSettings.maxWidth}px`) {
|
868
|
+
this.containerEl.style.maxWidth = `${this.widgetSettings.maxWidth}px`;
|
869
|
+
}
|
870
|
+
if (!this.widgetSettings.constrainWidth) {
|
871
|
+
this.containerEl.style.maxWidth = '';
|
872
|
+
}
|
873
|
+
this.leftArrowEl.remove();
|
874
|
+
this.rightArrowEl.remove();
|
875
|
+
if (hasChanges(applied, breakpoint, [
|
876
|
+
'showControls',
|
877
|
+
'controlsPosition',
|
878
|
+
'controlsJustification',
|
879
|
+
'showLabel',
|
880
|
+
'labelPosition',
|
881
|
+
'labelJustification',
|
882
|
+
]) ||
|
883
|
+
(showControls && !this.leftArrowEl.isConnected) ||
|
884
|
+
(showLabel && !this.labelEl.isConnected)) {
|
885
|
+
this.leftArrowEl.remove();
|
886
|
+
this.rightArrowEl.remove();
|
887
|
+
this.labelEl.remove();
|
888
|
+
this.headerEl.remove();
|
889
|
+
this.footerEl.remove();
|
890
|
+
setCssVars(this.containerEl, {
|
891
|
+
'--ec-button-border-radius': `${buttonBorderRadius}%`,
|
892
|
+
'--ec-controls-margin': `${controlsMargin}px`,
|
893
|
+
});
|
894
|
+
setClasses(this.containerEl, {
|
895
|
+
'ec-carousel--controls-sides': controlsPosition === 'sides',
|
896
|
+
'ec-carousel--controls-left': controlsJustification === 'left',
|
897
|
+
'ec-carousel--controls-right': controlsJustification === 'right',
|
898
|
+
'ec-carousel--controls-center': controlsJustification === 'center',
|
899
|
+
'ec-carousel--controls-split': controlsJustification === 'split',
|
900
|
+
'ec-carousel--label-left': labelJustification === 'left',
|
901
|
+
'ec-carousel--label-right': labelJustification === 'right',
|
902
|
+
'ec-carousel--label-center': labelJustification === 'left',
|
903
|
+
});
|
904
|
+
// Show label?
|
905
|
+
if (showLabel) {
|
906
|
+
if (labelPosition === 'top') {
|
907
|
+
this.headerEl.append(this.labelEl);
|
908
|
+
}
|
909
|
+
if (labelPosition === 'bottom') {
|
910
|
+
this.footerEl.append(this.labelEl);
|
911
|
+
}
|
912
|
+
}
|
913
|
+
// Set controls container
|
914
|
+
let controlsContainerEl = this.innerEl;
|
915
|
+
if (controlsPosition === 'top') {
|
916
|
+
controlsContainerEl = this.headerEl;
|
917
|
+
}
|
918
|
+
if (controlsPosition === 'bottom') {
|
919
|
+
controlsContainerEl = this.footerEl;
|
920
|
+
}
|
921
|
+
// Add controls
|
922
|
+
if (showControls && controlsPosition === 'sides') {
|
923
|
+
controlsContainerEl.prepend(this.leftArrowEl);
|
924
|
+
controlsContainerEl.append(this.rightArrowEl);
|
925
|
+
}
|
926
|
+
else if (showControls) {
|
927
|
+
switch (controlsJustification) {
|
928
|
+
case 'left':
|
929
|
+
controlsContainerEl.prepend(this.rightArrowEl);
|
930
|
+
controlsContainerEl.prepend(this.leftArrowEl);
|
931
|
+
break;
|
932
|
+
case 'right':
|
933
|
+
controlsContainerEl.append(this.leftArrowEl);
|
934
|
+
controlsContainerEl.append(this.rightArrowEl);
|
935
|
+
break;
|
936
|
+
default:
|
937
|
+
controlsContainerEl.prepend(this.leftArrowEl);
|
938
|
+
controlsContainerEl.append(this.rightArrowEl);
|
939
|
+
break;
|
940
|
+
}
|
941
|
+
}
|
942
|
+
if (this.headerEl.childElementCount) {
|
943
|
+
this.containerEl.prepend(this.headerEl);
|
944
|
+
}
|
945
|
+
if (this.footerEl.childElementCount) {
|
946
|
+
this.containerEl.append(this.footerEl);
|
947
|
+
}
|
948
|
+
}
|
949
|
+
this.containerEl.setAttribute('data-hover-effect', this.widgetSettings.hoverEffect);
|
950
|
+
// Then render posts into updated container
|
951
|
+
if (forceRender) {
|
952
|
+
this.renderPosts(breakpoint);
|
953
|
+
}
|
954
|
+
else {
|
955
|
+
this.gyre.updateSettings({
|
956
|
+
height: breakpoint.postHeight,
|
957
|
+
gap: breakpoint.gap.x,
|
958
|
+
borderRadius: parseInt(breakpoint.borderRadius),
|
959
|
+
applyBorderRadiusToContainer: breakpoint.applyBorderRadiusToContainer,
|
960
|
+
snapToSlide: breakpoint.snapToSlide,
|
961
|
+
bindToScrollPos: breakpoint.bindToScrollPos,
|
962
|
+
});
|
963
|
+
}
|
964
|
+
this.appliedBreakpoint = breakpoint;
|
965
|
+
}
|
966
|
+
/*
|
967
|
+
* Create post Els based on a breakpoint
|
968
|
+
*/
|
969
|
+
createPostEls(breakpoint) {
|
970
|
+
const { numPosts, postHeight, postAspectRatio, forcePostAspectRatio } = breakpoint;
|
971
|
+
const filteredArray = this.posts.filter((p, i) => {
|
972
|
+
const numPosts = breakpoint?.numPosts || this.posts?.length || 12;
|
973
|
+
return i < numPosts;
|
974
|
+
});
|
975
|
+
const postEls = filteredArray.map((post, i) => {
|
976
|
+
const naturalAspectRatio = [post.sizes.full.width, post.sizes.full.height];
|
977
|
+
const aspectRatioArray = forcePostAspectRatio
|
978
|
+
? postAspectRatio || [1, 1]
|
979
|
+
: naturalAspectRatio;
|
980
|
+
const aspectRatio = aspectRatioArray.reduce((w, h) => w / h);
|
981
|
+
let postType = 'behold-image-post';
|
982
|
+
if (post.mediaType === 'VIDEO') {
|
983
|
+
postType = 'behold-video-post';
|
984
|
+
}
|
985
|
+
if (post.mediaType === 'CAROUSEL_ALBUM') {
|
986
|
+
postType = 'behold-album-post';
|
987
|
+
}
|
988
|
+
const postEl = createElement({
|
989
|
+
type: postType,
|
990
|
+
props: {
|
991
|
+
post,
|
992
|
+
widgetSettings: this.widgetSettings,
|
993
|
+
feedMetadata: this.feedMetadata,
|
994
|
+
medianPaletteHSL: this.medianPaletteHSL,
|
995
|
+
onClick: this._handlePostClick,
|
996
|
+
hasRowGap: true,
|
997
|
+
isLastRow: false,
|
998
|
+
index: i,
|
999
|
+
aspectRatio: aspectRatioArray,
|
1000
|
+
totalPosts: filteredArray?.length || numPosts || this.posts?.length || 0,
|
1001
|
+
},
|
1002
|
+
style: {
|
1003
|
+
width: `${postHeight * aspectRatio}px`,
|
1004
|
+
'--post-aspect-ratio': aspectRatio,
|
1005
|
+
},
|
1006
|
+
});
|
1007
|
+
return postEl;
|
1008
|
+
});
|
1009
|
+
while (postEls.length < breakpoint.numPosts) {
|
1010
|
+
const placeholderEl = createElement({
|
1011
|
+
classes: 'post post--placeholder',
|
1012
|
+
style: {
|
1013
|
+
width: `${postHeight}px`,
|
1014
|
+
'--post-aspect-ratio': 1,
|
1015
|
+
},
|
1016
|
+
});
|
1017
|
+
postEls.push(placeholderEl);
|
1018
|
+
}
|
1019
|
+
return postEls;
|
1020
|
+
}
|
1021
|
+
/*
|
1022
|
+
* Register
|
1023
|
+
*/
|
1024
|
+
static register(name = 'behold-elastic-carousel') {
|
1025
|
+
if (!customElements.get(name)) {
|
1026
|
+
customElements.define(name, ElasticCarousel);
|
1027
|
+
}
|
1028
|
+
return name;
|
1029
|
+
}
|
1030
|
+
}
|
1031
|
+
|
1032
|
+
export { ElasticCarousel as default };
|