From 0018fe607442b1ff4c447422c7d84ce3412f74d0 Mon Sep 17 00:00:00 2001
From: Adam
Date: Fri, 13 Sep 2024 18:46:07 -0700
Subject: [PATCH 01/15] feat(carousel): Improve support for high item counts
---
.../src/components/carousel/carousel.scss | 30 ++
.../src/components/carousel/carousel.tsx | 121 ++++++--
.../src/components/carousel/resources.ts | 3 +
.../src/demos/carousel.html | 288 +++++++++++++++++-
4 files changed, 412 insertions(+), 30 deletions(-)
diff --git a/packages/calcite-components/src/components/carousel/carousel.scss b/packages/calcite-components/src/components/carousel/carousel.scss
index 0b5efba059f..ad7a4dc765e 100644
--- a/packages/calcite-components/src/components/carousel/carousel.scss
+++ b/packages/calcite-components/src/components/carousel/carousel.scss
@@ -219,6 +219,36 @@ calcite-carousel-item:not([selected]) {
--calcite-color-foreground-3: var(--calcite-internal-internal-carousel-item-background-color-selected);
color: var(--calcite-internal-internal-carousel-item-icon-color-selected);
}
+ &.pagination-item--edge {
+ opacity: 0.5;
+ &:hover {
+ opacity: 1;
+ }
+ }
+}
+
+.pagination-item--individual {
+ inline-size: 0;
+ padding: 0;
+ opacity: 0;
+ pointer-events: none;
+ visibility: hidden;
+ transition:
+ 0.15s ease-in-out inline-size,
+ 0.15s ease-in-out padding,
+ 0.25s ease-in-out opacity;
+ &.visible {
+ @apply w-8;
+ pointer-events: all;
+ visibility: visible;
+ opacity: 1;
+ }
+}
+
+.pagination-item--range-edge calcite-icon {
+ scale: 0.75;
+ opacity: 0.75;
+ transition: 0.25s ease-in-out scale;
}
.container--overlaid .pagination-item {
diff --git a/packages/calcite-components/src/components/carousel/carousel.tsx b/packages/calcite-components/src/components/carousel/carousel.tsx
index c8d397278f0..da43c900bbe 100644
--- a/packages/calcite-components/src/components/carousel/carousel.tsx
+++ b/packages/calcite-components/src/components/carousel/carousel.tsx
@@ -38,6 +38,8 @@ import {
T9nComponent,
updateMessages,
} from "../../utils/t9n";
+import { createObserver } from "../../utils/observers";
+import { breakpoints } from "../../utils/responsive";
import { getRoundRobinIndex } from "../../utils/array";
import { CSS, DURATION, ICONS } from "./resources";
import { CarouselMessages } from "./assets/carousel/t9n";
@@ -46,6 +48,15 @@ import { ArrowType, AutoplayType } from "./interfaces";
/**
* @slot - A slot for adding `calcite-carousel-item`s.
*/
+
+const maxItemBreakpoints = {
+ large: 11,
+ medium: 9,
+ small: 7,
+ xsmall: 5,
+ xxsmall: 3,
+};
+
@Component({
tag: "calcite-carousel",
styleUrl: "carousel.scss",
@@ -140,10 +151,12 @@ export class Carousel
connectedCallback(): void {
connectLocalized(this);
connectMessages(this);
+ this.resizeObserver?.observe(this.el);
}
componentDidLoad(): void {
setComponentLoaded(this);
+ this.setMaxItemsToBreakpoint(this.el.clientWidth);
}
componentDidRender(): void {
@@ -154,6 +167,7 @@ export class Carousel
disconnectLocalized(this);
disconnectMessages(this);
this.clearIntervals();
+ this.resizeObserver?.disconnect();
}
async componentWillLoad(): Promise {
@@ -263,6 +277,8 @@ export class Carousel
@State() slideDurationRemaining = 1;
+ @State() maxItems = maxItemBreakpoints.xxsmall;
+
private container: HTMLDivElement;
private containerId = `calcite-carousel-container-${guid()}`;
@@ -273,6 +289,10 @@ export class Carousel
private tabList: HTMLDivElement;
+ private resizeObserver = createObserver("resize", (entries) =>
+ entries.forEach(this.resizeHandler),
+ );
+
// --------------------------------------------------------------------------
//
// Events
@@ -300,6 +320,26 @@ export class Carousel
//
// --------------------------------------------------------------------------
+ private setMaxItemsToBreakpoint(width: number): void {
+ if (!breakpoints || !width) {
+ return;
+ }
+
+ const breakpointKeys = ["medium", "small", "xsmall", "xxsmall"];
+ for (const key of breakpointKeys) {
+ if (width >= breakpoints.width[key]) {
+ this.maxItems = maxItemBreakpoints[key];
+ return;
+ }
+ }
+
+ this.maxItems = maxItemBreakpoints.xxsmall;
+ }
+
+ private resizeHandler = ({ contentRect: { width } }: ResizeObserverEntry): void => {
+ this.setMaxItemsToBreakpoint(width);
+ };
+
private clearIntervals() {
clearInterval(this.slideDurationInterval);
clearInterval(this.slideInterval);
@@ -379,11 +419,13 @@ export class Carousel
const requestedSelectedIndex = activeItemIndex > -1 ? activeItemIndex : 0;
this.items = items;
+
this.setSelectedItem(requestedSelectedIndex, false);
};
private setSelectedItem = (requestedIndex: number, emit: boolean): void => {
const previousSelected = this.selectedIndex;
+
this.items.forEach((el, index) => {
const isMatch = requestedIndex === index;
el.selected = isMatch;
@@ -501,10 +543,12 @@ export class Carousel
}
break;
case "ArrowRight":
+ event.preventDefault();
this.direction = "forward";
this.nextItem(true);
break;
case "ArrowLeft":
+ event.preventDefault();
this.direction = "backward";
this.previousItem();
break;
@@ -609,31 +653,58 @@ export class Carousel
);
- renderPaginationItems = (): VNode => (
-
- );
+ renderPaginationItems = (): VNode => {
+ const { selectedIndex, maxItems, items, label, handleItemSelection } = this;
+ // todo handle arrow navigation for non-visible pagination items when overflowing
+ // todo handle 3 example
+ return (
+
+ );
+ };
renderArrow = (direction: "previous" | "next"): VNode => {
const isPrev = direction === "previous";
diff --git a/packages/calcite-components/src/components/carousel/resources.ts b/packages/calcite-components/src/components/carousel/resources.ts
index 088997a1a50..b939cc4fdf4 100644
--- a/packages/calcite-components/src/components/carousel/resources.ts
+++ b/packages/calcite-components/src/components/carousel/resources.ts
@@ -11,7 +11,10 @@ export const CSS = {
paginationItems: "pagination-items",
paginationItem: "pagination-item",
paginationItemIndividual: "pagination-item--individual",
+ paginationItemVisible: "pagination-item--visible",
paginationItemSelected: "pagination-item--selected",
+ paginationItemEdge: "pagination-item--edge",
+ paginationItemRangeEdge: "pagination-item--range-edge",
pageNext: "page-next",
pagePrevious: "page-previous",
autoplayControl: "autoplay-control",
diff --git a/packages/calcite-components/src/demos/carousel.html b/packages/calcite-components/src/demos/carousel.html
index 4fb500a67fc..060262e27f4 100644
--- a/packages/calcite-components/src/demos/carousel.html
+++ b/packages/calcite-components/src/demos/carousel.html
@@ -116,7 +116,13 @@ Interactive Demo
duration
-
+
play() (if autoplay is present)
@@ -156,8 +162,281 @@ Interactive Demo
An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
Max items overlap prevention
+
+
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ Another bit of content about this unbelievable item can go here on the second carousel item as
+ an example
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+
+
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ Another bit of content about this unbelievable item can go here on the second carousel item as
+ an example
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+
+
+
Themed
@@ -286,7 +565,7 @@
autoplay
Launch onboarding
-
+
@@ -386,7 +665,7 @@
autoplay
Launch onboarding
-
+
@@ -462,8 +741,7 @@
autoplay
carousel.controlOverlay = event.target.checked;
});
- durationInput.addEventListener("calciteInputInput", function () {
- console.log(event.target.value);
+ durationInput.addEventListener("calciteInputNumberInput", function () {
carousel.autoplayDuration = event.target.value;
});
From b9b6a7dc1d18a07816096a9b8f564803850de19c Mon Sep 17 00:00:00 2001
From: Adam
Date: Sat, 14 Sep 2024 10:21:53 -0700
Subject: [PATCH 02/15] WIP
---
.../src/components/carousel/carousel.e2e.ts | 74 +++++++
.../src/components/carousel/carousel.scss | 4 +-
.../components/carousel/carousel.stories.ts | 194 ++++++++++++++++++
.../src/components/carousel/carousel.tsx | 18 +-
4 files changed, 279 insertions(+), 11 deletions(-)
diff --git a/packages/calcite-components/src/components/carousel/carousel.e2e.ts b/packages/calcite-components/src/components/carousel/carousel.e2e.ts
index 64867a6d3df..8c51fd2a98b 100644
--- a/packages/calcite-components/src/components/carousel/carousel.e2e.ts
+++ b/packages/calcite-components/src/components/carousel/carousel.e2e.ts
@@ -1055,3 +1055,77 @@ describe("calcite-carousel", () => {
expect(animationEndSpy).toHaveReceivedEventTimes(8);
});
});
+describe("handles overflow of pagination items", () => {
+ it("correctly limits the number of slide pagination items shown when overflowing", async () => {
+ const page = await newE2EPage();
+ await page.setContent(
+ html`
+ first
+ second
+ third
+ fourth
+ fifth
+ sixth
+ seventh
+ eighth
+ `,
+ );
+
+ const items = await page.findAll(`calcite-carousel >>> .${CSS.paginationItemVisible}`);
+ expect(items).toHaveLength(3);
+ });
+ it("correctly limits the number of slide pagination items shown when overflowing selected middle", async () => {
+ const page = await newE2EPage();
+ await page.setContent(
+ html`
+ first
+ second
+ third
+ fourth
+ fifth
+ sixth
+ seventh
+ eighth
+ `,
+ );
+
+ const items = await page.findAll(`calcite-carousel >>> .${CSS.paginationItemVisible}`);
+ expect(items).toHaveLength(5);
+ });
+ it("correctly limits the number of slide pagination items shown when overflowing selected last", async () => {
+ const page = await newE2EPage();
+ await page.setContent(
+ html`
+ first
+ second
+ third
+ fourth
+ fifth
+ sixth
+ seventh
+ eighth
+ `,
+ );
+
+ const items = await page.findAll(`calcite-carousel >>> .${CSS.paginationItemVisible}`);
+ expect(items).toHaveLength(3);
+ });
+ it("correctly adjusts as the container or carousel width changes", async () => {
+ const page = await newE2EPage();
+ await page.setContent(
+ html`
+ first
+ second
+ third
+ fourth
+ fifth
+ sixth
+ seventh
+ eighth
+ `,
+ );
+
+ const items = await page.findAll(`calcite-carousel >>> .${CSS.paginationItemVisible}`);
+ expect(items).toHaveLength(3);
+ });
+});
diff --git a/packages/calcite-components/src/components/carousel/carousel.scss b/packages/calcite-components/src/components/carousel/carousel.scss
index ad7a4dc765e..fd80eef6d8d 100644
--- a/packages/calcite-components/src/components/carousel/carousel.scss
+++ b/packages/calcite-components/src/components/carousel/carousel.scss
@@ -236,7 +236,7 @@ calcite-carousel-item:not([selected]) {
transition:
0.15s ease-in-out inline-size,
0.15s ease-in-out padding,
- 0.25s ease-in-out opacity;
+ 0.3s ease-in-out opacity;
&.visible {
@apply w-8;
pointer-events: all;
@@ -248,7 +248,7 @@ calcite-carousel-item:not([selected]) {
.pagination-item--range-edge calcite-icon {
scale: 0.75;
opacity: 0.75;
- transition: 0.25s ease-in-out scale;
+ transition: 0.3s ease-in-out scale;
}
.container--overlaid .pagination-item {
diff --git a/packages/calcite-components/src/components/carousel/carousel.stories.ts b/packages/calcite-components/src/components/carousel/carousel.stories.ts
index a6812b7f86b..7fad5781b7c 100644
--- a/packages/calcite-components/src/components/carousel/carousel.stories.ts
+++ b/packages/calcite-components/src/components/carousel/carousel.stories.ts
@@ -76,6 +76,200 @@ export const simple = (args: CarouselStoryArgs): string =>
`;
+export const simpleManyItems = (args: CarouselStoryArgs): string =>
+ html`
+
+
+
+ Some kind of carousel item content
+ In this case, in a card
+
+
+
+
+
+ Some kind of carousel item content
+ In this case, in a card
+
+
+
+
+
+ Some kind of carousel item content
+ In this case, in a card
+
+
+
+
+
+ Some kind of carousel item content
+ In this case, in a card
+
+
+
+
+
+ Some kind of carousel item content
+ In this case, in a card
+
+
+
+
+
+ Some kind of carousel item content
+ In this case, in a card
+
+
+
+
+
+ Some kind of carousel item content
+ In this case, in a card
+
+
+
+
+
+ Some kind of carousel item content
+ In this case, in a card
+
+
+
+
+
+ Some kind of carousel item content
+ In this case, in a card
+
+
+
+
+
`;
+
+export const simpleManyItemsResizable = (args: CarouselStoryArgs): string =>
+ html`
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ Another bit of content about this unbelievable item can go here on the second carousel item as an example
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ An unbelievable new feature has arrived in this exciting product category. It's pretty neat.
+
+
+
+
+
+ `;
+
export const carouselAutoplayFullImageWithOverlayAndEdge = (): string =>
html`