@elxjs/ui 0.0.178 → 0.0.179

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.
@@ -1742,6 +1742,315 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1742
1742
  type: Input
1743
1743
  }] } });
1744
1744
 
1745
+ class EluxCarouselFixedComponent {
1746
+ constructor(renderer) {
1747
+ this.renderer = renderer;
1748
+ this.itemWidth = 200; // Fixed width in pixels
1749
+ this.gap = 16; // Gap between items in pixels
1750
+ this.removeArrows = false;
1751
+ this.previousClickEventEmitter = new EventEmitter();
1752
+ this.nextClickEventEmitter = new EventEmitter();
1753
+ this.currentIndex = 0;
1754
+ this.translateX = 0;
1755
+ this.isDragging = false;
1756
+ this.startX = 0;
1757
+ this.currentX = 0;
1758
+ this.initialTranslateX = 0;
1759
+ this.dragOffset = 0;
1760
+ this.containerWidth = 0;
1761
+ this.maxTranslateX = 0;
1762
+ this.minTranslateX = 0;
1763
+ this.velocity = 0;
1764
+ this.lastMoveTime = 0;
1765
+ this.lastMoveX = 0;
1766
+ }
1767
+ ngAfterContentInit() {
1768
+ this.calculateDimensions();
1769
+ this.setItemWidths();
1770
+ this.updateLimits();
1771
+ this.items.changes.subscribe(() => {
1772
+ this.calculateDimensions();
1773
+ this.setItemWidths();
1774
+ this.updateLimits();
1775
+ this.goToFirstItem();
1776
+ });
1777
+ window.addEventListener('resize', () => this.onResize());
1778
+ }
1779
+ ngOnDestroy() {
1780
+ window.removeEventListener('resize', () => this.onResize());
1781
+ }
1782
+ calculateDimensions() {
1783
+ if (this.slidesContainer) {
1784
+ this.containerWidth = this.slidesContainer.nativeElement.offsetWidth;
1785
+ }
1786
+ }
1787
+ updateLimits() {
1788
+ const totalWidth = this.items.length * (this.itemWidth + this.gap) - this.gap;
1789
+ this.maxTranslateX = 0;
1790
+ // Calculate minimum translateX to ensure last item is fully visible
1791
+ if (totalWidth > this.containerWidth) {
1792
+ this.minTranslateX = this.containerWidth - totalWidth;
1793
+ // Add some padding to ensure last item is completely visible
1794
+ const paddingOffset = this.gap;
1795
+ this.minTranslateX -= paddingOffset;
1796
+ }
1797
+ else {
1798
+ this.minTranslateX = 0;
1799
+ }
1800
+ }
1801
+ onResize() {
1802
+ this.calculateDimensions();
1803
+ this.updateLimits();
1804
+ this.updateTransform();
1805
+ }
1806
+ setItemWidths() {
1807
+ this.items.forEach((item) => {
1808
+ this.renderer.setStyle(item.nativeElement, 'width', `${this.itemWidth}px`);
1809
+ this.renderer.setStyle(item.nativeElement, 'flex-shrink', '0');
1810
+ this.renderer.setStyle(item.nativeElement, 'margin-right', `${this.gap}px`);
1811
+ });
1812
+ // Remove margin from last item
1813
+ if (this.items.last) {
1814
+ this.renderer.setStyle(this.items.last.nativeElement, 'margin-right', '0');
1815
+ }
1816
+ }
1817
+ // Mouse events
1818
+ onMouseDown(event) {
1819
+ event.preventDefault();
1820
+ this.startDrag(event.clientX);
1821
+ }
1822
+ onMouseMove(event) {
1823
+ if (this.isDragging) {
1824
+ event.preventDefault();
1825
+ this.updateDrag(event.clientX);
1826
+ }
1827
+ }
1828
+ onMouseUp(event) {
1829
+ if (this.isDragging) {
1830
+ this.endDrag();
1831
+ }
1832
+ }
1833
+ onMouseLeave(event) {
1834
+ if (this.isDragging) {
1835
+ this.endDrag();
1836
+ }
1837
+ }
1838
+ // Touch events
1839
+ onTouchStart(event) {
1840
+ event.preventDefault();
1841
+ this.startDrag(event.touches[0].clientX);
1842
+ }
1843
+ onTouchMove(event) {
1844
+ if (this.isDragging) {
1845
+ event.preventDefault();
1846
+ this.updateDrag(event.touches[0].clientX);
1847
+ }
1848
+ }
1849
+ onTouchEnd(event) {
1850
+ if (this.isDragging) {
1851
+ this.endDrag();
1852
+ }
1853
+ }
1854
+ startDrag(clientX) {
1855
+ this.isDragging = true;
1856
+ this.startX = clientX;
1857
+ this.currentX = clientX;
1858
+ this.initialTranslateX = this.translateX;
1859
+ this.dragOffset = 0;
1860
+ this.velocity = 0;
1861
+ this.lastMoveTime = Date.now();
1862
+ this.lastMoveX = clientX;
1863
+ this.calculateDimensions();
1864
+ this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'none');
1865
+ }
1866
+ updateDrag(clientX) {
1867
+ if (!this.isDragging)
1868
+ return;
1869
+ this.currentX = clientX;
1870
+ const deltaX = this.currentX - this.startX;
1871
+ const now = Date.now();
1872
+ const timeDelta = now - this.lastMoveTime;
1873
+ if (timeDelta > 0) {
1874
+ this.velocity = (clientX - this.lastMoveX) / timeDelta;
1875
+ }
1876
+ this.lastMoveTime = now;
1877
+ this.lastMoveX = clientX;
1878
+ let resistedDeltaX = deltaX;
1879
+ const potentialTranslateX = this.initialTranslateX + deltaX;
1880
+ // Apply softer resistance at boundaries
1881
+ if (potentialTranslateX > this.maxTranslateX) {
1882
+ const overDrag = potentialTranslateX - this.maxTranslateX;
1883
+ resistedDeltaX = deltaX - (overDrag * 0.5); // Reduced resistance for more natural feel
1884
+ }
1885
+ else if (potentialTranslateX < this.minTranslateX) {
1886
+ const overDrag = this.minTranslateX - potentialTranslateX;
1887
+ resistedDeltaX = deltaX + (overDrag * 0.5); // Reduced resistance for more natural feel
1888
+ }
1889
+ this.dragOffset = resistedDeltaX;
1890
+ this.updateDragPosition();
1891
+ }
1892
+ updateDragPosition() {
1893
+ const newTranslateX = this.initialTranslateX + this.dragOffset;
1894
+ this.renderer.setStyle(this.slidesContainer.nativeElement, 'transform', `translateX(${newTranslateX}px)`);
1895
+ }
1896
+ endDrag() {
1897
+ if (!this.isDragging)
1898
+ return;
1899
+ this.isDragging = false;
1900
+ this.renderer.setStyle(this.slidesContainer.nativeElement, 'transition', 'transform 0.3s ease-out');
1901
+ // Calculate how far we've actually moved in terms of items
1902
+ const currentTranslateX = this.initialTranslateX + this.dragOffset;
1903
+ const itemStep = this.itemWidth + this.gap;
1904
+ const movedItemsFloat = Math.abs(currentTranslateX - this.initialTranslateX) / itemStep;
1905
+ // More sensitive thresholds
1906
+ const minThreshold = 0.3; // 30% of an item width
1907
+ const dragDistance = Math.abs(this.currentX - this.startX);
1908
+ const momentumThreshold = 0.3;
1909
+ const hasSignificantVelocity = Math.abs(this.velocity) > momentumThreshold;
1910
+ // If we've moved more than 30% of an item width OR have significant velocity
1911
+ if (movedItemsFloat > minThreshold || hasSignificantVelocity || dragDistance > 60) {
1912
+ // Calculate how many items to advance based on actual drag distance
1913
+ let itemsToAdvance = Math.round(movedItemsFloat);
1914
+ // For velocity-based movement, use a separate calculation
1915
+ if (hasSignificantVelocity && movedItemsFloat < 1) {
1916
+ const velocityMultiplier = Math.abs(this.velocity) * 2;
1917
+ itemsToAdvance = Math.max(1, Math.floor(velocityMultiplier));
1918
+ }
1919
+ // Ensure minimum 1 item for significant drags, but don't double count
1920
+ if (itemsToAdvance === 0 && movedItemsFloat >= minThreshold) {
1921
+ itemsToAdvance = 1;
1922
+ }
1923
+ if (this.dragOffset > 0 || (hasSignificantVelocity && this.velocity > 0)) {
1924
+ // Dragged right - go to previous items
1925
+ this.goToPreviousItems(itemsToAdvance);
1926
+ }
1927
+ else if (this.dragOffset < 0 || (hasSignificantVelocity && this.velocity < 0)) {
1928
+ // Dragged left - go to next items
1929
+ this.goToNextItems(itemsToAdvance);
1930
+ }
1931
+ else {
1932
+ this.snapToNearestItem();
1933
+ }
1934
+ }
1935
+ else {
1936
+ // Small movement - snap to nearest item
1937
+ this.snapToNearestItem();
1938
+ }
1939
+ this.dragOffset = 0;
1940
+ this.velocity = 0;
1941
+ }
1942
+ snapToNearestItem() {
1943
+ const currentTranslateX = this.initialTranslateX + this.dragOffset;
1944
+ const itemStep = this.itemWidth + this.gap;
1945
+ // Find the closest valid position
1946
+ let targetIndex = Math.round(-currentTranslateX / itemStep);
1947
+ // Clamp to valid bounds
1948
+ const maxIndex = this.getMaxIndex();
1949
+ targetIndex = Math.max(0, Math.min(targetIndex, maxIndex));
1950
+ this.currentIndex = targetIndex;
1951
+ this.updateTransform();
1952
+ }
1953
+ goToPreviousItems(itemCount) {
1954
+ // Use the current index instead of visual calculation to avoid double movement
1955
+ const newIndex = Math.max(0, this.currentIndex - itemCount);
1956
+ if (newIndex !== this.currentIndex) {
1957
+ this.currentIndex = newIndex;
1958
+ this.updateTransform();
1959
+ this.previousClickEventEmitter.emit();
1960
+ }
1961
+ else {
1962
+ this.snapToNearestItem();
1963
+ }
1964
+ }
1965
+ goToNextItems(itemCount) {
1966
+ // Use the current index instead of visual calculation to avoid double movement
1967
+ const maxIndex = this.getMaxIndex();
1968
+ const newIndex = Math.min(maxIndex, this.currentIndex + itemCount);
1969
+ if (newIndex !== this.currentIndex) {
1970
+ this.currentIndex = newIndex;
1971
+ this.updateTransform();
1972
+ this.nextClickEventEmitter.emit();
1973
+ }
1974
+ else {
1975
+ this.snapToNearestItem();
1976
+ }
1977
+ }
1978
+ canGoPrevious() {
1979
+ return this.currentIndex > 0;
1980
+ }
1981
+ canGoNext() {
1982
+ return this.currentIndex < this.getMaxIndex();
1983
+ }
1984
+ goToFirstItem() {
1985
+ this.currentIndex = 0;
1986
+ this.updateTransform();
1987
+ }
1988
+ prevItem() {
1989
+ if (this.canGoPrevious()) {
1990
+ this.currentIndex--;
1991
+ this.updateTransform();
1992
+ this.previousClickEventEmitter.emit();
1993
+ }
1994
+ }
1995
+ nextItem() {
1996
+ if (this.canGoNext()) {
1997
+ this.currentIndex++;
1998
+ this.updateTransform();
1999
+ this.nextClickEventEmitter.emit();
2000
+ }
2001
+ }
2002
+ updateTransform() {
2003
+ const itemStep = this.itemWidth + this.gap;
2004
+ this.translateX = -this.currentIndex * itemStep;
2005
+ // Apply strict boundaries to ensure last item is always fully visible
2006
+ this.translateX = Math.max(this.minTranslateX, Math.min(this.maxTranslateX, this.translateX));
2007
+ // Recalculate currentIndex based on actual translateX to stay in sync
2008
+ if (this.translateX <= this.minTranslateX) {
2009
+ this.currentIndex = this.getMaxIndex();
2010
+ }
2011
+ else {
2012
+ this.currentIndex = Math.max(0, Math.min(this.currentIndex, this.getMaxIndex()));
2013
+ }
2014
+ if (!this.isDragging && this.slidesContainer) {
2015
+ this.renderer.setStyle(this.slidesContainer.nativeElement, 'transform', `translateX(${this.translateX}px)`);
2016
+ }
2017
+ }
2018
+ getMaxIndex() {
2019
+ const visibleWidth = this.containerWidth;
2020
+ const totalItemsWidth = this.items.length * (this.itemWidth + this.gap) - this.gap;
2021
+ if (totalItemsWidth <= visibleWidth) {
2022
+ return 0;
2023
+ }
2024
+ // Calculate how many complete items can fit in the visible area
2025
+ const itemsPerView = Math.floor(visibleWidth / (this.itemWidth + this.gap));
2026
+ // Maximum index ensures the last item is always fully visible
2027
+ const maxIndex = Math.max(0, this.items.length - itemsPerView);
2028
+ return maxIndex;
2029
+ }
2030
+ 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 }); }
2031
+ 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" }, 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"] }] }); }
2032
+ }
2033
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EluxCarouselFixedComponent, decorators: [{
2034
+ type: Component,
2035
+ 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"] }]
2036
+ }], ctorParameters: () => [{ type: i0.Renderer2 }], propDecorators: { items: [{
2037
+ type: ContentChildren,
2038
+ args: ['carouselItem', { read: ElementRef }]
2039
+ }], slidesContainer: [{
2040
+ type: ViewChild,
2041
+ args: ['slidesContainer']
2042
+ }], itemWidth: [{
2043
+ type: Input
2044
+ }], gap: [{
2045
+ type: Input
2046
+ }], removeArrows: [{
2047
+ type: Input
2048
+ }], previousClickEventEmitter: [{
2049
+ type: Output
2050
+ }], nextClickEventEmitter: [{
2051
+ type: Output
2052
+ }] } });
2053
+
1745
2054
  class EluxArrowLink {
1746
2055
  constructor() {
1747
2056
  this.title = '';
@@ -1803,5 +2112,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1803
2112
  * Generated bundle index. Do not edit.
1804
2113
  */
1805
2114
 
1806
- export { ElectroluxComponents, ElectroluxDesignSystemModule, EluxAccordion, EluxArrowLink, EluxBreadcrumb, EluxButton, EluxCard1, EluxCard2, EluxCard3, EluxCard4, EluxCarouselComponent, EluxChip, EluxDataTable, EluxDatepicker, EluxDropdown, EluxDropdownMultiple, EluxIcon, EluxInputSearchComponent, EluxInputSearchModalComponent, EluxLoading, EluxLoading2, EluxModal, EluxPaginator, EluxStepBar, EluxSwitch, EluxToast, ToasterService };
2115
+ export { ElectroluxComponents, ElectroluxDesignSystemModule, EluxAccordion, EluxArrowLink, EluxBreadcrumb, EluxButton, EluxCard1, EluxCard2, EluxCard3, EluxCard4, EluxCarouselComponent, EluxCarouselFixedComponent, EluxChip, EluxDataTable, EluxDatepicker, EluxDropdown, EluxDropdownMultiple, EluxIcon, EluxInputSearchComponent, EluxInputSearchModalComponent, EluxLoading, EluxLoading2, EluxModal, EluxPaginator, EluxStepBar, EluxSwitch, EluxToast, ToasterService };
1807
2116
  //# sourceMappingURL=elxjs-ui.mjs.map