Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/loaders/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"start": "../../utils/scripts/start.sh"
},
"dependencies": {
"@zendeskgarden/container-schedule": "^1.1.0",
"@zendeskgarden/css-variables": "^6.1.0",
"classnames": "^2.2.5",
"polished": "^2.3.3"
Expand Down
16 changes: 8 additions & 8 deletions packages/loaders/src/Dots.example.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const Color = ({ name, color, includeSample }) =>
<State
initialState={{
size: 48,
velocity: 0.05,
duration: 1250,
color: colors[0]
}}
>
Expand All @@ -105,13 +105,13 @@ const Color = ({ name, color, includeSample }) =>
</Col>
<Col md={6}>
<FormField>
<Label>Velocity {state.velocity}</Label>
<Label>Duration {state.duration}ms</Label>
<Range
value={state.velocity}
onChange={event => setState({ velocity: parseFloat(event.target.value) })}
min={-0.5}
max={1}
step={0.05}
value={state.duration}
onChange={event => setState({ duration: parseFloat(event.target.value) })}
min={625}
max={2500}
step={625}
/>
</FormField>
</Col>
Expand Down Expand Up @@ -139,7 +139,7 @@ const Color = ({ name, color, includeSample }) =>
</SpacedRow>
<SpacedRow>
<Col style={{ textAlign: 'center' }}>
<Dots size={`${state.size}px`} velocity={state.velocity} color={state.color.value} />
<Dots size={`${state.size}px`} duration={state.duration} color={state.color.value} />
</Col>
</SpacedRow>
</Grid>
Expand Down
171 changes: 73 additions & 98 deletions packages/loaders/src/Dots.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,112 +5,87 @@
* found at http://www.apache.org/licenses/LICENSE-2.0.
*/

import React from 'react';
import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { useSchedule } from '@zendeskgarden/container-schedule';

import { retrieveXCoordinate, retrieveYCoordinate, KEYFRAME_MAX } from './utils/dot-coordinates';
import { DotsCircle, StyledSVG } from './styled-elements';
import ScheduleContainer from './containers/ScheduleContainer';
import {
DotsOneCircle,
DotsTwoCircle,
DotsThreeCircle,
StyledSVG,
LoadingPlaceholder
} from './styled-elements';
import { useCSSSVGAnimation } from './hooks/useCSSSVGAnimation';

const COMPONENT_ID = 'loaders.dots';

export default class Dots extends React.Component {
static propTypes = {
/**
* Size of the loader. Can inherit from `font-size` styling.
**/
size: PropTypes.any,
/**
* Velocity (speed) of the animation. Between -1 and 1.
* This should only be maniuplated at extreme sizes.
**/
velocity: PropTypes.number,
/**
* Color of the loader. Can inherit from `color` styling.
**/
color: PropTypes.string,
/**
* Delay in MS to begin loader rendering. This helps prevent
* quick flashes of the loader during normal loading times.
**/
delayMS: PropTypes.number
};
/** @component */
export default function Dots({
size = 'inherit',
color = 'inherit',
duration = 1250,
delayMS = 750,
...other
}) {
const { delayComplete } = useSchedule({ duration, delayMS });
const noAnimatedSVGSupport = useCSSSVGAnimation();
const dotOne = useRef(null);
const dotTwo = useRef(null);
const dotThree = useRef(null);

static defaultProps = {
size: 'inherit',
color: 'inherit',
velocity: 0.05,
delayMS: 750
};
useEffect(() => {
if (noAnimatedSVGSupport && delayComplete) {
const transforms = [
window.getComputedStyle(dotOne.current).getPropertyValue('transform'),
window.getComputedStyle(dotTwo.current).getPropertyValue('transform'),
window.getComputedStyle(dotThree.current).getPropertyValue('transform')
];

state = {
frame: 0,
timestamp: 0
};

performAnimationFrame = (timestamp = 0) => {
const { velocity } = this.props;

let pinnedVelocity = velocity;

if (velocity < -1) {
pinnedVelocity = -0.9;
} else if (velocity > 1) {
pinnedVelocity = 1;
dotOne.current.setAttribute('transform', transforms[0]);
dotTwo.current.setAttribute('transform', transforms[1]);
dotThree.current.setAttribute('transform', transforms[2]);
}
});

this.setState(prevState => {
const factor = 1000 + 1000 * pinnedVelocity;
const elapsed = (timestamp - prevState.timestamp) / factor;
const frame = prevState.frame + (elapsed % KEYFRAME_MAX);

return { frame, timestamp };
});
};

retrieveFrame = offset => {
const loop = KEYFRAME_MAX * 2;

return (this.state.frame + offset * loop) % loop;
};

render() {
const { size, color, delayMS, ...other } = this.props;
const dotOneFrame = this.retrieveFrame(0);
const dotTwoFrame = this.retrieveFrame(1 / 3);
const dotThreeFrame = this.retrieveFrame(2 / 3);

return (
<ScheduleContainer tick={this.performAnimationFrame} size={size} delayMS={delayMS}>
{() => (
<StyledSVG
fontSize={size}
color={color}
width="80"
height="72"
data-garden-id={COMPONENT_ID}
{...other}
>
<g fill="currentColor">
<DotsCircle
transform={`translate(${retrieveXCoordinate(dotOneFrame)} ${retrieveYCoordinate(
dotOneFrame
)})`}
/>
<DotsCircle
transform={`translate(${retrieveXCoordinate(dotTwoFrame)} ${retrieveYCoordinate(
dotTwoFrame
)})`}
/>
<DotsCircle
transform={`translate(${retrieveXCoordinate(dotThreeFrame)} ${retrieveYCoordinate(
dotThreeFrame
)})`}
/>
</g>
</StyledSVG>
)}
</ScheduleContainer>
);
if (!delayComplete && delayMS !== 0) {
return <LoadingPlaceholder fontSize={size}>&nbsp;</LoadingPlaceholder>;
}

return (
<StyledSVG
fontSize={size}
color={color}
width="80"
height="72"
data-garden-id={COMPONENT_ID}
{...other}
>
<g fill="currentColor">
<DotsOneCircle duration={duration} ref={dotOne} />
<DotsTwoCircle duration={duration} ref={dotTwo} />
<DotsThreeCircle duration={duration} ref={dotThree} />
</g>
</StyledSVG>
);
}

Dots.propTypes = {
/**
* Size of the loader. Can inherit from `font-size` styling.
**/
size: PropTypes.any,
/**
* Duration (ms) of the animation. Default is 1250ms.
**/
duration: PropTypes.number,
/**
* Color of the loader. Can inherit from `color` styling.
**/
color: PropTypes.string,
/**
* Delay in MS to begin loader rendering. This helps prevent
* quick flashes of the loader during normal loading times.
**/
delayMS: PropTypes.number
};
Loading