Skip to content

Commit

Permalink
feat: more flexible carousels and fix homepage alignment
Browse files Browse the repository at this point in the history
Currently the carousels on the front page don't align with any other elements on the page which looks odd. This seems to be due to a limitation where the item size has to be passed in to the carousel.

These changes reimplement the carousel, making it more flexible and allowing it to handle any size of items and any spacing. The new carousel is used on the frontpage to lay out the items in a way that aligns with the rest of the content.
  • Loading branch information
crisbeto authored and Splaktar committed Jun 14, 2021
1 parent 71133d7 commit 84b3139
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 218 deletions.
16 changes: 0 additions & 16 deletions material.angular.io/src/app/pages/homepage/_homepage-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,5 @@
}
outline: none;
}

.docs-featured-components-carousel-item:hover {
img {
transform: scale(1.2);
}

box-shadow:inset 0 0 0 1.5px mat.get-color-from-palette($foreground, divider);
}

.docs-featured-components-carousel-item:focus {
img {
transform: scale(1.2);
}

box-shadow:inset 0 0 0 1.5px mat.get-color-from-palette($foreground, disabled);
}
}
}
36 changes: 20 additions & 16 deletions material.angular.io/src/app/pages/homepage/homepage.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,38 @@ <h2>Frictionless</h2>
<mat-divider></mat-divider>

<div class="docs-homepage-featured-components docs-homepage-carousel-row">
<h2>Featured components</h2>
<app-carousel [itemWidth]="260" [aria-label]="'Featured components'">
<a *ngFor="let comp of getTopComponents()" carousel-item class='docs-featured-components-carousel-item'
<div class="docs-homepage-header">
<h2>Featured components</h2>

<a class="docs-link" routerLink="/components">
View all components
<mat-icon>chevron_right</mat-icon>
</a>
</div>
<app-carousel [aria-label]="'Featured components'">
<a *ngFor="let comp of getTopComponents()" carousel-item class="carousel-item docs-featured-components-carousel-item"
routerLink="/components/{{comp}}">
<div class="docs-homepage-img-container">
<img alt="" src="../../../assets/screenshots/{{comp}}.scene.png" role="presentation">
</div>
{{(comp[0].toUpperCase() + comp.slice(1)).replace('-', ' ')}}
</a>
</app-carousel>

<a class="docs-link" routerLink="/components">
View all components
<mat-icon>chevron_right</mat-icon>
</a>
</div>

<mat-divider></mat-divider>

<div class="docs-homepage-guides docs-homepage-carousel-row">
<h2>Guides</h2>
<app-carousel [itemWidth]="260" [aria-label]="'Guides'">
<a carousel-item class='docs-homepage-guides-carousel-item' *ngFor="let guide of guideItems.getAllItems()"
<div class="docs-homepage-header">
<h2>Guides</h2>

<a class="docs-link" routerLink="/guides">
View all guides
<mat-icon>chevron_right</mat-icon>
</a>
</div>
<app-carousel [aria-label]="'Guides'">
<a carousel-item class="carousel-item docs-homepage-guides-carousel-item" *ngFor="let guide of guideItems.getAllItems()"
routerLink='/guide/{{guide.id}}'>
<mat-card class="docs-homepage-guides-card">
<mat-card-title>{{guide.name}}</mat-card-title>
Expand All @@ -76,11 +85,6 @@ <h2>Guides</h2>
</mat-card>
</a>
</app-carousel>

<a class="docs-link" routerLink="/guides">
View all guides
<mat-icon>chevron_right</mat-icon>
</a>
</div>

</main>
Expand Down
80 changes: 63 additions & 17 deletions material.angular.io/src/app/pages/homepage/homepage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ $margin-promotion-sections-small: 15px;
display: flex;
flex-direction: column;
padding: 16px;
width: 75%;
max-width: 1080px;
margin: auto;

a {
text-decoration: none;
Expand All @@ -61,7 +64,7 @@ $margin-promotion-sections-small: 15px;
h2 {
font-size: 25px;
font-weight: 400;
margin: 16px 0;
margin: 0 0 16px;
padding: 0;
}

Expand All @@ -74,37 +77,60 @@ $margin-promotion-sections-small: 15px;
}

mat-divider {
width: 75%;
width: 100%;
}

mat-icon {
vertical-align: middle;
}
}

.carousel-item {
width: 48%;

& + & {
margin-left: 2%;
}

@media (min-width: 1020px) {
max-width: 32%;
}
}

.docs-homepage-row {
width: 75%;
display: flex;
flex-direction: row;
margin: $margin-promotion-sections 0;
}

.docs-homepage-carousel-row {
margin: $margin-promotion-sections 0;
width: 75%;
display: flex;
flex-direction: column;
width: 100%;

a.docs-link {
width: 100%;
text-align: right;
h2 {
margin-top: 0;
}
}

.docs-homepage-header {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;

h2 {
margin-top: 0;
margin: 0;
}
}

.docs-link {
display: inline-flex;
align-items: center;
}

.docs-homepage-guides {
.docs-homepage-guides-card-divider {
width: 30%;
Expand All @@ -113,9 +139,18 @@ $margin-promotion-sections-small: 15px;
}

.docs-homepage-guides-carousel-item {
padding: 15px;
display: flex;
text-decoration: none;
box-sizing: border-box;

// We need a slight padding so the box shadow doesn't get cut off by the carousel.
padding: 3px;

.mat-card {
// Ensures that the cards stretch to the full width of the item.
width: 100%;
min-height: 140px;
}
}

.docs-homepage-guides-card.mat-card {
Expand All @@ -131,31 +166,39 @@ $margin-promotion-sections-small: 15px;
}
}


.docs-homepage-featured-components {
.docs-featured-components-carousel-item {
padding: 15px;
text-align: center;
box-sizing: border-box;
font-size: 18px;

.docs-homepage-img-container {
overflow: hidden;
width: 259px;
height: 144px;
margin-bottom: 10px;
margin-bottom: 16px;
max-height: 200px;
}

img {
transition: 0.3s ease-in-out;
width: 100%;
}

&:hover, &:focus {
img {
transform: scale(1.1);
}
}
}
}

.docs-homepage-row-column {
display: flex;
flex-direction: column;
margin: 0 auto;
width: 30%;
width: 32%;

& + & {
margin-left: 2%;
}
}

.docs-header-start {
Expand Down Expand Up @@ -187,14 +230,17 @@ $margin-promotion-sections-small: 15px;

.docs-homepage-row-column {
width: 100%;

& + & {
margin-left: 0;
}
}
}

/**
* Rules for when the device is detected to be a small screen.
*/
@media (max-width: 720px) {

.docs-header-start {
margin: $margin-promotion-sections-small 0 0 0;
}
Expand Down
22 changes: 12 additions & 10 deletions material.angular.io/src/app/shared/carousel/carousel.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<button (click)="previous()" *ngIf="this.showPrevArrow" aria-hidden="true" tabindex="-1"
class="docs-carousel-nav docs-carousel-nav-prev" mat-mini-fab aria-label="previous">
<mat-icon>navigate_before</mat-icon>
</button>

<div #contentWrapper (keyup)="onKeydown($event)"
(window:resize)="onResize()" [style.minWidth]="shiftWidth + 'px'" class="docs-carousel-content-wrapper" role="region">
<button (click)="previous()" *ngIf="this.showPrevArrow" aria-hidden="true" tabindex="-1"
class="docs-carousel-nav docs-carousel-nav-prev" mat-mini-fab aria-label="previous">
<mat-icon>navigate_before</mat-icon>
</button>
<div class= "docs-carousel-content" role="list" tabindex="0">
class="docs-carousel-content-wrapper" role="region">
<div class="docs-carousel-content" role="list" tabindex="0" #list>
<ng-content></ng-content>
</div>
<button (click)="next()" *ngIf="this.showNextArrow" aria-hidden="true" tabindex="-1"
class="docs-carousel-nav docs-carousel-nav-next" mat-mini-fab aria-label="next">
<mat-icon>navigate_next</mat-icon>
</button>
</div>

<button (click)="next()" *ngIf="this.showNextArrow" aria-hidden="true" tabindex="-1"
class="docs-carousel-nav docs-carousel-nav-next" mat-mini-fab aria-label="next">
<mat-icon>navigate_next</mat-icon>
</button>
15 changes: 6 additions & 9 deletions material.angular.io/src/app/shared/carousel/carousel.scss
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
app-carousel {
display: flex;
align-items: center;
justify-content: center;
margin: 0 40px;
display: block;
position: relative;
}

.docs-carousel-content {
display: flex;
flex-direction: row;
overflow: hidden;
outline: none;
transition: transform 0.5s ease-in-out;
}

.docs-carousel-content-wrapper {
position: relative;
overflow: hidden;
}

[carousel-item] {
flex-shrink: 0;
transition: transform 0.5s ease-in-out;
}

.docs-carousel-nav {
Expand All @@ -28,9 +25,9 @@ app-carousel {
}

.docs-carousel-nav-prev {
left: -40px;
left: -50px;
}

.docs-carousel-nav-next {
right: -40px;
right: -50px;
}
43 changes: 7 additions & 36 deletions material.angular.io/src/app/shared/carousel/carousel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,63 +39,34 @@ describe('HorizontalCarousel', () => {
component.next();
fixture.detectChanges();

expect(component.index).toEqual(1);

const navPrevious = fixture.nativeElement.querySelector('.docs-carousel-nav-prev');
expect(navPrevious).toBeDefined();
});

it('should hide next nav arrow after reaching end of items', () => {
expect(component.visibleItems).toBe(4);

component.next();
component.next();
expect(component.index).toEqual(2);
fixture.detectChanges();

const navPrevious = fixture.nativeElement.querySelector('.docs-carousel-nav-next');
expect(navPrevious).toBeNull();

// in case of keyboard nav at end of items
component.next();
expect(component.index).toEqual(2);
});

it('should resize carousel when not all content can be displayed', () => {
const carouselWrapper = fixture.nativeElement.querySelector('.docs-carousel-content-wrapper');
fixture.nativeElement.style.width = '1350px';
window.dispatchEvent(new Event('resize'));

fixture.detectChanges();

expect(carouselWrapper.clientWidth).toEqual(1250);
expect(component.visibleItems).toEqual(5);
});

it('should not resize carousel when all content can be displayed', () => {
fixture.componentInstance.numberOfItems = 2;
fixture.detectChanges();

const carouselWrapper = fixture.nativeElement.querySelector('.docs-carousel-content-wrapper');
fixture.nativeElement.style.width = '1350px';
window.dispatchEvent(new Event('resize'));

fixture.detectChanges();

expect(carouselWrapper.clientWidth).toEqual(500);
expect(component.visibleItems).toEqual(2);
});
});

@Component({
selector: 'test-carousel',
template:
`
<app-carousel itemWidth="250">
<app-carousel>
<div carousel-item class="docs-carousel-item-container"
*ngFor="let i of [].constructor(numberOfItems) "></div>
</app-carousel>`,
styles: ['.docs-carousel-item-container { display: flex; }']
styles: [`
.docs-carousel-item-container {
display: flex;
width: 250px;
}
`]
})
class CarouselTestComponent {
numberOfItems = 6;
Expand Down
Loading

0 comments on commit 84b3139

Please sign in to comment.