aboutsummaryrefslogtreecommitdiffstats
path: root/packages/instant/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/instant/src/components')
-rw-r--r--packages/instant/src/components/time_counter.tsx64
-rw-r--r--packages/instant/src/components/timed_progress_bar.tsx101
2 files changed, 165 insertions, 0 deletions
diff --git a/packages/instant/src/components/time_counter.tsx b/packages/instant/src/components/time_counter.tsx
new file mode 100644
index 000000000..26deb82bd
--- /dev/null
+++ b/packages/instant/src/components/time_counter.tsx
@@ -0,0 +1,64 @@
+import * as React from 'react';
+
+import { timeUtil } from '../util/time';
+
+import { Flex } from './ui/flex';
+import { Text } from './ui/text';
+
+export interface TimeCounterProps {
+ estimatedTimeMs: number;
+ ended: boolean;
+}
+interface TimeCounterState {
+ elapsedSeconds: number;
+}
+
+export class TimeCounter extends React.Component<TimeCounterProps, TimeCounterState> {
+ public state = {
+ elapsedSeconds: 0,
+ };
+ private _timerId?: number;
+
+ public componentDidMount(): void {
+ this._setupTimerBasedOnProps();
+ }
+
+ public componentWillUnmount(): void {
+ this._clearTimer();
+ }
+
+ public componentDidUpdate(prevProps: TimeCounterProps): void {
+ if (prevProps.ended !== this.props.ended) {
+ this._setupTimerBasedOnProps();
+ }
+ }
+
+ public render(): React.ReactNode {
+ const estimatedTimeSeconds = this.props.estimatedTimeMs / 1000;
+ return (
+ <Flex justify="space-between">
+ <Text>Est. Time ({timeUtil.secondsToHumanDescription(estimatedTimeSeconds)})</Text>
+ <Text>Time: {timeUtil.secondsToStopwatchTime(this.state.elapsedSeconds)}</Text>
+ </Flex>
+ );
+ }
+
+ private _setupTimerBasedOnProps(): void {
+ this.props.ended ? this._clearTimer() : this._newTimer();
+ }
+
+ private _newTimer(): void {
+ this._clearTimer();
+ this._timerId = window.setInterval(() => {
+ this.setState({
+ elapsedSeconds: this.state.elapsedSeconds + 1,
+ });
+ }, 1000);
+ }
+
+ private _clearTimer(): void {
+ if (this._timerId) {
+ window.clearInterval(this._timerId);
+ }
+ }
+}
diff --git a/packages/instant/src/components/timed_progress_bar.tsx b/packages/instant/src/components/timed_progress_bar.tsx
new file mode 100644
index 000000000..7fdfe1a25
--- /dev/null
+++ b/packages/instant/src/components/timed_progress_bar.tsx
@@ -0,0 +1,101 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { Keyframes } from 'styled-components';
+
+import { PROGRESS_FINISH_ANIMATION_TIME_MS, PROGRESS_STALL_AT_PERCENTAGE } from '../constants';
+import { ColorOption, keyframes, styled } from '../style/theme';
+import { timeUtil } from '../util/time';
+
+import { Container } from './ui/container';
+import { Flex } from './ui/flex';
+import { Text } from './ui/text';
+
+export interface TimedProgressBarProps {
+ expectedTimeMs: number;
+ ended: boolean;
+}
+
+interface TimedProgressBarState {
+ animationTimeMs: number;
+ animationStartingWidth: string;
+ maxWidthPercent: number;
+}
+
+export const beginningState = (props: TimedProgressBarProps): TimedProgressBarState => {
+ return {
+ animationTimeMs: props.expectedTimeMs,
+ animationStartingWidth: '0%',
+ maxWidthPercent: PROGRESS_STALL_AT_PERCENTAGE,
+ };
+};
+
+export class TimedProgressBar extends React.Component<TimedProgressBarProps, TimedProgressBarState> {
+ private readonly _barRef = React.createRef<HTMLDivElement>();
+
+ public constructor(props: TimedProgressBarProps) {
+ super(props);
+ this.state = beginningState(props);
+ }
+
+ public componentDidUpdate(prevProps: TimedProgressBarProps, prevState: TimedProgressBarState): void {
+ if (prevProps.ended === false && this.props.ended === true) {
+ // Show nice animation going to end
+ // barRef current should always exist, but checking for typesafety
+ if (this._barRef.current) {
+ const curProgressWidth = this._barRef.current.offsetWidth;
+ this.setState({
+ animationTimeMs: PROGRESS_FINISH_ANIMATION_TIME_MS,
+ animationStartingWidth: `${curProgressWidth}px`,
+ maxWidthPercent: 100,
+ });
+ }
+ return;
+ }
+
+ if (prevProps.expectedTimeMs !== this.props.expectedTimeMs || prevProps.ended !== this.props.ended) {
+ // things changed, get fresh state
+ this.setState(beginningState(this.props));
+ }
+ }
+
+ public render(): React.ReactNode {
+ return (
+ <Container width="100%" backgroundColor={ColorOption.lightGrey} borderRadius="6px">
+ <TimedProgress
+ fromWidth={this.state.animationStartingWidth}
+ timeMs={this.state.animationTimeMs}
+ maxWidthPercent={this.state.maxWidthPercent}
+ ref={this._barRef as any}
+ />
+ </Container>
+ );
+ }
+}
+
+const expandingWidthKeyframes = (fromWidth: string, maxWidthPercent: number) => {
+ return keyframes`
+ from {
+ width: ${fromWidth}
+ }
+ to {
+ width: ${maxWidthPercent}%;
+ }
+ `;
+};
+
+interface TimedProgressProps {
+ timeMs: number;
+ fromWidth: string;
+ maxWidthPercent: number;
+}
+// TODO use PrimaryColor instead of black
+export const TimedProgress =
+ styled.div <
+ TimedProgressProps >
+ `
+ background-color: black;
+ border-radius: 6px;
+ height: 6px;
+ animation: ${props => expandingWidthKeyframes(props.fromWidth, props.maxWidthPercent)}
+ ${props => props.timeMs}ms linear 1 forwards;
+ `;