diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d22d40ebe8..987e4040046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,8 @@ All notable changes for each version of this project will be documented in this - `indicatorsOrientation` input is added, which can be used to set the position of indicators it can be top or bottom - `animationType` input is added, which can be used to set animation when changing slides - `indicatorTemplate` directive is added, which can be used to provide a custom indicator for carousel. If this property is not provided, a default indicator template will be used instead. + - `nextButtonTemplate` directive is added, which is used to provide a custom next button template. If not provided, a default next button is used. + - `prevButtonTemplate` directive is added, which is used to provide a custom previous button template. If not provided, a default previous button is used. ## 8.2.6 diff --git a/projects/igniteui-angular/src/lib/carousel/README.md b/projects/igniteui-angular/src/lib/carousel/README.md index 1121334bfe1..e8770a77810 100644 --- a/projects/igniteui-angular/src/lib/carousel/README.md +++ b/projects/igniteui-angular/src/lib/carousel/README.md @@ -42,16 +42,40 @@ Keyboard navigation will be enabled when the **IgxCarousel** component is focuse - `End` will focus the last slide inside the carousel view. ### Templates -The **IgxCarousel** supports templating of its indicators +The **IgxCarousel** supports templating indicators and navigation buttons #### Defining item template: ```html ... - - brightness_7 - brightness_5 - + + brightness_7 + brightness_5 + + +``` + +#### Defining next button template: +```html + + ... + + + + +``` + +#### Defining previous button template: +```html + + ... + + + ``` diff --git a/projects/igniteui-angular/src/lib/carousel/carousel.component.html b/projects/igniteui-angular/src/lib/carousel/carousel.component.html index 4a42a238f33..906322b8429 100644 --- a/projects/igniteui-angular/src/lib/carousel/carousel.component.html +++ b/projects/igniteui-angular/src/lib/carousel/carousel.component.html @@ -5,6 +5,22 @@ + + + arrow_forward + + + + + + arrow_back + + +
- - - arrow_back - - - arrow_forward - - + + + + + diff --git a/projects/igniteui-angular/src/lib/carousel/carousel.component.spec.ts b/projects/igniteui-angular/src/lib/carousel/carousel.component.spec.ts index 85d30855bba..e59ba0809b6 100644 --- a/projects/igniteui-angular/src/lib/carousel/carousel.component.spec.ts +++ b/projects/igniteui-angular/src/lib/carousel/carousel.component.spec.ts @@ -515,7 +515,7 @@ describe('Carousel', () => { }); describe('Templates Tests: ', () => { - it('verify that template can be defined in the markup', () => { + it('verify that templates can be defined in the markup', () => { fixture = TestBed.createComponent(CarouselTemplateSetInMarkupTestComponent); carousel = fixture.componentInstance.carousel; fixture.detectChanges(); @@ -526,9 +526,15 @@ describe('Carousel', () => { const indicator = HelperTestFunctions.getIndicators(fixture)[index] as HTMLElement; expect(indicator.innerText).toEqual(index.toString()); } + + expect(HelperTestFunctions.getNextButtonArrow(fixture)).toBeNull(); + expect(HelperTestFunctions.getPreviousButtonArrow(fixture)).toBeNull(); + + expect(HelperTestFunctions.getNextButton(fixture).innerText).toEqual('next'); + expect(HelperTestFunctions.getPreviousButton(fixture).innerText).toEqual('prev'); }); - it('verify that template can be changed', () => { + it('verify that templates can be changed', () => { fixture = TestBed.createComponent(CarouselTemplateSetInTypescriptTestComponent); carousel = fixture.componentInstance.carousel; fixture.detectChanges(); @@ -538,8 +544,12 @@ describe('Carousel', () => { expect(HelperTestFunctions.getIndicators(fixture).length).toBe(4); expect(HelperTestFunctions.getIndicatorsDots(fixture).length).toBe(4); + expect(HelperTestFunctions.getNextButtonArrow(fixture)).toBeDefined(); + expect(HelperTestFunctions.getPreviousButtonArrow(fixture)).toBeDefined(); carousel.indicatorTemplate = fixture.componentInstance.customIndicatorTemplate1; + carousel.nextButtonTemplate = fixture.componentInstance.customNextTemplate; + carousel.prevButtonTemplate = fixture.componentInstance.customPrevTemplate; fixture.detectChanges(); expect(HelperTestFunctions.getIndicators(fixture).length).toBe(4); @@ -548,6 +558,11 @@ describe('Carousel', () => { const indicator = HelperTestFunctions.getIndicators(fixture)[index] as HTMLElement; expect(indicator.innerText).toEqual(index.toString()); } + expect(HelperTestFunctions.getNextButtonArrow(fixture)).toBeNull(); + expect(HelperTestFunctions.getPreviousButtonArrow(fixture)).toBeNull(); + + expect(HelperTestFunctions.getNextButton(fixture).innerText).toEqual('next'); + expect(HelperTestFunctions.getPreviousButton(fixture).innerText).toEqual('prev'); carousel.indicatorTemplate = fixture.componentInstance.customIndicatorTemplate2; fixture.detectChanges(); @@ -565,10 +580,14 @@ describe('Carousel', () => { } carousel.indicatorTemplate = null; + carousel.nextButtonTemplate = null; + carousel.prevButtonTemplate = null; fixture.detectChanges(); expect(HelperTestFunctions.getIndicators(fixture).length).toBe(4); expect(HelperTestFunctions.getIndicatorsDots(fixture).length).toBe(4); + expect(HelperTestFunctions.getNextButtonArrow(fixture)).toBeDefined(); + expect(HelperTestFunctions.getPreviousButtonArrow(fixture)).toBeDefined(); }); }); @@ -704,8 +723,8 @@ describe('Carousel', () => { expect(carousel.total).toEqual(0); expect(HelperTestFunctions.getIndicatorsContainer(fixture)).toBeNull(); expect(HelperTestFunctions.getIndicatorsContainer(fixture, CarouselIndicatorsOrientation.top)).toBeNull(); - expect(HelperTestFunctions.getNextButton(fixture).hidden).toBeTruthy(); - expect(HelperTestFunctions.getPreviousButton(fixture).hidden).toBeTruthy(); + expect(HelperTestFunctions.getNextButton(fixture)).toBeNull(); + expect(HelperTestFunctions.getPreviousButton(fixture)).toBeNull(); // add a slide fixture.componentInstance.addSlides(); @@ -724,6 +743,7 @@ describe('Carousel', () => { class HelperTestFunctions { public static NEXT_BUTTON_CLASS = '.igx-carousel__arrow--next'; public static PRIV_BUTTON_CLASS = '.igx-carousel__arrow--prev'; + public static BUTTON_ARROW_CLASS = '.igx-nav-arrow'; public static ACTIVE_SLIDE_CLASS = 'igx-slide--current'; public static PREVIOUS_SLIDE_CLASS = 'igx-slide--previous'; public static INDICATORS_TOP_CLASS = '.igx-carousel-indicators--top'; @@ -741,6 +761,16 @@ class HelperTestFunctions { return fixture.nativeElement.querySelector(HelperTestFunctions.PRIV_BUTTON_CLASS); } + public static getNextButtonArrow(fixture): HTMLElement { + const next = HelperTestFunctions.getNextButton(fixture); + return next.querySelector(HelperTestFunctions.BUTTON_ARROW_CLASS); + } + + public static getPreviousButtonArrow(fixture): HTMLElement { + const prev = HelperTestFunctions.getPreviousButton(fixture); + return prev.querySelector(HelperTestFunctions.BUTTON_ARROW_CLASS); + } + public static getIndicatorsContainer(fixture, position = CarouselIndicatorsOrientation.bottom): HTMLElement { const carouselNative = fixture.nativeElement; if (position === CarouselIndicatorsOrientation.bottom) { @@ -819,6 +849,14 @@ class CarouselAnimationsComponent { {{slide.index}} + + + next + + + + prev + ` }) @@ -837,6 +875,14 @@ class CarouselTemplateSetInMarkupTestComponent { {{slide.index}}: Active + + next + + + + prev + +

Slide1

Slide2

@@ -851,6 +897,10 @@ class CarouselTemplateSetInTypescriptTestComponent { public customIndicatorTemplate1; @ViewChild('customIndicatorTemplate2', { read: TemplateRef, static: false }) public customIndicatorTemplate2; + @ViewChild('customNextTemplate', { read: TemplateRef, static: false }) + public customNextTemplate; + @ViewChild('customPrevTemplate', { read: TemplateRef, static: false }) + public customPrevTemplate; } @Component({ diff --git a/projects/igniteui-angular/src/lib/carousel/carousel.component.ts b/projects/igniteui-angular/src/lib/carousel/carousel.component.ts index 7354c543edd..bd41cef0a96 100644 --- a/projects/igniteui-angular/src/lib/carousel/carousel.component.ts +++ b/projects/igniteui-angular/src/lib/carousel/carousel.component.ts @@ -23,7 +23,7 @@ import { IgxIconModule } from '../icon/index'; import { IBaseEventArgs } from '../core/utils'; import { Subject, merge } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { IgxCarouselIndicatorDirective } from './carousel.directives'; +import { IgxCarouselIndicatorDirective, IgxCarouselNextButtonDirective, IgxCarouselPrevButtonDirective } from './carousel.directives'; import { useAnimation, AnimationBuilder, AnimationPlayer, AnimationReferenceMetadata } from '@angular/animations'; import { slideInLeft, fadeIn, rotateInCenter } from '../animations/main'; import { IgxSlideComponent, Direction } from './slide.component'; @@ -40,8 +40,7 @@ export enum CarouselIndicatorsOrientation { export enum CarouselAnimationType { none = 'none', slide = 'slide', - fade = 'fade', - grow = 'grow' + fade = 'fade' } export interface CarouselAnimationSettings { @@ -248,7 +247,13 @@ export class IgxCarouselComponent implements OnDestroy, AfterContentInit { } @ViewChild('defaultIndicator', { read: TemplateRef, static: true }) - protected defaultIndicator: TemplateRef; + private defaultIndicator: TemplateRef; + + @ViewChild('defaultNextButton', { read: TemplateRef, static: true }) + private defaultNextButton: TemplateRef; + + @ViewChild('defaultPrevButton', { read: TemplateRef, static: true }) + private defaultPrevButton: TemplateRef; /** * The custom template, if any, that should be used when rendering carousel indicators @@ -272,6 +277,52 @@ export class IgxCarouselComponent implements OnDestroy, AfterContentInit { @ContentChild(IgxCarouselIndicatorDirective, { read: TemplateRef, static: false }) public indicatorTemplate: TemplateRef = null; + /** + * The custom template, if any, that should be used when rendering carousel next button + * + * ```typescript + * // Set in typescript + * const myCustomTemplate: TemplateRef = myComponent.customTemplate; + * myComponent.carousel.nextButtonTemplate = myCustomTemplate; + * ``` + * ```html + * + * + * ... + * + * + * + * + * ``` + */ + @ContentChild(IgxCarouselNextButtonDirective, { read: TemplateRef, static: false }) + public nextButtonTemplate: TemplateRef = null; + + /** + * The custom template, if any, that should be used when rendering carousel previous button + * + * ```typescript + * // Set in typescript + * const myCustomTemplate: TemplateRef = myComponent.customTemplate; + * myComponent.carousel.nextButtonTemplate = myCustomTemplate; + * ``` + * ```html + * + * + * ... + * + * + * + * + * ``` + */ + @ContentChild(IgxCarouselPrevButtonDirective, { read: TemplateRef, static: false }) + public prevButtonTemplate: TemplateRef = null; + /** * The collection of `slides` currently in the carousel. * ```typescript @@ -545,6 +596,26 @@ export class IgxCarouselComponent implements OnDestroy, AfterContentInit { return this.defaultIndicator; } + /** + * @hidden + */ + public get getNextButtonTemplate(): TemplateRef { + if (this.nextButtonTemplate) { + return this.nextButtonTemplate; + } + return this.defaultNextButton; + } + + /** + * @hidden + */ + public get getPrevButtonTemplate(): TemplateRef { + if (this.prevButtonTemplate) { + return this.prevButtonTemplate; + } + return this.defaultPrevButton; + } + /** * @hidden * @memberof IgxCarouselComponent @@ -785,6 +856,22 @@ export class IgxCarouselComponent implements OnDestroy, AfterContentInit { }, this.interval); } } + + /** + *@hidden + */ + public get nextButtonDisabled() { + return !this.loop && this.current === (this.total - 1); + } + + /** + *@hidden + */ + public get prevButtonDisabled() { + return !this.loop && this.current === 0; + } + + /** *@hidden */ @@ -894,8 +981,20 @@ export interface ISlideEventArgs extends IBaseEventArgs { * @hidden */ @NgModule({ - declarations: [IgxCarouselComponent, IgxSlideComponent, IgxCarouselIndicatorDirective], - exports: [IgxCarouselComponent, IgxSlideComponent, IgxCarouselIndicatorDirective], + declarations: [ + IgxCarouselComponent, + IgxSlideComponent, + IgxCarouselIndicatorDirective, + IgxCarouselNextButtonDirective, + IgxCarouselPrevButtonDirective + ], + exports: [ + IgxCarouselComponent, + IgxSlideComponent, + IgxCarouselIndicatorDirective, + IgxCarouselNextButtonDirective, + IgxCarouselPrevButtonDirective + ], imports: [CommonModule, IgxIconModule] }) export class IgxCarouselModule { diff --git a/projects/igniteui-angular/src/lib/carousel/carousel.directives.ts b/projects/igniteui-angular/src/lib/carousel/carousel.directives.ts index db5c492da1d..ea26c4a5e93 100644 --- a/projects/igniteui-angular/src/lib/carousel/carousel.directives.ts +++ b/projects/igniteui-angular/src/lib/carousel/carousel.directives.ts @@ -5,3 +5,15 @@ import { Directive, TemplateRef } from '@angular/core'; }) export class IgxCarouselIndicatorDirective { } + +@Directive({ + selector: '[igxCarouselNextButton]' +}) +export class IgxCarouselNextButtonDirective { +} + +@Directive({ + selector: '[igxCarouselPrevButton]' +}) +export class IgxCarouselPrevButtonDirective { +} diff --git a/projects/igniteui-angular/src/lib/core/styles/components/carousel/_carousel-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/carousel/_carousel-component.scss index 16acc4c6a70..854f4d618aa 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/carousel/_carousel-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/carousel/_carousel-component.scss @@ -28,7 +28,7 @@ } /// @access private -@mixin _igx-carousel-naviation-partial { +@mixin _igx-carousel-navigation-partial { @include b(igx-nav-dot) { @extend %igx-nav-dot !optional; @@ -36,6 +36,18 @@ @extend %igx-nav-dot--active !optional; } } + + @include b(igx-nav-arrow) { + @extend %igx-nav-arrow !optional; + + @include m(disabled) { + @extend %igx-nav-arrow--disabled !optional; + } + + &:hover { + @extend %igx-nav-arrow--hover !optional; + } + } } /// @access private @@ -75,23 +87,15 @@ @include e(arrow, $m: prev) { @extend %igx-carousel-arrow !optional; @extend %igx-carousel-arrow--prev !optional; - - &:hover { - @extend %igx-carousel-arrow--hover !optional; - } } @include e(arrow, $m: next) { @extend %igx-carousel-arrow !optional; @extend %igx-carousel-arrow--next !optional; - - &:hover { - @extend %igx-carousel-arrow--hover !optional; - } } @include _igx-carousel-indicators-partial(); - @include _igx-carousel-naviation-partial(); + @include _igx-carousel-navigation-partial(); @include _igx-carousel-slide-partial(); } diff --git a/projects/igniteui-angular/src/lib/core/styles/components/carousel/_carousel-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/carousel/_carousel-theme.scss index 88bdf1ffe95..9c3ed411d72 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/carousel/_carousel-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/carousel/_carousel-theme.scss @@ -10,8 +10,10 @@ /// @param {Color} $slide-background [null] - The slide background color. /// @param {Color} $button-background [null] - The previous/next buttons idle background color. /// @param {Color} $button-hover-background [null] - The previous/next buttons hover background color. +/// @param {Color} $button-disabled-background [null] - The previous/next buttons disabled background color. /// @param {Color} $button-arrow-color [null] - The previous/next buttons idle arrow color. /// @param {Color} $button-hover-arrow-color [null] - The previous/next buttons hover arrow color. +/// @param {Color} $button-disabled-arrow-color [null] - The previous/next buttons disabled arrow color. /// @param {Color} $indicator-dot-color [null] - The active indicator dot color. /// @param {Color} $indicator-border-color [null] - The idle indicator border color. /// @param {Color} $indicator-active-border-color [null] - The active indicator border color. @@ -47,8 +49,10 @@ $button-background: null, $button-hover-background: null, + $button-disabled-background: null, $button-arrow-color: null, $button-hover-arrow-color: null, + $button-disabled-arrow-color: null, $indicator-dot-color: null, $indicator-border-color: null, @@ -82,6 +86,10 @@ $button-hover-arrow-color: text-contrast($button-hover-background); } + @if not($button-disabled-arrow-color) and $button-disabled-background { + $button-disabled-arrow-color: rgba(text-contrast($button-disabled-background), .3); + } + @return extend($theme, ( name: $name, palette: $palette, @@ -92,9 +100,11 @@ button-shadow: $button-shadow, button-background: $button-background, + button-disabled-background: $button-disabled-background, button-hover-background: $button-hover-background, button-arrow-color: $button-arrow-color, button-hover-arrow-color: $button-hover-arrow-color, + button-disabled-arrow-color: $button-disabled-arrow-color, indicator-dot-color: $indicator-dot-color, indicator-border-color: $indicator-border-color, @@ -136,16 +146,12 @@ flex-flow: column nowrap; } - %igx-carousel-arrow { + %igx-nav-arrow { display: flex; - margin: $carousel-arrow-margin; - position: absolute; justify-content: center; align-items: center; width: 46px; height: 46px; - top: 50%; - transform: translateY(-50%); cursor: pointer; border-radius: 50%; outline-style: none; @@ -153,19 +159,34 @@ color: --var($theme, 'button-arrow-color'); background: --var($theme, 'button-background'); box-shadow: --var($theme, 'button-shadow'); - z-index: 3; + } - span { - display: flex; - align-items: center; + %igx-nav-arrow--disabled { + background: --var($theme, 'button-disabled-background'); + color: --var($theme, 'button-disabled-arrow-color'); + pointer-events: none; + box-shadow: none; + + igx-icon { + color: currentColor; } } - %igx-carousel-arrow--hover { + %igx-nav-arrow--hover { color: --var($theme, 'button-hover-arrow-color'); background: --var($theme, 'button-hover-background'); } + %igx-carousel-arrow { + margin: $carousel-arrow-margin; + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 3; + outline: none; + user-select: none; + } + %igx-carousel-arrow--next { #{$right}: 0; } diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_carousel.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_carousel.scss index 6a7ff4bbb42..524f6f40562 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_carousel.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_carousel.scss @@ -10,10 +10,20 @@ /// @prop {Color} slide-background [#222] - The slide background color. /// @prop {Color} button-background [#222] - The previous/next buttons idle background color. /// @prop {Color} button-hover-background [#222] - The previous/next buttons hover background color. +/// @prop {Map} button-disabled-background [igx-color: ('grays', 100), hexrgba: (#222)] - The previous/next buttons disabled background color. +/// @prop {Map} button-disabled-arrow-color [igx-color: ('grays' 400), hexrgba: (#222)] - The previous/next buttons disabled color. $_base-dark-carousel: ( slide-background: #222, button-background: #222, button-hover-background: #222, + button-disabled-background: ( + igx-color: ('grays', 100), + hexrgba: (#222) + ), + button-disabled-arrow-color: ( + igx-color: ('grays' 400), + hexrgba: (#222) + ), ); /// Generates a dark carousel schema based on a mix of $_light-carousel and $_base-dark-carousel diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_carousel.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_carousel.scss index 63870ab21c9..a629781222a 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_carousel.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_carousel.scss @@ -12,8 +12,10 @@ /// @prop {Color} slide-background [#fff] - The slide background color. /// @prop {Color} button-background [#fff] - The previous/next buttons idle background color. /// @prop {Color} button-hover-background [#fff] - The previous/next buttons hover background color. +/// @prop {Map} button-disabled-background [igx-color: ('grays', 100), hexrgba: ()] - The previous/next buttons disabled background color. /// @prop {Map} button-arrow-color [igx-color: ('grays', 700)] - The previous/next buttons idle arrow color. /// @prop {Map} button-hover-arrow-color [igx-color: ('grays', 900)] - The previous/next buttons hover arrow color. +/// @prop {Map} button-disabled-arrow-color [igx-color: ('grays' 400)] - The previous/next buttons disabled color. /// @prop {Color} indicator-dot-color [#fff] - The active indicator dot color. /// @prop {Color} indicator-border-color [#fff] - The idle indicator border color. /// @prop {Color} indicator-active-border-color [#fff] - The active indicator border color. @@ -39,9 +41,18 @@ $_light-carousel: extend( igx-color: ('grays', 900) ), + button-disabled-background: ( + igx-color: ('grays', 100), + hexrgba: () + ), + + button-disabled-arrow-color: ( + igx-color: ('grays' 400) + ), + indicator-dot-color: #fff, indicator-border-color: #fff, - indicator-active-border-color: #fff + indicator-active-border-color: #fff, ) ); diff --git a/src/app/carousel/carousel.sample.html b/src/app/carousel/carousel.sample.html index d94edb0c002..7dca81b7a6c 100644 --- a/src/app/carousel/carousel.sample.html +++ b/src/app/carousel/carousel.sample.html @@ -34,6 +34,18 @@

Desktop

+ +