Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.

Commit d0dfe57

Browse files
authored
feat(Loader): add support for SVG animations, update in Teams theme (#1097)
* feat(Loader): add support for SVG animations, update in Teams theme * add changelog entry * add missing sizes * feat(Loader): add support for SVG animations, update in Teams theme * add changelog entry * add missing sizes * use svg slot * add propType * fix className * move changelog entry * update loader size, translation variables & update the Loader size example * removing old changelog entry from bad merge * fix size in prototype
1 parent 7bd7426 commit d0dfe57

File tree

9 files changed

+196
-13
lines changed

9 files changed

+196
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
2727
### Features
2828
- Add `attached` prop on the `ChatMessage` component, which is automatically set by the `ChatItem` component @mnajdova ([#1100](https://github.com/stardust-ui/react/pull/1100))
2929
- Align `Alert` component styles to latest design for Teams theme @Bugaa92 ([#1111](https://github.com/stardust-ui/react/pull/1111))
30+
- Add support for SVG animations to `Loader`, update in Teams theme @layershifter ([#1097](https://github.com/stardust-ui/react/pull/1097))
3031

3132
<!--------------------------------[ v0.25.0 ]------------------------------- -->
3233
## [v0.25.0](https://github.com/stardust-ui/react/tree/v0.25.0) (2019-03-26)

docs/src/examples/components/Loader/Variations/LoaderExampleSize.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import { Grid, Loader } from '@stardust-ui/react'
22
import * as React from 'react'
33

44
const LoaderExampleSize: React.FC = () => (
5-
<Grid columns="3" variables={{ gridGap: '20px' }}>
6-
<Loader size="smallest" />
7-
<Loader size="smaller" />
8-
<Loader size="small" />
5+
<Grid columns="4" variables={{ gridGap: '20px' }}>
6+
<Loader size="smallest" label="smallest" labelPosition="below" />
7+
<Loader size="smaller" label="smaller" labelPosition="below" />
8+
<Loader size="small" label="small" labelPosition="below" />
99

10-
<Loader size="large" />
11-
<Loader size="larger" />
12-
<Loader size="largest" />
10+
<Loader label="medium (default)" labelPosition="below" />
11+
12+
<Loader size="large" label="large" labelPosition="below" />
13+
<Loader size="larger" label="larger" labelPosition="below" />
14+
<Loader size="largest" label="largest" labelPosition="below" />
1315
</Grid>
1416
)
1517

docs/src/prototypes/dropdowns/AsyncDropdownSearch.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class AsyncDropdownSearch extends React.Component<{}, SearchPageState> {
5757
if (this.state.items.length > 10) return
5858

5959
this.setState({ loading: true })
60-
this.searchTimer = setTimeout(() => {
60+
this.searchTimer = window.setTimeout(() => {
6161
this.setState(prevState => ({
6262
loading: false,
6363
items: [...prevState.items, ..._.times<Entry>(2, createEntry)],
@@ -76,7 +76,7 @@ class AsyncDropdownSearch extends React.Component<{}, SearchPageState> {
7676
items={items}
7777
loading={loading}
7878
loadingMessage={{
79-
content: <Loader label="Loading..." labelPosition="end" size="larger" />,
79+
content: <Loader label="Loading..." labelPosition="end" />,
8080
}}
8181
multiple
8282
onSearchQueryChange={this.handleSearchQueryChange}

packages/react/src/components/Loader/Loader.tsx

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ import Box from '../Box/Box'
1717

1818
export type LoaderPosition = 'above' | 'below' | 'start' | 'end'
1919

20+
export interface LoaderSlotClassNames {
21+
indicator: string
22+
label: string
23+
svg: string
24+
}
25+
2026
export interface LoaderProps extends UIComponentProps, ColorComponentProps {
2127
/**
2228
* Accessibility behavior if overridden by the user.
@@ -41,6 +47,9 @@ export interface LoaderProps extends UIComponentProps, ColorComponentProps {
4147

4248
/** A size of the loader. */
4349
size?: SizeValue
50+
51+
/** A loader can contain a custom svg element. */
52+
svg?: ShorthandValue
4453
}
4554

4655
export interface LoaderState {
@@ -54,6 +63,11 @@ class Loader extends UIComponent<ReactProps<LoaderProps>, LoaderState> {
5463
static create: Function
5564
static displayName = 'Loader'
5665
static className = 'ui-loader'
66+
static slotClassNames: LoaderSlotClassNames = {
67+
indicator: `${Loader.className}__indicator`,
68+
label: `${Loader.className}__label`,
69+
svg: `${Loader.className}__svg`,
70+
}
5771

5872
static propTypes = {
5973
...commonPropTypes.createCommon({
@@ -67,13 +81,15 @@ class Loader extends UIComponent<ReactProps<LoaderProps>, LoaderState> {
6781
label: customPropTypes.itemShorthand,
6882
labelPosition: PropTypes.oneOf(['above', 'below', 'start', 'end']),
6983
size: customPropTypes.size,
84+
svg: customPropTypes.itemShorthand,
7085
}
7186

7287
static defaultProps = {
7388
accessibility: loaderBehavior,
7489
delay: 0,
75-
indicator: '',
90+
indicator: {},
7691
labelPosition: 'below',
92+
svg: '',
7793
size: 'medium',
7894
}
7995

@@ -102,18 +118,30 @@ class Loader extends UIComponent<ReactProps<LoaderProps>, LoaderState> {
102118
}
103119

104120
renderComponent({ ElementType, classes, accessibility, variables, styles, unhandledProps }) {
105-
const { indicator, label } = this.props
121+
const { indicator, label, svg } = this.props
106122
const { visible } = this.state
107123

124+
const svgElement = Box.create(svg, {
125+
defaultProps: { className: Loader.slotClassNames.svg, styles: styles.svg },
126+
})
127+
108128
return (
109129
visible && (
110130
<ElementType
111131
className={classes.root}
112132
{...accessibility.attributes.root}
113133
{...unhandledProps}
114134
>
115-
{Box.create(indicator, { defaultProps: { styles: styles.indicator } })}
116-
{Box.create(label, { defaultProps: { styles: styles.label } })}
135+
{Box.create(indicator, {
136+
defaultProps: {
137+
children: svgElement,
138+
className: Loader.slotClassNames.indicator,
139+
styles: styles.indicator,
140+
},
141+
})}
142+
{Box.create(label, {
143+
defaultProps: { className: Loader.slotClassNames.label, styles: styles.label },
144+
})}
117145
</ElementType>
118146
)
119147
)

packages/react/src/themes/teams/componentStyles.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ export { default as Label } from './components/Label/labelStyles'
4343

4444
export { default as Layout } from './components/Layout/layoutStyles'
4545

46+
export { default as Loader } from './components/Loader/loaderStyles'
47+
4648
export { default as ItemLayout } from './components/ItemLayout/itemLayoutStyles'
4749

4850
export { default as List } from './components/List/listStyles'

packages/react/src/themes/teams/componentVariables.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export { default as Label } from './components/Label/labelVariables'
4040

4141
export { default as Layout } from './components/Layout/layoutVariables'
4242

43+
export { default as Loader } from './components/Loader/loaderVariables'
44+
4345
export { default as ItemLayout } from './components/ItemLayout/itemLayoutVariables'
4446

4547
export { default as ListItem } from './components/List/listItemVariables'
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { LoaderProps } from '../../../../components/Loader/Loader'
2+
import { ComponentStyleFunctionParam, ICSSInJSStyle } from '../../../types'
3+
import { LoaderVariables } from './loaderVariables'
4+
5+
export default {
6+
indicator: ({
7+
props: p,
8+
variables: v,
9+
}: ComponentStyleFunctionParam<LoaderProps, LoaderVariables>): ICSSInJSStyle => ({
10+
// Reset existing styles from base theme
11+
animationName: 'none',
12+
animationDuration: 'unset',
13+
animationIterationCount: 'unset',
14+
animationTimingFunction: 'unset',
15+
borderColor: 'transparent',
16+
borderRadius: 0,
17+
borderStyle: 'none',
18+
borderWidth: 0,
19+
20+
height: v.containerHeights[p.size],
21+
width: v.containerWidths[p.size],
22+
overflow: 'hidden',
23+
}),
24+
svg: ({
25+
props: p,
26+
theme: t,
27+
variables: v,
28+
}: ComponentStyleFunctionParam<LoaderProps, LoaderVariables>) => {
29+
const outerAnimation: ICSSInJSStyle = {
30+
animationName: t.renderer.renderKeyframe(
31+
() =>
32+
({
33+
to: {
34+
opacity: 1,
35+
},
36+
} as any),
37+
{},
38+
),
39+
animationDelay: '1.5s',
40+
animationDirection: 'normal',
41+
animationDuration: '.3s',
42+
animationFillMode: 'both',
43+
animationIterationCount: '1',
44+
animationPlayState: 'running',
45+
animationTimingFunction: 'ease-out',
46+
display: 'block',
47+
overflow: 'hidden',
48+
position: 'relative',
49+
}
50+
const svgAnimation: ICSSInJSStyle = {
51+
animationName: t.renderer.renderKeyframe(
52+
() =>
53+
({
54+
to: {
55+
transform: `translate3d(0, ${v.svgTranslatePosition[p.size]}, 0)`,
56+
},
57+
} as any),
58+
{},
59+
),
60+
animationDelay: '0s',
61+
animationDirection: 'normal',
62+
animationDuration: '2s',
63+
animationFillMode: 'both',
64+
animationPlayState: 'running',
65+
animationTimingFunction: 'steps(60, end)',
66+
animationIterationCount: 'infinite',
67+
}
68+
69+
return {
70+
...outerAnimation,
71+
72+
':before': {
73+
...svgAnimation,
74+
75+
backgroundImage: v.svgContent,
76+
content: '" "',
77+
display: 'block',
78+
overflow: 'hidden',
79+
80+
height: v.svgHeights[p.size],
81+
width: v.svgWidths[p.size],
82+
},
83+
}
84+
},
85+
}

0 commit comments

Comments
 (0)