Skip to content

Commit e1f6711

Browse files
committed
slide indicators
1 parent 2843fd3 commit e1f6711

5 files changed

Lines changed: 237 additions & 10 deletions

File tree

src/controls/carousel/Carousel.module.scss

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,61 @@
1+
@import "~office-ui-fabric-react/dist/sass/References.scss";
2+
13
.container {
24
display: flex;
35

46
// Styles for elements container
57
.contentContainer {
68
flex-grow: 2;
9+
position: relative;
10+
}
11+
12+
.indicators {
13+
position: absolute;
14+
right: 0;
15+
bottom: 0;
16+
left: 0;
17+
z-index: 10;
18+
display: flex;
19+
justify-content: center;
20+
list-style: none;
21+
22+
&.rectangle > li {
23+
width: 25px;
24+
height: 5px;
25+
border-top: 10px solid transparent;
26+
border-bottom: 10px solid transparent;
27+
}
28+
29+
&.square > li {
30+
width: 10px;
31+
height: 10px;
32+
border-top: 10px solid transparent;
33+
border-bottom: 10px solid transparent;
34+
}
35+
36+
&.circle > li {
37+
width: 10px;
38+
height: 10px;
39+
border-radius: 50%;
40+
border-top: 10px solid transparent;
41+
border-bottom: 10px solid transparent;
42+
}
43+
44+
& > li {
45+
flex: 0 1 auto;
46+
text-indent: -999px;
47+
cursor: pointer;
48+
background-color: "[theme:white, default: #fff]";
49+
opacity: 0.5;
50+
transition: opacity 0.5 ease;
51+
margin: 0 3px;
52+
box-sizing: content-box;
53+
background-clip: padding-box;
54+
55+
&.active {
56+
opacity: 1;
57+
}
58+
}
759
}
860

961
.loadingComponent {

src/controls/carousel/Carousel.tsx

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ initializeIcons();
55

66
import * as React from "react";
77
import styles from "./Carousel.module.scss";
8-
import { ICarouselProps, ICarouselState, CarouselButtonsDisplay, CarouselButtonsLocation } from ".";
8+
import { ICarouselProps, ICarouselState, CarouselButtonsDisplay, CarouselButtonsLocation, CarouselIndicatorShape } from ".";
99
import { css, ICssInput } from "@uifabric/utilities/lib";
1010
import { ProcessingState } from "./ICarouselState";
1111
import { Spinner } from "office-ui-fabric-react/lib/Spinner";
@@ -48,16 +48,23 @@ export class Carousel extends React.Component<ICarouselProps, ICarouselState> {
4848

4949
public render(): React.ReactElement<ICarouselProps> {
5050
const { currentIndex, processingState } = this.state;
51-
const { containerStyles, contentContainerStyles, containerButtonsStyles, prevButtonStyles, nextButtonStyles, loadingComponentContainerStyles } = this.props;
51+
const {
52+
containerStyles,
53+
contentContainerStyles,
54+
containerButtonsStyles,
55+
prevButtonStyles,
56+
nextButtonStyles,
57+
loadingComponentContainerStyles,
58+
prevButtonIconName = 'ChevronLeft',
59+
nextButtonIconName = 'ChevronRight',
60+
loadingComponent = <Spinner />
61+
} = this.props;
5262

53-
const prevButtonIconName = this.props.prevButtonIconName ? this.props.prevButtonIconName : "ChevronLeft";
54-
const nextButtonIconName = this.props.nextButtonIconName ? this.props.nextButtonIconName : "ChevronRight";
5563
const processing = processingState === ProcessingState.processing;
5664

5765
const prevButtonDisabled = processing || this.isCarouselButtonDisabled(false);
5866
const nextButtonDisabled = processing || this.isCarouselButtonDisabled(true);
5967

60-
const loadingComponent = this.props.loadingComponent ? this.props.loadingComponent : <Spinner />;
6168
const element = this.getElementToDisplay();
6269

6370
return (
@@ -80,10 +87,9 @@ export class Carousel extends React.Component<ICarouselProps, ICarouselState> {
8087
}
8188

8289
{
83-
!processing && element &&
84-
element
90+
!processing && element
8591
}
86-
92+
{this.getIndicatorsElement()}
8793
</div>
8894

8995
<div className={this.getMergedStyles(this.getButtonContainerStyles(), containerButtonsStyles)}
@@ -98,6 +104,64 @@ export class Carousel extends React.Component<ICarouselProps, ICarouselState> {
98104
);
99105
}
100106

107+
private getIndicatorsElement = (): JSX.Element | null => {
108+
const {
109+
indicators,
110+
indicatorShape = CarouselIndicatorShape.rectangle,
111+
onRenderIndicator,
112+
triggerPageEvent
113+
} = this.props;
114+
115+
const {
116+
currentIndex = 0
117+
} = this.state;
118+
119+
if (indicators === false) {
120+
return null;
121+
}
122+
123+
const elementsCount = triggerPageEvent ? this.props.elementsCount : isArray(this.props.element) ? (this.props.element as any[]).length : 1;
124+
125+
const indicatorElements: JSX.Element[] = [];
126+
for (let i = 0; i < elementsCount; i++) {
127+
if (onRenderIndicator) {
128+
indicatorElements.push(onRenderIndicator(i, this.onIndicatorClick));
129+
}
130+
else {
131+
indicatorElements.push(<li
132+
className={i === currentIndex ? styles.active : undefined}
133+
onClick={e => this.onIndicatorClick(e, i)}
134+
/>);
135+
}
136+
}
137+
138+
if (onRenderIndicator) {
139+
return <div className={styles.indicators}>
140+
{indicatorElements}
141+
</div>;
142+
}
143+
else {
144+
return <ol className={css({
145+
[styles.indicators]: true,
146+
[styles.circle]: indicatorShape === CarouselIndicatorShape.circle,
147+
[styles.rectangle]: indicatorShape === CarouselIndicatorShape.rectangle,
148+
[styles.square]: indicatorShape === CarouselIndicatorShape.square
149+
})}>
150+
{indicatorElements}
151+
</ol>;
152+
}
153+
}
154+
155+
private onIndicatorClick = (e: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement>, index: number): void => {
156+
if (this.props.onSelect) {
157+
this.props.onSelect(index);
158+
}
159+
160+
this.setState({
161+
currentIndex: index
162+
});
163+
}
164+
101165
/**
102166
* Return merged styles for Button containers.
103167
*/

src/controls/carousel/CarouselImage.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,78 @@ import styles from './CarouselImage.module.scss';
33
import { Image, ImageFit } from 'office-ui-fabric-react/lib/Image';
44
import { css } from 'office-ui-fabric-react/lib/Utilities';
55

6+
/**
7+
* Carousel Image component props
8+
*/
69
export interface ICarouselImageProps {
10+
/**
11+
* Image source
12+
*/
713
imageSrc: string;
14+
/**
15+
* Specifies the method to be used to fit image
16+
*/
817
imageFit?: ImageFit;
18+
/**
19+
* URL to be opened when clicking on details
20+
*/
921
url?: string;
22+
/**
23+
* Title to display in details
24+
*/
1025
title?: string;
26+
/**
27+
* Description to show in details.
28+
* Can be either a string (text) or JSX.Element to show HTML
29+
*/
1130
description?: string | JSX.Element;
31+
/**
32+
* Target of the URL to open
33+
*/
1234
target?: '_blank' | '_self';
35+
/**
36+
* Specifies if the details are shown on hover or constantly
37+
*/
1338
showDetailsOnHover?: boolean;
39+
/**
40+
* Class to apply to the component
41+
*/
1442
className?: string;
43+
/**
44+
* Styles to apply to the component
45+
*/
1546
style?: React.CSSProperties;
47+
/**
48+
* Class to apply to the image control
49+
*/
1650
imgClassName?: string;
51+
/**
52+
* Styles to apply to the image control
53+
*/
1754
imgStyle?: React.CSSProperties;
55+
/**
56+
* Class to apply to the details control
57+
*/
1858
detailsClassName?: string;
59+
/**
60+
* Styles to apply to the details control
61+
*/
1962
detailsStyle?: React.CSSProperties;
63+
/**
64+
* Class to apply to the title control
65+
*/
2066
titleClassName?: string;
67+
/**
68+
* Styles to apply to the title control
69+
*/
2170
titleStyle?: React.CSSProperties;
71+
/**
72+
* Class to apply to the description control
73+
*/
2274
descriptionClassName?: string;
75+
/**
76+
* Class to apply to the description control
77+
*/
2378
descriptionStyle?: React.CSSProperties;
2479
}
2580

src/controls/carousel/ICarouselProps.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as React from 'react';
12
import { ICssInput } from "@uifabric/utilities/lib";
23
import { ICarouselImageProps } from "./CarouselImage";
34

@@ -27,6 +28,24 @@ export enum CarouselButtonsDisplay {
2728
hidden
2829
}
2930

31+
/**
32+
* Provides options for carousel indicators' shape
33+
*/
34+
export enum CarouselIndicatorShape {
35+
/**
36+
* Indicators displayed as cirlces
37+
*/
38+
circle,
39+
/**
40+
* Indicators displayed as squares
41+
*/
42+
square,
43+
/**
44+
* Indicators displayed as rectangles
45+
*/
46+
rectangle
47+
}
48+
3049
export interface ICarouselProps {
3150
/**
3251
* Specifies the initial index of the element to be displayed.
@@ -92,10 +111,11 @@ export interface ICarouselProps {
92111
/**
93112
* Triggers parent control to provide new element to be displayed. After the method is executed, carousel control switches to processing mode and loadingComponent is displayed.
94113
*/
95-
triggerPageEvent? : (index: number) => void;
114+
triggerPageEvent?: (index: number) => void;
96115
/**
97116
* Fixed array of elemenets to be displayed in carousel - if triggerPageEvent is not used.
98117
* In case triggerPageEvent is in use, JSX.Element has to be provided. Elements are distinguished based on the 'key' property.
118+
* It's also possible to provide an array of ICarouselImageProps to use default implementation for the elements
99119
*/
100120
element: JSX.Element | JSX.Element[] | ICarouselImageProps[];
101121
/**
@@ -112,4 +132,39 @@ export interface ICarouselProps {
112132
*/
113133
onMovePrevClicked?: (currentIndex: number) => void;
114134

135+
/**
136+
* In case triggerPageEvent is in use, provides total number of slides in the carousel.
137+
*/
138+
elementsCount?: number;
139+
140+
/**
141+
* Callback function called when element has been selected in the carousel
142+
*/
143+
onSelect?: (selectedIndex: number) => void;
144+
145+
/**
146+
* Enables animation on the Carousel as it transitions between slides.
147+
*/
148+
slide?: boolean;
149+
150+
/**
151+
* The amount of time to delay between automatically cycling an item. If undefined, carousel will not automatically cycle.
152+
*/
153+
interval?: number;
154+
155+
/**
156+
* Specifies if set of slide position indicators is shown
157+
*/
158+
indicators?: boolean;
159+
160+
/**
161+
* Specifies indicators' shape. If onRenderIndicator is provided - this property is ignored
162+
*/
163+
indicatorShape?: CarouselIndicatorShape;
164+
165+
/**
166+
* Function to render indicator element
167+
*/
168+
onRenderIndicator?: (index: number, onClick: (e: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement>, selectedIndex: number) => void) => JSX.Element;
169+
115170
}

src/webparts/controlsTest/components/ControlsTest.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { TermLabelAction, TermActionsDisplayMode } from '../../../controls/taxon
3333
import { ListItemAttachments } from '../../../ListItemAttachments';
3434
import { RichText } from '../../../RichText';
3535
import { Link } from 'office-ui-fabric-react/lib/components/Link';
36-
import { Carousel, CarouselButtonsLocation, CarouselButtonsDisplay } from '../../../controls/carousel';
36+
import { Carousel, CarouselButtonsLocation, CarouselButtonsDisplay, CarouselIndicatorShape } from '../../../controls/carousel';
3737
import { TimeDisplayControlType } from '../../../controls/dateTimePicker/TimeDisplayControlType';
3838
import { GridLayout } from '../../../GridLayout';
3939
import { ComboBoxListItemPicker } from '../../../controls/listItemPicker/ComboBoxListItemPicker';
@@ -1045,6 +1045,7 @@ export default class ControlsTest extends React.Component<IControlsTestProps, IC
10451045
//containerButtonsStyles={styles.carouselButtonsContainer}
10461046

10471047
isInfinite={true}
1048+
indicatorShape={CarouselIndicatorShape.circle}
10481049

10491050
element={[
10501051
{

0 commit comments

Comments
 (0)