@behold/widget 0.5.57 → 0.5.58

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1032 +0,0 @@
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 };