@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.
@@ -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.setupItemClickListeners();
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.setupItemClickListeners();
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
- // Consider it a tap if duration < 300ms and distance < 10px
1448
- if (touchDuration < 300 && touchDistance < 10) {
1449
- this.wasDragging = false;
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
- setupItemClickListeners() {
1457
- this.items.forEach((item, index) => {
1458
- // Remove any existing listeners
1459
- const newItem = item.nativeElement.cloneNode(true);
1460
- item.nativeElement.parentNode?.replaceChild(newItem, item.nativeElement);
1461
- // Add both click and touchend listeners for better mobile support
1462
- this.renderer.listen(newItem, 'click', (event) => {
1463
- this.handleItemClick(event, index, newItem);
1464
- });
1465
- this.renderer.listen(newItem, 'touchend', (event) => {
1466
- // Only handle touchend if it wasn't a drag
1467
- if (!this.wasDragging && !this.isVerticalScroll) {
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
- // Clear the wasDragging flag after a shorter delay for mobile
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
- }, 50); // Reduced from 100ms to 50ms
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; // Reduced from 50px to 40px for mobile
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; // Reduced from 0.5 to 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", itemClickEventEmitter: "itemClickEventEmitter" }, 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"] }] }); }
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; // Fixed width in pixels
1833
- this.gap = 16; // Gap between items in pixels
1834
- this.removeArrows = false;
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
- this.calculateDimensions();
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.goToFirstItem();
1867
- this.setupItemClickListeners();
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) - 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.startDrag(event.clientX);
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.startDrag(event.touches[0].clientX);
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.isDragging)
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.wasDragging = false;
2054
+ this.isMouseDown = false;
1950
2055
  this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'transform 0.3s ease-out');
1951
2056
  return;
1952
2057
  }
1953
- // Only prevent default if it's a horizontal drag
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(event.touches[0].clientX);
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
- // Consider it a tap if duration < 300ms and distance < 10px
1967
- if (touchDuration < 300 && touchDistance < 10) {
1968
- this.wasDragging = false;
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
- setupItemClickListeners() {
1976
- this.items.forEach((item, index) => {
1977
- // Remove any existing listeners
1978
- const newItem = item.nativeElement.cloneNode(true);
1979
- item.nativeElement.parentNode?.replaceChild(newItem, item.nativeElement);
1980
- // Add both click and touchend listeners for better mobile support
1981
- this.renderer.listen(newItem, 'click', (event) => {
1982
- this.handleItemClick(event, index, newItem);
1983
- });
1984
- this.renderer.listen(newItem, 'touchend', (event) => {
1985
- // Only handle touchend if it wasn't a drag
1986
- if (!this.wasDragging && !this.isVerticalScroll) {
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
- // More lenient threshold for mobile - mark as dragging only for significant movement
2029
- if (Math.abs(totalDeltaX) > 15) { // Increased from 5px to 15px for mobile
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); // Reduced resistance for more natural feel
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); // Reduced resistance for more natural feel
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
- // Clear the wasDragging flag after a shorter delay for mobile
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
- }, 50); // Reduced from 100ms to 50ms
2161
+ this.dragStarted = false;
2162
+ }, 250);
2065
2163
  this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'transform 0.3s ease-out');
2066
- // Calculate how far we've actually moved in terms of items
2067
- const currentTranslateX = this.initialTranslateX + this.dragOffset;
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
- snapToNearestItem() {
2108
- const currentTranslateX = this.initialTranslateX + this.dragOffset;
2109
- const itemStep = this.itemWidth + this.gap;
2110
- // Find the closest valid position
2111
- let targetIndex = Math.round(-currentTranslateX / itemStep);
2112
- // Clamp to valid bounds
2113
- const maxIndex = this.getMaxIndex();
2114
- targetIndex = Math.max(0, Math.min(targetIndex, maxIndex));
2115
- this.currentIndex = targetIndex;
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
- goToPreviousItems(itemCount) {
2119
- // Use the current index instead of visual calculation to avoid double movement
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
- goToNextItems(itemCount) {
2131
- // Use the current index instead of visual calculation to avoid double movement
2182
+ nextItem() {
2132
2183
  const maxIndex = this.getMaxIndex();
2133
- const newIndex = Math.min(maxIndex, this.currentIndex + itemCount);
2134
- if (newIndex !== this.currentIndex) {
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
- nextItem() {
2161
- if (this.canGoNext()) {
2162
- this.currentIndex++;
2163
- this.updateTransform();
2164
- this.nextClickEventEmitter.emit();
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
- // Apply strict boundaries to ensure last item is always fully visible
2171
- this.translateX = Math.max(this.minTranslateX, Math.min(this.maxTranslateX, this.translateX));
2172
- // Recalculate currentIndex based on actual translateX to stay in sync
2173
- if (this.translateX <= this.minTranslateX) {
2174
- this.currentIndex = this.getMaxIndex();
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.currentIndex = Math.max(0, Math.min(this.currentIndex, this.getMaxIndex()));
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
- getMaxIndex() {
2184
- const visibleWidth = this.containerWidth;
2185
- const totalItemsWidth = this.items.length * (this.itemWidth + this.gap) - this.gap;
2186
- if (totalItemsWidth <= visibleWidth) {
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", removeArrows: "removeArrows" }, outputs: { previousClickEventEmitter: "previousClickEventEmitter", nextClickEventEmitter: "nextClickEventEmitter", itemClickEventEmitter: "itemClickEventEmitter" }, queries: [{ propertyName: "items", predicate: ["carouselItem"], read: ElementRef }], viewQueries: [{ propertyName: "slidesContainer", first: true, predicate: ["slidesContainer"], descendants: true }], ngImport: i0, template: "<div class=\"c-carousel-fixed-container\">\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\n <div class=\"c-carousel-fixed-footer\" [hidden]=\"removeArrows\">\n <button \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 <button \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</div>\n", styles: [".c-carousel-fixed-container{position:relative;width:100%;padding-right:16px}.c-carousel-fixed-slides{display:flex;transition:transform .3s ease-out;touch-action:pan-y;cursor:grab;-webkit-user-select:none;user-select:none;will-change:transform;padding-right:16px}.c-carousel-fixed-slides:active{cursor:grabbing}.c-carousel-fixed-footer{display:flex;align-items:center;justify-content:center;gap:16px;padding:16px 0}.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.1)}.c-carousel-fixed-footer .c-carousel-fixed-nav:disabled{opacity:.5;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}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: EluxIcon, selector: "lib-elux-icon", inputs: ["color", "useSubscription", "icon", "description", "size", "height", "width", "sizeType"] }] }); }
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 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\n <div class=\"c-carousel-fixed-footer\" [hidden]=\"removeArrows\">\n <button \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 <button \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</div>\n", styles: [".c-carousel-fixed-container{position:relative;width:100%;padding-right:16px}.c-carousel-fixed-slides{display:flex;transition:transform .3s ease-out;touch-action:pan-y;cursor:grab;-webkit-user-select:none;user-select:none;will-change:transform;padding-right:16px}.c-carousel-fixed-slides:active{cursor:grabbing}.c-carousel-fixed-footer{display:flex;align-items:center;justify-content:center;gap:16px;padding:16px 0}.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.1)}.c-carousel-fixed-footer .c-carousel-fixed-nav:disabled{opacity:.5;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}\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
- }], removeArrows: [{
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 {