@elxjs/ui 0.0.181 → 0.0.183
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/icons/MeatballMenu.svg +3 -0
- package/esm2022/lib/components/carousel/carousel.component.mjs +71 -53
- package/esm2022/lib/components/carousel-fixed/carousel-fixed.component.mjs +258 -186
- package/fesm2022/elxjs-ui.mjs +326 -237
- package/fesm2022/elxjs-ui.mjs.map +1 -1
- package/lib/components/carousel/carousel.component.d.ts +7 -9
- package/lib/components/carousel-fixed/carousel-fixed.component.d.ts +21 -18
- package/package.json +1 -1
package/fesm2022/elxjs-ui.mjs
CHANGED
|
@@ -1304,7 +1304,6 @@ class EluxCarouselComponent {
|
|
|
1304
1304
|
this.previousClickEventEmitter = new EventEmitter();
|
|
1305
1305
|
this.nextClickEventEmitter = new EventEmitter();
|
|
1306
1306
|
this.dotClickEventEmitter = new EventEmitter();
|
|
1307
|
-
this.itemClickEventEmitter = new EventEmitter();
|
|
1308
1307
|
this.currentIndex = 0;
|
|
1309
1308
|
this.currentStepIndex = 0;
|
|
1310
1309
|
this.offset = 0;
|
|
@@ -1321,17 +1320,21 @@ class EluxCarouselComponent {
|
|
|
1321
1320
|
this.velocity = 0;
|
|
1322
1321
|
this.lastMoveTime = 0;
|
|
1323
1322
|
this.lastMoveX = 0;
|
|
1324
|
-
this.wasDragging = false;
|
|
1325
1323
|
this.touchStartTime = 0;
|
|
1326
1324
|
this.touchStartX = 0;
|
|
1327
1325
|
this.touchStartY = 0;
|
|
1328
1326
|
this.isVerticalScroll = false;
|
|
1327
|
+
this.wasDragging = false;
|
|
1328
|
+
this.clicksDisabled = false;
|
|
1329
|
+
this.clickListeners = [];
|
|
1330
|
+
this.dragStarted = false;
|
|
1331
|
+
this.mouseDownTime = 0;
|
|
1329
1332
|
}
|
|
1330
1333
|
ngAfterContentInit() {
|
|
1331
1334
|
this.calculateDimensions();
|
|
1332
1335
|
this.setItemWidths();
|
|
1333
1336
|
this.updateLimits();
|
|
1334
|
-
this.
|
|
1337
|
+
this.setupClickPrevention();
|
|
1335
1338
|
if (this.autoPlayInterval) {
|
|
1336
1339
|
this.startAutoPlay();
|
|
1337
1340
|
}
|
|
@@ -1340,7 +1343,7 @@ class EluxCarouselComponent {
|
|
|
1340
1343
|
this.setItemWidths();
|
|
1341
1344
|
this.updateLimits();
|
|
1342
1345
|
this.goToFirstSlide();
|
|
1343
|
-
this.
|
|
1346
|
+
this.setupClickPrevention();
|
|
1344
1347
|
});
|
|
1345
1348
|
// Listen for window resize to recalculate dimensions
|
|
1346
1349
|
window.addEventListener('resize', () => this.onResize());
|
|
@@ -1350,6 +1353,9 @@ class EluxCarouselComponent {
|
|
|
1350
1353
|
if (this.animationId) {
|
|
1351
1354
|
cancelAnimationFrame(this.animationId);
|
|
1352
1355
|
}
|
|
1356
|
+
// Clean up click listeners
|
|
1357
|
+
this.clickListeners.forEach(unlisten => unlisten());
|
|
1358
|
+
this.clickListeners = [];
|
|
1353
1359
|
window.removeEventListener('resize', () => this.onResize());
|
|
1354
1360
|
}
|
|
1355
1361
|
calculateDimensions() {
|
|
@@ -1390,21 +1396,40 @@ class EluxCarouselComponent {
|
|
|
1390
1396
|
// Mouse events
|
|
1391
1397
|
onMouseDown(event) {
|
|
1392
1398
|
event.preventDefault();
|
|
1399
|
+
this.mouseDownTime = Date.now();
|
|
1400
|
+
this.dragStarted = false;
|
|
1393
1401
|
this.startDrag(event.clientX);
|
|
1394
1402
|
}
|
|
1395
1403
|
onMouseMove(event) {
|
|
1396
1404
|
if (this.isDragging) {
|
|
1397
1405
|
event.preventDefault();
|
|
1406
|
+
// Mark that we've started dragging if we've moved more than 5px
|
|
1407
|
+
const dragDistance = Math.abs(this.currentX - this.startX);
|
|
1408
|
+
if (dragDistance > 5 && !this.dragStarted) {
|
|
1409
|
+
this.dragStarted = true;
|
|
1410
|
+
this.wasDragging = true;
|
|
1411
|
+
}
|
|
1398
1412
|
this.updateDrag(event.clientX);
|
|
1399
1413
|
}
|
|
1400
1414
|
}
|
|
1401
1415
|
onMouseUp(event) {
|
|
1402
1416
|
if (this.isDragging) {
|
|
1417
|
+
const mouseUpTime = Date.now();
|
|
1418
|
+
const holdDuration = mouseUpTime - this.mouseDownTime;
|
|
1419
|
+
const mouseDistance = Math.abs(this.currentX - this.startX);
|
|
1420
|
+
// Consider it a drag if:
|
|
1421
|
+
// 1. We moved more than 5px, OR
|
|
1422
|
+
// 2. We held the mouse down for more than 200ms and moved at all
|
|
1423
|
+
if (mouseDistance > 5 || (holdDuration > 200 && mouseDistance > 0)) {
|
|
1424
|
+
this.wasDragging = true;
|
|
1425
|
+
}
|
|
1403
1426
|
this.endDrag();
|
|
1404
1427
|
}
|
|
1405
1428
|
}
|
|
1406
1429
|
onMouseLeave(event) {
|
|
1407
1430
|
if (this.isDragging) {
|
|
1431
|
+
// If mouse leaves while dragging, definitely consider it a drag
|
|
1432
|
+
this.wasDragging = true;
|
|
1408
1433
|
this.endDrag();
|
|
1409
1434
|
}
|
|
1410
1435
|
}
|
|
@@ -1427,7 +1452,6 @@ class EluxCarouselComponent {
|
|
|
1427
1452
|
if (deltaY > deltaX && deltaY > 10) {
|
|
1428
1453
|
this.isVerticalScroll = true;
|
|
1429
1454
|
this.isDragging = false;
|
|
1430
|
-
this.wasDragging = false;
|
|
1431
1455
|
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'transform 0.4s ease-out');
|
|
1432
1456
|
return;
|
|
1433
1457
|
}
|
|
@@ -1444,53 +1468,41 @@ class EluxCarouselComponent {
|
|
|
1444
1468
|
}
|
|
1445
1469
|
const touchDuration = Date.now() - this.touchStartTime;
|
|
1446
1470
|
const touchDistance = Math.abs(this.currentX - this.startX);
|
|
1447
|
-
//
|
|
1448
|
-
if (
|
|
1449
|
-
this.wasDragging =
|
|
1450
|
-
this.isDragging = false;
|
|
1471
|
+
// Mark as dragging if significant movement occurred
|
|
1472
|
+
if (touchDistance > 10 || touchDuration > 300) {
|
|
1473
|
+
this.wasDragging = true;
|
|
1451
1474
|
}
|
|
1452
1475
|
if (this.isDragging) {
|
|
1453
1476
|
this.endDrag();
|
|
1454
1477
|
}
|
|
1455
1478
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
//
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
const touchDuration = Date.now() - this.touchStartTime;
|
|
1469
|
-
const touchDistance = Math.abs(event.changedTouches[0].clientX - this.touchStartX);
|
|
1470
|
-
if (touchDuration < 300 && touchDistance < 10) {
|
|
1471
|
-
event.preventDefault();
|
|
1472
|
-
this.handleItemClick(event, index, newItem);
|
|
1473
|
-
}
|
|
1479
|
+
setupClickPrevention() {
|
|
1480
|
+
// Clean up existing listeners
|
|
1481
|
+
this.clickListeners.forEach(unlisten => unlisten());
|
|
1482
|
+
this.clickListeners = [];
|
|
1483
|
+
this.items.forEach((item) => {
|
|
1484
|
+
// Only prevent click events, not mouseup which is needed for drag functionality
|
|
1485
|
+
const eventHandler = (event) => {
|
|
1486
|
+
if (event.type === 'click' && (this.wasDragging || this.clicksDisabled || this.dragStarted)) {
|
|
1487
|
+
event.preventDefault();
|
|
1488
|
+
event.stopPropagation();
|
|
1489
|
+
event.stopImmediatePropagation();
|
|
1490
|
+
return false;
|
|
1474
1491
|
}
|
|
1492
|
+
return true;
|
|
1493
|
+
};
|
|
1494
|
+
// Only listen to click events to avoid interfering with drag functionality
|
|
1495
|
+
item.nativeElement.addEventListener('click', eventHandler, true);
|
|
1496
|
+
this.clickListeners.push(() => {
|
|
1497
|
+
item.nativeElement.removeEventListener('click', eventHandler, true);
|
|
1475
1498
|
});
|
|
1476
1499
|
});
|
|
1477
1500
|
}
|
|
1478
|
-
handleItemClick(event, index, element) {
|
|
1479
|
-
// Prevent click if it was preceded by a drag
|
|
1480
|
-
if (this.wasDragging) {
|
|
1481
|
-
event.preventDefault();
|
|
1482
|
-
event.stopPropagation();
|
|
1483
|
-
return;
|
|
1484
|
-
}
|
|
1485
|
-
this.itemClickEventEmitter.emit({
|
|
1486
|
-
event,
|
|
1487
|
-
index,
|
|
1488
|
-
element
|
|
1489
|
-
});
|
|
1490
|
-
}
|
|
1491
1501
|
startDrag(clientX) {
|
|
1492
1502
|
this.isDragging = true;
|
|
1493
1503
|
this.wasDragging = false;
|
|
1504
|
+
this.dragStarted = false;
|
|
1505
|
+
this.clicksDisabled = false;
|
|
1494
1506
|
this.startX = clientX;
|
|
1495
1507
|
this.currentX = clientX;
|
|
1496
1508
|
this.initialOffset = this.offset;
|
|
@@ -1506,13 +1518,13 @@ class EluxCarouselComponent {
|
|
|
1506
1518
|
updateDrag(clientX) {
|
|
1507
1519
|
if (!this.isDragging)
|
|
1508
1520
|
return;
|
|
1509
|
-
// More lenient threshold for mobile - mark as dragging only for significant movement
|
|
1510
|
-
const deltaX = Math.abs(this.currentX - this.startX);
|
|
1511
|
-
if (deltaX > 15) { // Increased from 5px to 15px for mobile
|
|
1512
|
-
this.wasDragging = true;
|
|
1513
|
-
}
|
|
1514
1521
|
this.currentX = clientX;
|
|
1515
1522
|
const totalDeltaX = this.currentX - this.startX;
|
|
1523
|
+
// Mark as dragging if movement is significant
|
|
1524
|
+
if (Math.abs(totalDeltaX) > 5) {
|
|
1525
|
+
this.wasDragging = true;
|
|
1526
|
+
this.dragStarted = true;
|
|
1527
|
+
}
|
|
1516
1528
|
// Calculate velocity for momentum
|
|
1517
1529
|
const now = Date.now();
|
|
1518
1530
|
const timeDelta = now - this.lastMoveTime;
|
|
@@ -1545,17 +1557,25 @@ class EluxCarouselComponent {
|
|
|
1545
1557
|
if (!this.isDragging)
|
|
1546
1558
|
return;
|
|
1547
1559
|
this.isDragging = false;
|
|
1548
|
-
//
|
|
1560
|
+
// Disable clicks temporarily after any drag
|
|
1561
|
+
if (this.wasDragging || this.dragStarted) {
|
|
1562
|
+
this.clicksDisabled = true;
|
|
1563
|
+
setTimeout(() => {
|
|
1564
|
+
this.clicksDisabled = false;
|
|
1565
|
+
}, 200);
|
|
1566
|
+
}
|
|
1567
|
+
// Clear the wasDragging and dragStarted flags after a shorter delay
|
|
1549
1568
|
setTimeout(() => {
|
|
1550
1569
|
this.wasDragging = false;
|
|
1551
|
-
|
|
1570
|
+
this.dragStarted = false;
|
|
1571
|
+
}, 250);
|
|
1552
1572
|
// Re-enable CSS transitions
|
|
1553
1573
|
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'transform 0.4s ease-out');
|
|
1554
1574
|
const finalOffset = this.initialOffset + this.dragOffset;
|
|
1555
|
-
const threshold = 40;
|
|
1575
|
+
const threshold = 40;
|
|
1556
1576
|
const dragDistance = Math.abs(this.currentX - this.startX);
|
|
1557
1577
|
// Check for momentum (velocity-based slide change)
|
|
1558
|
-
const momentumThreshold = 0.3;
|
|
1578
|
+
const momentumThreshold = 0.3;
|
|
1559
1579
|
const hasSignificantVelocity = Math.abs(this.velocity) > momentumThreshold;
|
|
1560
1580
|
if (dragDistance > threshold || hasSignificantVelocity) {
|
|
1561
1581
|
if (this.dragOffset > 0 || (hasSignificantVelocity && this.velocity > 0)) {
|
|
@@ -1656,7 +1676,7 @@ class EluxCarouselComponent {
|
|
|
1656
1676
|
});
|
|
1657
1677
|
}
|
|
1658
1678
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EluxCarouselComponent, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1659
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: EluxCarouselComponent, isStandalone: true, selector: "lib-elux-carousel", inputs: { itemsPerSlide: "itemsPerSlide", removeArrows: "removeArrows", removeDots: "removeDots", autoPlayInterval: "autoPlayInterval" }, outputs: { previousClickEventEmitter: "previousClickEventEmitter", nextClickEventEmitter: "nextClickEventEmitter", dotClickEventEmitter: "dotClickEventEmitter"
|
|
1679
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: EluxCarouselComponent, isStandalone: true, selector: "lib-elux-carousel", inputs: { itemsPerSlide: "itemsPerSlide", removeArrows: "removeArrows", removeDots: "removeDots", autoPlayInterval: "autoPlayInterval" }, outputs: { previousClickEventEmitter: "previousClickEventEmitter", nextClickEventEmitter: "nextClickEventEmitter", dotClickEventEmitter: "dotClickEventEmitter" }, queries: [{ propertyName: "items", predicate: ["carouselItem"], read: ElementRef }], viewQueries: [{ propertyName: "dotsContainer", first: true, predicate: ["dotsContainer"], descendants: true }, { propertyName: "slidesContainer", first: true, predicate: ["slidesContainer"], descendants: true }], ngImport: i0, template: "<div class=\"c-carousel-container\">\n <div \n class=\"c-carousel-slides\" \n #slidesContainer\n [style.transform]=\"'translateX(' + offset + '%)'\" \n (mousedown)=\"onMouseDown($event)\"\n (mousemove)=\"onMouseMove($event)\"\n (mouseup)=\"onMouseUp($event)\"\n (mouseleave)=\"onMouseLeave($event)\"\n (touchstart)=\"onTouchStart($event)\" \n (touchmove)=\"onTouchMove($event)\" \n (touchend)=\"onTouchEnd($event)\">\n <ng-content></ng-content>\n </div>\n\n <div class=\"c-carousel-footer\">\n <button [hidden]=\"removeArrows\" class=\"c-carousel-nav prev\" title=\"Slide anterior\" aria-label=\"Slide anterior\" (click)=\"prevSlide()\">\n <lib-elux-icon [icon]=\"'Chevron_Left.svg'\" [size]=\"'16'\" [sizeType]=\"'px'\" [color]=\"'#020F2E'\"></lib-elux-icon>\n </button>\n\n <div [hidden]=\"removeDots\" class=\"c-carousel-dots\" #dotsContainer>\n <span\n role=\"button\"\n class=\"dot\"\n *ngFor=\"let index of getStepIndexCount()\"\n (click)=\"goToSlide(index)\"\n (keyup)=\"goToSlide(index)\"\n [class.active]=\"index === getCurrentStepIndex()\"\n [attr.aria-disabled]=\"true\"\n [attr.aria-label]=\"'pagina' + (index + 1)\"\n >\n </span>\n </div>\n\n <button [hidden]=\"removeArrows\" class=\"c-carousel-nav next\" title=\"Pr\u00F3ximo slide\" aria-label=\"Pr\u00F3ximo slide\" (click)=\"nextSlide()\">\n <lib-elux-icon [icon]=\"'Chevron_Right.svg'\" [size]=\"'16'\" [sizeType]=\"'px'\" [color]=\"'#020F2E'\"></lib-elux-icon>\n </button>\n </div>\n</div>\n", styles: [".display{font-family:Electrolux Sans;font-size:48px;font-weight:700;line-height:56px}.heading-1{font-family:Electrolux Sans;font-size:34px;font-weight:700;line-height:40px}.heading-2{font-family:Electrolux Sans;font-size:28px;font-weight:600;line-height:32px}.heading-3{font-family:Electrolux Sans;font-size:20px;font-weight:600;line-height:24px}.subtitle-1{font-family:Electrolux Sans;font-size:16px;font-weight:600;line-height:20px}.body{font-family:Electrolux Sans;font-size:16px;font-weight:400;line-height:21px}.caption{font-family:Electrolux Sans;font-size:14px;font-weight:400;line-height:normal}.links{font-family:Electrolux Sans;font-size:16px;font-weight:600;line-height:normal;text-decoration-line:underline}@media screen and (max-width: 768px){.display{font-family:Electrolux Sans;font-size:40px;font-weight:700;line-height:46px}.heading-1{font-family:Electrolux Sans;font-size:28px;font-weight:600;line-height:32px}.heading-2{font-family:Electrolux Sans;font-size:24px;font-weight:600;line-height:28px}.heading-3{font-family:Electrolux Sans;font-size:20px;font-weight:600;line-height:24px}.subtitle-1{font-family:Electrolux Sans;font-size:16px;font-weight:600;line-height:20px}.body{font-family:Electrolux Sans;font-size:16px;font-weight:400;line-height:21px}.caption{font-family:Electrolux Sans;font-size:14px;font-weight:400;line-height:normal}.links{font-family:Electrolux Sans;font-size:16px;font-weight:600;line-height:normal;text-decoration-line:underline}}.c-carousel-container{position:relative;width:100%}.c-carousel-slides{display:flex;transition:transform .4s ease-out;touch-action:pan-y;cursor:grab;-webkit-user-select:none;user-select:none;will-change:transform}.c-carousel-slides:active{cursor:grabbing}.c-carousel-item{display:flex;flex-direction:column;text-align:center;align-items:center;justify-content:center;gap:16px;height:150px;margin:6px;border:1px solid #dee7ea;border-radius:2px;cursor:pointer;pointer-events:auto}.c-carousel-item .c-span{font-weight:400;font-size:18px;color:#011e41}.c-carousel-footer{width:min-content;margin:auto;display:flex;align-items:center;justify-content:space-between;padding:16px 0 24px}.c-carousel-footer .c-carousel-nav{background:none;border:none;cursor:pointer;padding:8px;border-radius:50px;background-color:#dfe7ea;transition:all ease-in-out .2s;z-index:2}.c-carousel-footer .c-carousel-nav:hover{transform:scale(1.1)}.c-carousel-footer .c-carousel-nav ::ng-deep span{display:block}.c-carousel-footer .c-carousel-dots{padding:6px;display:flex;text-align:center;border:1px solid #020f2e;border-radius:16px;margin:0 16px;max-width:120px;overflow:hidden;position:relative}@media (max-width: 768px){.c-carousel-footer .c-carousel-dots{margin:0 12px;max-width:100px}}.c-carousel-footer .dot{height:10px;width:10px;margin:0 5px;background-color:#dfe7ea;border-radius:50%;display:inline-block;cursor:pointer;transition:all ease-in-out .2s;flex-shrink:0}.c-carousel-footer .dot:hover,.c-carousel-footer .dot.active{background-color:#011e41}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: EluxIcon, selector: "lib-elux-icon", inputs: ["color", "useSubscription", "icon", "description", "size", "height", "width", "sizeType"] }] }); }
|
|
1660
1680
|
}
|
|
1661
1681
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EluxCarouselComponent, decorators: [{
|
|
1662
1682
|
type: Component,
|
|
@@ -1684,8 +1704,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
1684
1704
|
type: Output
|
|
1685
1705
|
}], dotClickEventEmitter: [{
|
|
1686
1706
|
type: Output
|
|
1687
|
-
}], itemClickEventEmitter: [{
|
|
1688
|
-
type: Output
|
|
1689
1707
|
}] } });
|
|
1690
1708
|
|
|
1691
1709
|
class EluxSwitch {
|
|
@@ -1829,15 +1847,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
1829
1847
|
class EluxCarouselFixedComponent {
|
|
1830
1848
|
constructor(renderer) {
|
|
1831
1849
|
this.renderer = renderer;
|
|
1832
|
-
this.itemWidth = 200;
|
|
1833
|
-
this.gap = 16;
|
|
1834
|
-
this.
|
|
1850
|
+
this.itemWidth = 200;
|
|
1851
|
+
this.gap = 16;
|
|
1852
|
+
this.showArrows = true;
|
|
1853
|
+
this.showDots = true;
|
|
1835
1854
|
this.previousClickEventEmitter = new EventEmitter();
|
|
1836
1855
|
this.nextClickEventEmitter = new EventEmitter();
|
|
1837
|
-
this.itemClickEventEmitter = new EventEmitter();
|
|
1838
1856
|
this.currentIndex = 0;
|
|
1839
1857
|
this.translateX = 0;
|
|
1840
1858
|
this.isDragging = false;
|
|
1859
|
+
this.isMouseDown = false;
|
|
1841
1860
|
this.startX = 0;
|
|
1842
1861
|
this.currentX = 0;
|
|
1843
1862
|
this.initialTranslateX = 0;
|
|
@@ -1848,27 +1867,50 @@ class EluxCarouselFixedComponent {
|
|
|
1848
1867
|
this.velocity = 0;
|
|
1849
1868
|
this.lastMoveTime = 0;
|
|
1850
1869
|
this.lastMoveX = 0;
|
|
1851
|
-
this.wasDragging = false;
|
|
1852
1870
|
this.touchStartTime = 0;
|
|
1853
1871
|
this.touchStartX = 0;
|
|
1854
1872
|
this.touchStartY = 0;
|
|
1855
1873
|
this.isVerticalScroll = false;
|
|
1874
|
+
this.wasDragging = false;
|
|
1875
|
+
this.clicksDisabled = false;
|
|
1876
|
+
this.clickListeners = [];
|
|
1877
|
+
this.dragStarted = false;
|
|
1878
|
+
this.mouseDownTime = 0;
|
|
1879
|
+
this.calculateRightBleedContainer = () => {
|
|
1880
|
+
if (this.rightBleedContainer) {
|
|
1881
|
+
const el = this.rightBleedContainer.nativeElement;
|
|
1882
|
+
const left = el.getBoundingClientRect().left;
|
|
1883
|
+
let scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
1884
|
+
scrollbarWidth = scrollbarWidth == 0 || scrollbarWidth > 30 ? 0 : scrollbarWidth;
|
|
1885
|
+
el.style.setProperty('--scrollbar-width', `${scrollbarWidth}px`);
|
|
1886
|
+
el.style.setProperty('--left-offset', `${left}px`);
|
|
1887
|
+
}
|
|
1888
|
+
};
|
|
1856
1889
|
}
|
|
1857
1890
|
ngAfterContentInit() {
|
|
1858
|
-
|
|
1859
|
-
this.setItemWidths();
|
|
1860
|
-
this.updateLimits();
|
|
1861
|
-
this.setupItemClickListeners();
|
|
1862
|
-
this.items.changes.subscribe(() => {
|
|
1891
|
+
setTimeout(() => {
|
|
1863
1892
|
this.calculateDimensions();
|
|
1864
1893
|
this.setItemWidths();
|
|
1865
1894
|
this.updateLimits();
|
|
1866
|
-
this.
|
|
1867
|
-
this.
|
|
1868
|
-
|
|
1895
|
+
this.setupClickPrevention();
|
|
1896
|
+
this.items.changes.subscribe(() => {
|
|
1897
|
+
this.calculateDimensions();
|
|
1898
|
+
this.setItemWidths();
|
|
1899
|
+
this.updateLimits();
|
|
1900
|
+
this.goToFirstItem();
|
|
1901
|
+
this.setupClickPrevention();
|
|
1902
|
+
// Força atualização visual dos dots
|
|
1903
|
+
if (this.cd && typeof this.cd.detectChanges === 'function') {
|
|
1904
|
+
this.cd.detectChanges();
|
|
1905
|
+
}
|
|
1906
|
+
});
|
|
1907
|
+
}, 10);
|
|
1869
1908
|
window.addEventListener('resize', () => this.onResize());
|
|
1870
1909
|
}
|
|
1871
1910
|
ngOnDestroy() {
|
|
1911
|
+
// Clean up click listeners
|
|
1912
|
+
this.clickListeners.forEach(unlisten => unlisten());
|
|
1913
|
+
this.clickListeners = [];
|
|
1872
1914
|
window.removeEventListener('resize', () => this.onResize());
|
|
1873
1915
|
}
|
|
1874
1916
|
calculateDimensions() {
|
|
@@ -1877,23 +1919,41 @@ class EluxCarouselFixedComponent {
|
|
|
1877
1919
|
}
|
|
1878
1920
|
}
|
|
1879
1921
|
updateLimits() {
|
|
1880
|
-
const totalWidth = this.items.length * (this.itemWidth + this.gap)
|
|
1922
|
+
const totalWidth = this.items.length * (this.itemWidth + this.gap);
|
|
1881
1923
|
this.maxTranslateX = 0;
|
|
1882
|
-
// Calculate minimum translateX to ensure last item is fully visible
|
|
1883
1924
|
if (totalWidth > this.containerWidth) {
|
|
1884
1925
|
this.minTranslateX = this.containerWidth - totalWidth;
|
|
1885
|
-
// Add some padding to ensure last item is completely visible
|
|
1886
|
-
const paddingOffset = this.gap;
|
|
1887
|
-
this.minTranslateX -= paddingOffset;
|
|
1888
1926
|
}
|
|
1889
1927
|
else {
|
|
1890
1928
|
this.minTranslateX = 0;
|
|
1891
1929
|
}
|
|
1892
1930
|
}
|
|
1893
1931
|
onResize() {
|
|
1932
|
+
this.calculateRightBleedContainer();
|
|
1894
1933
|
this.calculateDimensions();
|
|
1895
1934
|
this.updateLimits();
|
|
1935
|
+
// Garante que o currentIndex não ultrapasse o novo máximo
|
|
1936
|
+
const maxIndex = this.getMaxIndex();
|
|
1937
|
+
if (this.currentIndex > maxIndex) {
|
|
1938
|
+
this.currentIndex = maxIndex;
|
|
1939
|
+
}
|
|
1896
1940
|
this.updateTransform();
|
|
1941
|
+
// Força detecção de mudanças para atualizar dots
|
|
1942
|
+
if (window.ng && window.ng.getInjector) {
|
|
1943
|
+
// Angular DevTools presente, não faz nada
|
|
1944
|
+
}
|
|
1945
|
+
else if (this.cd && typeof this.cd.detectChanges === 'function') {
|
|
1946
|
+
this.cd.detectChanges();
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
ngAfterViewInit() {
|
|
1950
|
+
// Garante cálculo correto após view carregada
|
|
1951
|
+
setTimeout(() => {
|
|
1952
|
+
this.calculateRightBleedContainer();
|
|
1953
|
+
this.calculateDimensions();
|
|
1954
|
+
this.updateLimits();
|
|
1955
|
+
this.updateTransform();
|
|
1956
|
+
});
|
|
1897
1957
|
}
|
|
1898
1958
|
setItemWidths() {
|
|
1899
1959
|
this.items.forEach((item) => {
|
|
@@ -1901,31 +1961,64 @@ class EluxCarouselFixedComponent {
|
|
|
1901
1961
|
this.renderer.setStyle(item.nativeElement, 'flex-shrink', '0');
|
|
1902
1962
|
this.renderer.setStyle(item.nativeElement, 'margin-right', `${this.gap}px`);
|
|
1903
1963
|
});
|
|
1904
|
-
// Remove margin from last item
|
|
1905
|
-
if (this.items.last) {
|
|
1906
|
-
this.renderer.setStyle(this.items.last.nativeElement, 'margin-right', '0');
|
|
1907
|
-
}
|
|
1908
1964
|
}
|
|
1909
1965
|
// Mouse events
|
|
1910
1966
|
onMouseDown(event) {
|
|
1911
1967
|
event.preventDefault();
|
|
1912
|
-
this.
|
|
1968
|
+
this.mouseDownTime = Date.now();
|
|
1969
|
+
this.dragStarted = false;
|
|
1970
|
+
this.isMouseDown = true;
|
|
1971
|
+
this.wasDragging = false;
|
|
1972
|
+
this.clicksDisabled = false;
|
|
1973
|
+
this.startX = event.clientX;
|
|
1974
|
+
this.currentX = event.clientX;
|
|
1975
|
+
this.initialTranslateX = this.translateX;
|
|
1976
|
+
this.dragOffset = 0;
|
|
1977
|
+
this.velocity = 0;
|
|
1978
|
+
this.lastMoveTime = Date.now();
|
|
1979
|
+
this.lastMoveX = event.clientX;
|
|
1980
|
+
this.calculateDimensions();
|
|
1981
|
+
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'none');
|
|
1913
1982
|
}
|
|
1914
1983
|
onMouseMove(event) {
|
|
1984
|
+
if (!this.isMouseDown)
|
|
1985
|
+
return;
|
|
1986
|
+
event.preventDefault();
|
|
1987
|
+
const dragDistance = Math.abs(event.clientX - this.startX);
|
|
1988
|
+
// Only start dragging if moved more than 5px
|
|
1989
|
+
if (dragDistance > 5 && !this.isDragging) {
|
|
1990
|
+
this.isDragging = true;
|
|
1991
|
+
this.dragStarted = true;
|
|
1992
|
+
this.wasDragging = true;
|
|
1993
|
+
}
|
|
1915
1994
|
if (this.isDragging) {
|
|
1916
|
-
event.preventDefault();
|
|
1917
1995
|
this.updateDrag(event.clientX);
|
|
1918
1996
|
}
|
|
1919
1997
|
}
|
|
1920
1998
|
onMouseUp(event) {
|
|
1999
|
+
if (!this.isMouseDown)
|
|
2000
|
+
return;
|
|
2001
|
+
this.isMouseDown = false;
|
|
1921
2002
|
if (this.isDragging) {
|
|
2003
|
+
const mouseUpTime = Date.now();
|
|
2004
|
+
const holdDuration = mouseUpTime - this.mouseDownTime;
|
|
2005
|
+
const mouseDistance = Math.abs(this.currentX - this.startX);
|
|
2006
|
+
if (mouseDistance > 5 || (holdDuration > 200 && mouseDistance > 0)) {
|
|
2007
|
+
this.wasDragging = true;
|
|
2008
|
+
}
|
|
1922
2009
|
this.endDrag();
|
|
1923
2010
|
}
|
|
2011
|
+
else {
|
|
2012
|
+
// Reset transition if it was just a click
|
|
2013
|
+
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'transform 0.3s ease-out');
|
|
2014
|
+
}
|
|
1924
2015
|
}
|
|
1925
2016
|
onMouseLeave(event) {
|
|
1926
2017
|
if (this.isDragging) {
|
|
2018
|
+
this.wasDragging = true;
|
|
1927
2019
|
this.endDrag();
|
|
1928
2020
|
}
|
|
2021
|
+
this.isMouseDown = false;
|
|
1929
2022
|
}
|
|
1930
2023
|
// Touch events
|
|
1931
2024
|
onTouchStart(event) {
|
|
@@ -1933,10 +2026,22 @@ class EluxCarouselFixedComponent {
|
|
|
1933
2026
|
this.touchStartX = event.touches[0].clientX;
|
|
1934
2027
|
this.touchStartY = event.touches[0].clientY;
|
|
1935
2028
|
this.isVerticalScroll = false;
|
|
1936
|
-
this.
|
|
2029
|
+
this.isMouseDown = true;
|
|
2030
|
+
this.wasDragging = false;
|
|
2031
|
+
this.dragStarted = false;
|
|
2032
|
+
this.clicksDisabled = false;
|
|
2033
|
+
this.startX = event.touches[0].clientX;
|
|
2034
|
+
this.currentX = event.touches[0].clientX;
|
|
2035
|
+
this.initialTranslateX = this.translateX;
|
|
2036
|
+
this.dragOffset = 0;
|
|
2037
|
+
this.velocity = 0;
|
|
2038
|
+
this.lastMoveTime = Date.now();
|
|
2039
|
+
this.lastMoveX = event.touches[0].clientX;
|
|
2040
|
+
this.calculateDimensions();
|
|
2041
|
+
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'none');
|
|
1937
2042
|
}
|
|
1938
2043
|
onTouchMove(event) {
|
|
1939
|
-
if (!this.
|
|
2044
|
+
if (!this.isMouseDown)
|
|
1940
2045
|
return;
|
|
1941
2046
|
const currentX = event.touches[0].clientX;
|
|
1942
2047
|
const currentY = event.touches[0].clientY;
|
|
@@ -1946,88 +2051,73 @@ class EluxCarouselFixedComponent {
|
|
|
1946
2051
|
if (deltaY > deltaX && deltaY > 10) {
|
|
1947
2052
|
this.isVerticalScroll = true;
|
|
1948
2053
|
this.isDragging = false;
|
|
1949
|
-
this.
|
|
2054
|
+
this.isMouseDown = false;
|
|
1950
2055
|
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'transform 0.3s ease-out');
|
|
1951
2056
|
return;
|
|
1952
2057
|
}
|
|
1953
|
-
//
|
|
1954
|
-
if (deltaX > 10) {
|
|
2058
|
+
// Start dragging if horizontal movement > 10px
|
|
2059
|
+
if (deltaX > 10 && !this.isDragging) {
|
|
2060
|
+
this.isDragging = true;
|
|
2061
|
+
this.dragStarted = true;
|
|
2062
|
+
this.wasDragging = true;
|
|
2063
|
+
}
|
|
2064
|
+
if (this.isDragging && deltaX > 10) {
|
|
1955
2065
|
event.preventDefault();
|
|
1956
|
-
this.updateDrag(
|
|
2066
|
+
this.updateDrag(currentX);
|
|
1957
2067
|
}
|
|
1958
2068
|
}
|
|
1959
2069
|
onTouchEnd(event) {
|
|
1960
2070
|
if (this.isVerticalScroll) {
|
|
1961
2071
|
this.isVerticalScroll = false;
|
|
2072
|
+
this.isMouseDown = false;
|
|
1962
2073
|
return;
|
|
1963
2074
|
}
|
|
2075
|
+
this.isMouseDown = false;
|
|
1964
2076
|
const touchDuration = Date.now() - this.touchStartTime;
|
|
1965
2077
|
const touchDistance = Math.abs(this.currentX - this.startX);
|
|
1966
|
-
//
|
|
1967
|
-
if (
|
|
1968
|
-
this.wasDragging =
|
|
1969
|
-
this.isDragging = false;
|
|
2078
|
+
// Mark as dragging if significant movement occurred
|
|
2079
|
+
if (touchDistance > 10 || touchDuration > 300) {
|
|
2080
|
+
this.wasDragging = true;
|
|
1970
2081
|
}
|
|
1971
2082
|
if (this.isDragging) {
|
|
1972
2083
|
this.endDrag();
|
|
1973
2084
|
}
|
|
2085
|
+
else {
|
|
2086
|
+
// Reset transition if it was just a tap
|
|
2087
|
+
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'transform 0.3s ease-out');
|
|
2088
|
+
}
|
|
1974
2089
|
}
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
//
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
const touchDuration = Date.now() - this.touchStartTime;
|
|
1988
|
-
const touchDistance = Math.abs(event.changedTouches[0].clientX - this.touchStartX);
|
|
1989
|
-
if (touchDuration < 300 && touchDistance < 10) {
|
|
1990
|
-
event.preventDefault();
|
|
1991
|
-
this.handleItemClick(event, index, newItem);
|
|
1992
|
-
}
|
|
2090
|
+
setupClickPrevention() {
|
|
2091
|
+
// Clean up existing listeners
|
|
2092
|
+
this.clickListeners.forEach(unlisten => unlisten());
|
|
2093
|
+
this.clickListeners = [];
|
|
2094
|
+
this.items.forEach((item) => {
|
|
2095
|
+
// Only prevent click events, not mouseup which is needed for drag functionality
|
|
2096
|
+
const eventHandler = (event) => {
|
|
2097
|
+
if (event.type === 'click' && (this.wasDragging || this.clicksDisabled || this.dragStarted)) {
|
|
2098
|
+
event.preventDefault();
|
|
2099
|
+
event.stopPropagation();
|
|
2100
|
+
event.stopImmediatePropagation();
|
|
2101
|
+
return false;
|
|
1993
2102
|
}
|
|
2103
|
+
return true;
|
|
2104
|
+
};
|
|
2105
|
+
// Only listen to click events to avoid interfering with drag functionality
|
|
2106
|
+
item.nativeElement.addEventListener('click', eventHandler, true);
|
|
2107
|
+
this.clickListeners.push(() => {
|
|
2108
|
+
item.nativeElement.removeEventListener('click', eventHandler, true);
|
|
1994
2109
|
});
|
|
1995
2110
|
});
|
|
1996
2111
|
}
|
|
1997
|
-
handleItemClick(event, index, element) {
|
|
1998
|
-
// Prevent click if it was preceded by a drag
|
|
1999
|
-
if (this.wasDragging) {
|
|
2000
|
-
event.preventDefault();
|
|
2001
|
-
event.stopPropagation();
|
|
2002
|
-
return;
|
|
2003
|
-
}
|
|
2004
|
-
this.itemClickEventEmitter.emit({
|
|
2005
|
-
event,
|
|
2006
|
-
index,
|
|
2007
|
-
element
|
|
2008
|
-
});
|
|
2009
|
-
}
|
|
2010
|
-
startDrag(clientX) {
|
|
2011
|
-
this.isDragging = true;
|
|
2012
|
-
this.wasDragging = false;
|
|
2013
|
-
this.startX = clientX;
|
|
2014
|
-
this.currentX = clientX;
|
|
2015
|
-
this.initialTranslateX = this.translateX;
|
|
2016
|
-
this.dragOffset = 0;
|
|
2017
|
-
this.velocity = 0;
|
|
2018
|
-
this.lastMoveTime = Date.now();
|
|
2019
|
-
this.lastMoveX = clientX;
|
|
2020
|
-
this.calculateDimensions();
|
|
2021
|
-
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'none');
|
|
2022
|
-
}
|
|
2023
2112
|
updateDrag(clientX) {
|
|
2024
2113
|
if (!this.isDragging)
|
|
2025
2114
|
return;
|
|
2026
2115
|
this.currentX = clientX;
|
|
2027
2116
|
const totalDeltaX = this.currentX - this.startX;
|
|
2028
|
-
//
|
|
2029
|
-
if (Math.abs(totalDeltaX) >
|
|
2117
|
+
// Mark as dragging if movement is significant
|
|
2118
|
+
if (Math.abs(totalDeltaX) > 5) {
|
|
2030
2119
|
this.wasDragging = true;
|
|
2120
|
+
this.dragStarted = true;
|
|
2031
2121
|
}
|
|
2032
2122
|
const now = Date.now();
|
|
2033
2123
|
const timeDelta = now - this.lastMoveTime;
|
|
@@ -2041,11 +2131,11 @@ class EluxCarouselFixedComponent {
|
|
|
2041
2131
|
// Apply softer resistance at boundaries
|
|
2042
2132
|
if (potentialTranslateX > this.maxTranslateX) {
|
|
2043
2133
|
const overDrag = potentialTranslateX - this.maxTranslateX;
|
|
2044
|
-
resistedDeltaX = totalDeltaX - (overDrag * 0.5);
|
|
2134
|
+
resistedDeltaX = totalDeltaX - (overDrag * 0.5);
|
|
2045
2135
|
}
|
|
2046
2136
|
else if (potentialTranslateX < this.minTranslateX) {
|
|
2047
2137
|
const overDrag = this.minTranslateX - potentialTranslateX;
|
|
2048
|
-
resistedDeltaX = totalDeltaX + (overDrag * 0.5);
|
|
2138
|
+
resistedDeltaX = totalDeltaX + (overDrag * 0.5);
|
|
2049
2139
|
}
|
|
2050
2140
|
this.dragOffset = resistedDeltaX;
|
|
2051
2141
|
this.updateDragPosition();
|
|
@@ -2058,164 +2148,163 @@ class EluxCarouselFixedComponent {
|
|
|
2058
2148
|
if (!this.isDragging)
|
|
2059
2149
|
return;
|
|
2060
2150
|
this.isDragging = false;
|
|
2061
|
-
//
|
|
2151
|
+
// Disable clicks temporarily after any drag
|
|
2152
|
+
if (this.wasDragging || this.dragStarted) {
|
|
2153
|
+
this.clicksDisabled = true;
|
|
2154
|
+
setTimeout(() => {
|
|
2155
|
+
this.clicksDisabled = false;
|
|
2156
|
+
}, 200);
|
|
2157
|
+
}
|
|
2158
|
+
// Clear the wasDragging and dragStarted flags after a shorter delay
|
|
2062
2159
|
setTimeout(() => {
|
|
2063
2160
|
this.wasDragging = false;
|
|
2064
|
-
|
|
2161
|
+
this.dragStarted = false;
|
|
2162
|
+
}, 250);
|
|
2065
2163
|
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'transform 0.3s ease-out');
|
|
2066
|
-
//
|
|
2067
|
-
|
|
2068
|
-
const itemStep = this.itemWidth + this.gap;
|
|
2069
|
-
const movedItemsFloat = Math.abs(currentTranslateX - this.initialTranslateX) / itemStep;
|
|
2070
|
-
// Adjusted thresholds for better mobile experience
|
|
2071
|
-
const minThreshold = 0.25; // Reduced from 0.3 to 0.25
|
|
2072
|
-
const dragDistance = Math.abs(this.currentX - this.startX);
|
|
2073
|
-
const momentumThreshold = 0.2; // Reduced from 0.3 to 0.2
|
|
2074
|
-
const hasSignificantVelocity = Math.abs(this.velocity) > momentumThreshold;
|
|
2075
|
-
// More lenient conditions for mobile
|
|
2076
|
-
if (movedItemsFloat > minThreshold || hasSignificantVelocity || dragDistance > 40) { // Reduced from 60px to 40px
|
|
2077
|
-
// Calculate how many items to advance based on actual drag distance
|
|
2078
|
-
let itemsToAdvance = Math.round(movedItemsFloat);
|
|
2079
|
-
// For velocity-based movement, use a separate calculation
|
|
2080
|
-
if (hasSignificantVelocity && movedItemsFloat < 1) {
|
|
2081
|
-
const velocityMultiplier = Math.abs(this.velocity) * 2;
|
|
2082
|
-
itemsToAdvance = Math.max(1, Math.floor(velocityMultiplier));
|
|
2083
|
-
}
|
|
2084
|
-
// Ensure minimum 1 item for significant drags, but don't double count
|
|
2085
|
-
if (itemsToAdvance === 0 && movedItemsFloat >= minThreshold) {
|
|
2086
|
-
itemsToAdvance = 1;
|
|
2087
|
-
}
|
|
2088
|
-
if (this.dragOffset > 0 || (hasSignificantVelocity && this.velocity > 0)) {
|
|
2089
|
-
// Dragged right - go to previous items
|
|
2090
|
-
this.goToPreviousItems(itemsToAdvance);
|
|
2091
|
-
}
|
|
2092
|
-
else if (this.dragOffset < 0 || (hasSignificantVelocity && this.velocity < 0)) {
|
|
2093
|
-
// Dragged left - go to next items
|
|
2094
|
-
this.goToNextItems(itemsToAdvance);
|
|
2095
|
-
}
|
|
2096
|
-
else {
|
|
2097
|
-
this.snapToNearestItem();
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
else {
|
|
2101
|
-
// Small movement - snap to nearest item
|
|
2102
|
-
this.snapToNearestItem();
|
|
2103
|
-
}
|
|
2164
|
+
// Snap sempre para o início da página mais próxima
|
|
2165
|
+
this.snapToNearestItem();
|
|
2104
2166
|
this.dragOffset = 0;
|
|
2105
2167
|
this.velocity = 0;
|
|
2106
2168
|
}
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
this.
|
|
2116
|
-
this.updateTransform();
|
|
2169
|
+
getMaxIndex() {
|
|
2170
|
+
// Permite avançar até que o último item esteja visível (não necessariamente alinhado à esquerda)
|
|
2171
|
+
if (!this.items || this.items.length === 0)
|
|
2172
|
+
return 0;
|
|
2173
|
+
const visibleCount = Math.max(1, Math.floor(this.containerWidth / (this.itemWidth + this.gap)));
|
|
2174
|
+
if (this.items.length <= visibleCount)
|
|
2175
|
+
return 0;
|
|
2176
|
+
// Última posição onde o último item aparece visível
|
|
2177
|
+
return this.items.length - visibleCount;
|
|
2117
2178
|
}
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
const newIndex = Math.max(0, this.currentIndex - itemCount);
|
|
2121
|
-
if (newIndex !== this.currentIndex) {
|
|
2122
|
-
this.currentIndex = newIndex;
|
|
2123
|
-
this.updateTransform();
|
|
2124
|
-
this.previousClickEventEmitter.emit();
|
|
2125
|
-
}
|
|
2126
|
-
else {
|
|
2127
|
-
this.snapToNearestItem();
|
|
2128
|
-
}
|
|
2179
|
+
canGoNext() {
|
|
2180
|
+
return this.currentIndex < this.getMaxIndex();
|
|
2129
2181
|
}
|
|
2130
|
-
|
|
2131
|
-
// Use the current index instead of visual calculation to avoid double movement
|
|
2182
|
+
nextItem() {
|
|
2132
2183
|
const maxIndex = this.getMaxIndex();
|
|
2133
|
-
|
|
2134
|
-
|
|
2184
|
+
if (this.canGoNext()) {
|
|
2185
|
+
let newIndex = this.currentIndex + 1;
|
|
2186
|
+
if (newIndex > maxIndex)
|
|
2187
|
+
newIndex = maxIndex;
|
|
2135
2188
|
this.currentIndex = newIndex;
|
|
2136
2189
|
this.updateTransform();
|
|
2137
2190
|
this.nextClickEventEmitter.emit();
|
|
2138
2191
|
}
|
|
2139
|
-
else {
|
|
2140
|
-
this.snapToNearestItem();
|
|
2141
|
-
}
|
|
2142
2192
|
}
|
|
2143
2193
|
canGoPrevious() {
|
|
2144
2194
|
return this.currentIndex > 0;
|
|
2145
2195
|
}
|
|
2146
|
-
canGoNext() {
|
|
2147
|
-
return this.currentIndex < this.getMaxIndex();
|
|
2148
|
-
}
|
|
2149
|
-
goToFirstItem() {
|
|
2150
|
-
this.currentIndex = 0;
|
|
2151
|
-
this.updateTransform();
|
|
2152
|
-
}
|
|
2153
2196
|
prevItem() {
|
|
2154
2197
|
if (this.canGoPrevious()) {
|
|
2155
|
-
this.currentIndex
|
|
2198
|
+
let newIndex = this.currentIndex - 1;
|
|
2199
|
+
if (newIndex < 0)
|
|
2200
|
+
newIndex = 0;
|
|
2201
|
+
this.currentIndex = newIndex;
|
|
2156
2202
|
this.updateTransform();
|
|
2157
2203
|
this.previousClickEventEmitter.emit();
|
|
2158
2204
|
}
|
|
2159
2205
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2206
|
+
goToDot(dotIndex) {
|
|
2207
|
+
const maxIndex = this.getMaxIndex();
|
|
2208
|
+
let newIndex = dotIndex;
|
|
2209
|
+
if (newIndex > maxIndex)
|
|
2210
|
+
newIndex = maxIndex;
|
|
2211
|
+
this.currentIndex = newIndex;
|
|
2212
|
+
this.updateTransform();
|
|
2213
|
+
}
|
|
2214
|
+
snapToNearestItem() {
|
|
2215
|
+
const itemStep = this.itemWidth + this.gap;
|
|
2216
|
+
const position = -(this.initialTranslateX + this.dragOffset) / itemStep;
|
|
2217
|
+
// Threshold: 0.3 = 30%, 0.5 = 50% (padrão)
|
|
2218
|
+
const threshold = 0.3;
|
|
2219
|
+
let targetIndex;
|
|
2220
|
+
// Calcula quantos itens completos foram arrastados
|
|
2221
|
+
const itemsMoved = Math.floor(Math.abs(position - this.currentIndex));
|
|
2222
|
+
const decimal = Math.abs(position - this.currentIndex) - itemsMoved;
|
|
2223
|
+
if (position > this.currentIndex) {
|
|
2224
|
+
// Arrastando para frente
|
|
2225
|
+
if (decimal >= threshold) {
|
|
2226
|
+
targetIndex = this.currentIndex + itemsMoved + 1;
|
|
2227
|
+
}
|
|
2228
|
+
else {
|
|
2229
|
+
targetIndex = this.currentIndex + itemsMoved;
|
|
2230
|
+
}
|
|
2165
2231
|
}
|
|
2232
|
+
else if (position < this.currentIndex) {
|
|
2233
|
+
// Arrastando para trás
|
|
2234
|
+
if (decimal >= threshold) {
|
|
2235
|
+
targetIndex = this.currentIndex - itemsMoved - 1;
|
|
2236
|
+
}
|
|
2237
|
+
else {
|
|
2238
|
+
targetIndex = this.currentIndex - itemsMoved;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
else {
|
|
2242
|
+
// Não se moveu
|
|
2243
|
+
targetIndex = this.currentIndex;
|
|
2244
|
+
}
|
|
2245
|
+
const maxIndex = this.getMaxIndex();
|
|
2246
|
+
targetIndex = Math.max(0, Math.min(targetIndex, maxIndex));
|
|
2247
|
+
this.currentIndex = targetIndex;
|
|
2248
|
+
this.updateTransform();
|
|
2249
|
+
}
|
|
2250
|
+
getDotsArray() {
|
|
2251
|
+
// Só mostra dots para cada posição possível (cada "página" possível)
|
|
2252
|
+
const maxIndex = this.getMaxIndex();
|
|
2253
|
+
return Array(maxIndex + 1);
|
|
2254
|
+
}
|
|
2255
|
+
getCurrentDotIndex() {
|
|
2256
|
+
return this.currentIndex;
|
|
2166
2257
|
}
|
|
2167
2258
|
updateTransform() {
|
|
2168
2259
|
const itemStep = this.itemWidth + this.gap;
|
|
2169
2260
|
this.translateX = -this.currentIndex * itemStep;
|
|
2170
|
-
//
|
|
2171
|
-
|
|
2172
|
-
//
|
|
2173
|
-
|
|
2174
|
-
|
|
2261
|
+
// Clamp para não mostrar espaço vazio à direita
|
|
2262
|
+
const visibleCount = Math.max(1, Math.floor(this.containerWidth / itemStep));
|
|
2263
|
+
// Inclui o gap após o último item
|
|
2264
|
+
const totalWidth = this.items.length * itemStep;
|
|
2265
|
+
if (this.items.length > visibleCount) {
|
|
2266
|
+
const minTranslateX = this.containerWidth - totalWidth;
|
|
2267
|
+
this.translateX = Math.max(this.translateX, minTranslateX);
|
|
2175
2268
|
}
|
|
2176
2269
|
else {
|
|
2177
|
-
this.
|
|
2270
|
+
this.translateX = 0;
|
|
2178
2271
|
}
|
|
2179
2272
|
if (!this.isDragging && this.slidesContainer) {
|
|
2180
2273
|
this.renderer.setStyle(this.slidesContainer.nativeElement, 'transform', `translateX(${this.translateX}px)`);
|
|
2181
2274
|
}
|
|
2182
2275
|
}
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
return 0;
|
|
2188
|
-
}
|
|
2189
|
-
// Calculate how many complete items can fit in the visible area
|
|
2190
|
-
const itemsPerView = Math.floor(visibleWidth / (this.itemWidth + this.gap));
|
|
2191
|
-
// Maximum index ensures the last item is always fully visible
|
|
2192
|
-
const maxIndex = Math.max(0, this.items.length - itemsPerView);
|
|
2193
|
-
return maxIndex;
|
|
2276
|
+
// Adicione este método para evitar erro de propriedade ausente
|
|
2277
|
+
goToFirstItem() {
|
|
2278
|
+
this.currentIndex = 0;
|
|
2279
|
+
this.updateTransform();
|
|
2194
2280
|
}
|
|
2195
2281
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EluxCarouselFixedComponent, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2196
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: EluxCarouselFixedComponent, isStandalone: true, selector: "lib-elux-carousel-fixed", inputs: { itemWidth: "itemWidth", gap: "gap",
|
|
2282
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: EluxCarouselFixedComponent, isStandalone: true, selector: "lib-elux-carousel-fixed", inputs: { itemWidth: "itemWidth", gap: "gap", showArrows: "showArrows", showDots: "showDots" }, outputs: { previousClickEventEmitter: "previousClickEventEmitter", nextClickEventEmitter: "nextClickEventEmitter" }, queries: [{ propertyName: "items", predicate: ["carouselItem"], read: ElementRef }], viewQueries: [{ propertyName: "slidesContainer", first: true, predicate: ["slidesContainer"], descendants: true }, { propertyName: "rightBleedContainer", first: true, predicate: ["rightBleedContainer"], descendants: true }], ngImport: i0, template: "<div class=\"c-carousel-fixed-container\" #rightBleedContainer>\n <div \n class=\"c-carousel-fixed-slides\" \n #slidesContainer\n [style.transform]=\"'translateX(' + translateX + 'px)'\" \n (mousedown)=\"onMouseDown($event)\"\n (mousemove)=\"onMouseMove($event)\"\n (mouseup)=\"onMouseUp($event)\"\n (mouseleave)=\"onMouseLeave($event)\"\n (touchstart)=\"onTouchStart($event)\" \n (touchmove)=\"onTouchMove($event)\" \n (touchend)=\"onTouchEnd($event)\">\n <ng-content></ng-content>\n </div>\n</div>\n<div class=\"c-carousel-fixed-footer\" *ngIf=\"showArrows || showDots\">\n <button *ngIf=\"showArrows\"\n class=\"c-carousel-fixed-nav prev\" \n [disabled]=\"!canGoPrevious()\"\n title=\"Item anterior\" \n aria-label=\"Item anterior\" \n (click)=\"prevItem()\">\n <lib-elux-icon [icon]=\"'Chevron_Left.svg'\" [size]=\"'16'\" [sizeType]=\"'px'\" [color]=\"'#020F2E'\"></lib-elux-icon>\n </button>\n\n <div class=\"c-carousel-fixed-dots\" *ngIf=\"showDots\">\n <ng-container *ngFor=\"let dot of getDotsArray(); let idx = index\">\n <span \n class=\"c-carousel-fixed-dot\" \n [class.active]=\"idx === getCurrentDotIndex()\" \n (click)=\"goToDot(idx)\">\n </span>\n </ng-container>\n </div>\n\n <button *ngIf=\"showArrows\"\n class=\"c-carousel-fixed-nav next\" \n [disabled]=\"!canGoNext()\"\n title=\"Pr\u00F3ximo item\" \n aria-label=\"Pr\u00F3ximo item\" \n (click)=\"nextItem()\">\n <lib-elux-icon [icon]=\"'Chevron_Right.svg'\" [size]=\"'16'\" [sizeType]=\"'px'\" [color]=\"'#020F2E'\"></lib-elux-icon>\n </button>\n</div>\n", styles: [".c-carousel-fixed-container{overflow-x:hidden;width:calc(100vw - var(--left-offset, 0px) - var(--scrollbar-width, 0px))}.c-carousel-fixed-slides{display:flex;transition:transform .5s ease-out;touch-action:pan-y;cursor:grab;will-change:transform}.c-carousel-fixed-slides:active{cursor:grabbing}.c-carousel-fixed-footer{display:flex;justify-content:space-between;padding:16px}.c-carousel-fixed-footer .c-carousel-fixed-nav{background:none;border:none;cursor:pointer;padding:8px;border-radius:50px;background-color:#dfe7ea;transition:all ease-in-out .2s;z-index:2}.c-carousel-fixed-footer .c-carousel-fixed-nav:hover:not(:disabled){transform:scale(1.2)}.c-carousel-fixed-footer .c-carousel-fixed-nav:disabled{opacity:.3;cursor:not-allowed}.c-carousel-fixed-footer .c-carousel-fixed-nav:disabled:hover{transform:none}.c-carousel-fixed-footer .c-carousel-fixed-nav ::ng-deep span{display:block}.c-carousel-fixed-footer .c-carousel-fixed-dots{display:flex;align-items:center;gap:8px;margin:0 16px}.c-carousel-fixed-footer .c-carousel-fixed-dots .c-carousel-fixed-dot{width:10px;height:10px;border-radius:50%;background:#dfe7ea;transition:background .3s;cursor:pointer;display:inline-block}.c-carousel-fixed-footer .c-carousel-fixed-dots .c-carousel-fixed-dot.active{background:#011e41}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: EluxIcon, selector: "lib-elux-icon", inputs: ["color", "useSubscription", "icon", "description", "size", "height", "width", "sizeType"] }] }); }
|
|
2197
2283
|
}
|
|
2198
2284
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EluxCarouselFixedComponent, decorators: [{
|
|
2199
2285
|
type: Component,
|
|
2200
|
-
args: [{ standalone: true, selector: 'lib-elux-carousel-fixed', imports: [CommonModule, EluxIcon], template: "<div class=\"c-carousel-fixed-container\">\n <div \n
|
|
2286
|
+
args: [{ standalone: true, selector: 'lib-elux-carousel-fixed', imports: [CommonModule, EluxIcon], template: "<div class=\"c-carousel-fixed-container\" #rightBleedContainer>\n <div \n class=\"c-carousel-fixed-slides\" \n #slidesContainer\n [style.transform]=\"'translateX(' + translateX + 'px)'\" \n (mousedown)=\"onMouseDown($event)\"\n (mousemove)=\"onMouseMove($event)\"\n (mouseup)=\"onMouseUp($event)\"\n (mouseleave)=\"onMouseLeave($event)\"\n (touchstart)=\"onTouchStart($event)\" \n (touchmove)=\"onTouchMove($event)\" \n (touchend)=\"onTouchEnd($event)\">\n <ng-content></ng-content>\n </div>\n</div>\n<div class=\"c-carousel-fixed-footer\" *ngIf=\"showArrows || showDots\">\n <button *ngIf=\"showArrows\"\n class=\"c-carousel-fixed-nav prev\" \n [disabled]=\"!canGoPrevious()\"\n title=\"Item anterior\" \n aria-label=\"Item anterior\" \n (click)=\"prevItem()\">\n <lib-elux-icon [icon]=\"'Chevron_Left.svg'\" [size]=\"'16'\" [sizeType]=\"'px'\" [color]=\"'#020F2E'\"></lib-elux-icon>\n </button>\n\n <div class=\"c-carousel-fixed-dots\" *ngIf=\"showDots\">\n <ng-container *ngFor=\"let dot of getDotsArray(); let idx = index\">\n <span \n class=\"c-carousel-fixed-dot\" \n [class.active]=\"idx === getCurrentDotIndex()\" \n (click)=\"goToDot(idx)\">\n </span>\n </ng-container>\n </div>\n\n <button *ngIf=\"showArrows\"\n class=\"c-carousel-fixed-nav next\" \n [disabled]=\"!canGoNext()\"\n title=\"Pr\u00F3ximo item\" \n aria-label=\"Pr\u00F3ximo item\" \n (click)=\"nextItem()\">\n <lib-elux-icon [icon]=\"'Chevron_Right.svg'\" [size]=\"'16'\" [sizeType]=\"'px'\" [color]=\"'#020F2E'\"></lib-elux-icon>\n </button>\n</div>\n", styles: [".c-carousel-fixed-container{overflow-x:hidden;width:calc(100vw - var(--left-offset, 0px) - var(--scrollbar-width, 0px))}.c-carousel-fixed-slides{display:flex;transition:transform .5s ease-out;touch-action:pan-y;cursor:grab;will-change:transform}.c-carousel-fixed-slides:active{cursor:grabbing}.c-carousel-fixed-footer{display:flex;justify-content:space-between;padding:16px}.c-carousel-fixed-footer .c-carousel-fixed-nav{background:none;border:none;cursor:pointer;padding:8px;border-radius:50px;background-color:#dfe7ea;transition:all ease-in-out .2s;z-index:2}.c-carousel-fixed-footer .c-carousel-fixed-nav:hover:not(:disabled){transform:scale(1.2)}.c-carousel-fixed-footer .c-carousel-fixed-nav:disabled{opacity:.3;cursor:not-allowed}.c-carousel-fixed-footer .c-carousel-fixed-nav:disabled:hover{transform:none}.c-carousel-fixed-footer .c-carousel-fixed-nav ::ng-deep span{display:block}.c-carousel-fixed-footer .c-carousel-fixed-dots{display:flex;align-items:center;gap:8px;margin:0 16px}.c-carousel-fixed-footer .c-carousel-fixed-dots .c-carousel-fixed-dot{width:10px;height:10px;border-radius:50%;background:#dfe7ea;transition:background .3s;cursor:pointer;display:inline-block}.c-carousel-fixed-footer .c-carousel-fixed-dots .c-carousel-fixed-dot.active{background:#011e41}\n"] }]
|
|
2201
2287
|
}], ctorParameters: () => [{ type: i0.Renderer2 }], propDecorators: { items: [{
|
|
2202
2288
|
type: ContentChildren,
|
|
2203
2289
|
args: ['carouselItem', { read: ElementRef }]
|
|
2204
2290
|
}], slidesContainer: [{
|
|
2205
2291
|
type: ViewChild,
|
|
2206
2292
|
args: ['slidesContainer']
|
|
2293
|
+
}], rightBleedContainer: [{
|
|
2294
|
+
type: ViewChild,
|
|
2295
|
+
args: ['rightBleedContainer', { static: false }]
|
|
2207
2296
|
}], itemWidth: [{
|
|
2208
2297
|
type: Input
|
|
2209
2298
|
}], gap: [{
|
|
2210
2299
|
type: Input
|
|
2211
|
-
}],
|
|
2300
|
+
}], showArrows: [{
|
|
2301
|
+
type: Input
|
|
2302
|
+
}], showDots: [{
|
|
2212
2303
|
type: Input
|
|
2213
2304
|
}], previousClickEventEmitter: [{
|
|
2214
2305
|
type: Output
|
|
2215
2306
|
}], nextClickEventEmitter: [{
|
|
2216
2307
|
type: Output
|
|
2217
|
-
}], itemClickEventEmitter: [{
|
|
2218
|
-
type: Output
|
|
2219
2308
|
}] } });
|
|
2220
2309
|
|
|
2221
2310
|
class EluxArrowLink {
|