Skip to content

Commit 8c87af4

Browse files
authored
feat: progress bar (#220)
* feat: progress bar * progress bar * v1.4.0
1 parent dc346ca commit 8c87af4

File tree

8 files changed

+577
-1
lines changed

8 files changed

+577
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "1.3.7",
2+
"version": "1.4.0",
33
"license": "MIT",
44
"main": "dist/index.js",
55
"typings": "dist/index.d.ts",

src/progress/ProgressBar.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { DOMRef } from '@react-types/shared';
2+
import React from 'react';
3+
import { useProgressBar } from '@react-aria/progress';
4+
import { ACProgressBarProps } from '../types';
5+
import { classNames } from '../utils';
6+
import { ProgressBarBase } from './ProgressBarBase';
7+
8+
function ProgressBar(props: ACProgressBarProps, ref: DOMRef<HTMLDivElement>) {
9+
let { staticColor, ...otherProps } = props;
10+
const { progressBarProps, labelProps } = useProgressBar(props);
11+
12+
return (
13+
<ProgressBarBase
14+
{...otherProps}
15+
ref={ref}
16+
barProps={progressBarProps}
17+
labelProps={labelProps}
18+
barClassName={classNames({
19+
'ac-barloader--static-white': staticColor === 'white',
20+
'ac-barloader--static-black': staticColor === 'black',
21+
})}
22+
/>
23+
);
24+
}
25+
26+
/**
27+
* ProgressBars show the progression of a system operation: downloading, uploading, processing, etc., in a visual way.
28+
* They can represent either determinate or indeterminate progress.
29+
*/
30+
let _ProgressBar = React.forwardRef(ProgressBar);
31+
export { _ProgressBar as ProgressBar };

src/progress/ProgressBarBase.tsx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { clamp } from '@react-aria/utils';
2+
import React, { CSSProperties, HTMLAttributes } from 'react';
3+
import { DOMRef, ProgressBarProps, ACProgressBarBaseProps } from '../types';
4+
import { classNames, useDOMRef, useStyleProps } from '../utils';
5+
import { progressBarCSS } from './styles';
6+
interface ProgressBarBaseProps
7+
extends ACProgressBarBaseProps,
8+
ProgressBarProps {
9+
barClassName?: string;
10+
barProps?: HTMLAttributes<HTMLDivElement>;
11+
labelProps?: HTMLAttributes<HTMLLabelElement>;
12+
}
13+
14+
// Base ProgressBar component shared with Meter.
15+
function ProgressBarBase(
16+
props: ProgressBarBaseProps,
17+
ref: DOMRef<HTMLDivElement>
18+
) {
19+
let {
20+
value = 0,
21+
minValue = 0,
22+
maxValue = 100,
23+
size = 'L',
24+
label,
25+
barClassName,
26+
showValueLabel = !!label,
27+
labelPosition = 'top',
28+
isIndeterminate = false,
29+
barProps,
30+
labelProps,
31+
'aria-label': ariaLabel,
32+
'aria-labelledby': ariaLabelledby,
33+
...otherProps
34+
} = props;
35+
let domRef = useDOMRef(ref);
36+
let { styleProps } = useStyleProps(otherProps);
37+
38+
value = clamp(value, minValue, maxValue);
39+
40+
let barStyle: CSSProperties = {};
41+
if (!isIndeterminate) {
42+
let percentage = (value - minValue) / (maxValue - minValue);
43+
barStyle.width = `${Math.round(percentage * 100)}%`;
44+
}
45+
46+
// Ideally this should be in useProgressBar, but children
47+
// are not supported in ProgressCircle which shares that hook...
48+
if (!label && !ariaLabel && !ariaLabelledby) {
49+
// eslint-disable-next-line no-console
50+
console.warn(
51+
'If you do not provide a visible label via children, you must specify an aria-label or aria-labelledby attribute for accessibility'
52+
);
53+
}
54+
// use inline style for fit-content because cssnano is too smart for us and will strip out the -moz prefix in css files
55+
return (
56+
<div
57+
{...barProps}
58+
ref={domRef}
59+
className={classNames(
60+
'ac-barloader',
61+
{
62+
'ac-barloader--small': size === 'S',
63+
'ac-barloader--large': size === 'L',
64+
'ac-barloader--indeterminate': isIndeterminate,
65+
'ac-barloader--sideLabel': labelPosition === 'side',
66+
},
67+
barClassName,
68+
styleProps.className
69+
)}
70+
css={progressBarCSS}
71+
style={{ minWidth: '-moz-fit-content', ...styleProps.style }}
72+
>
73+
{label && (
74+
<span {...labelProps} className="ac-barloader-label">
75+
{label}
76+
</span>
77+
)}
78+
{showValueLabel && barProps && (
79+
<div className={'ac-barloader-percentage'}>
80+
{barProps['aria-valuetext']}
81+
</div>
82+
)}
83+
<div className={'ac-barloader-track'}>
84+
<div className={'ac-barloader-fill'} style={barStyle} />
85+
</div>
86+
</div>
87+
);
88+
}
89+
90+
let _ProgressBarBase = React.forwardRef(ProgressBarBase);
91+
export { _ProgressBarBase as ProgressBarBase };

src/progress/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './ProgressCircle';
2+
export * from './ProgressBar';

src/progress/styles.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,83 @@ export const progressCircleCSS = css`
122122
}
123123
}
124124
`;
125+
126+
export const progressBarCSS = css`
127+
--ac-barloader-large-border-radius: 3px;
128+
--ac-barloader-track-color-default: var(--ac-global-color-grey-300);
129+
&.ac-barloader {
130+
--ac-barloader-large-track-fill-color: var(--ac-global-color-primary);
131+
--ac-barloader-static-black-track-color: #00000040;
132+
--ac-barloader-static-black-fill-color: var(
133+
--ac-global-static-color-black-900
134+
);
135+
136+
min-inline-size: var(--ac-global-dimension-static-size-600, 48px);
137+
inline-size: var(--ac-global-dimension-size-2400);
138+
vertical-align: top;
139+
isolation: isolate;
140+
flex-flow: wrap;
141+
justify-content: space-between;
142+
align-items: center;
143+
display: inline-flex;
144+
position: relative;
145+
}
146+
147+
&.ac-barloader--static-white {
148+
--mod-barloader-label-and-value-color: var(
149+
--ac-global-static-color-white-900
150+
);
151+
--mod-barloader-fill-color: var(--ac-global-color-white-900);
152+
}
153+
&.ac-barloader--static-black {
154+
--mod-barloader-label-and-value-color: var(
155+
--ac-global-static-color-black-900
156+
);
157+
--mod-barloader-fill-color: var(--ac-global-static-color-black-900);
158+
--mod-barloader-track-color: var(--ac-barloader-static-black-track-color);
159+
}
160+
161+
.ac-barloader-label,
162+
.ac-barloader-percentage {
163+
color: var(
164+
--mod-barloader-label-and-value-color,
165+
var(--ac-global-text-color-900)
166+
);
167+
font-size: var(--spectrum-global-dimension-font-size-75);
168+
font-weight: var(--spectrum-global-font-weight-regular);
169+
line-height: var(--spectrum-global-font-line-height-small);
170+
text-align: start;
171+
text-align: start;
172+
margin-bottom: var(--ac-global-dimension-size-115);
173+
}
174+
175+
.ac-barloader-label {
176+
flex: 1;
177+
}
178+
179+
.ac-barloader-percentage {
180+
align-self: flex-start;
181+
margin-inline-start: var(--ac-global-dimension-size-150);
182+
}
183+
184+
.ac-barloader-track {
185+
background-color: var(
186+
--mod-barloader-track-color,
187+
var(--ac-barloader-track-color-default)
188+
);
189+
min-inline-size: var(--ac-global-dimension-static-size-600);
190+
height: var(--ac-global-dimension-size-75);
191+
border-radius: var(--ac-barloader-large-border-radius);
192+
z-index: 1;
193+
inline-size: 100%;
194+
overflow: hidden;
195+
}
196+
197+
.ac-barloader-fill {
198+
background: var(--mod-barloader-fill-color, var(--ac-global-color-primary));
199+
height: var(--ac-global-dimension-size-75);
200+
201+
border: none;
202+
transition: width 1s;
203+
}
204+
`;

src/provider/GlobalStyles.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ const staticCSS = css`
6969
--ac-global-static-color-white-900: rgba(255, 255, 255, 0.9);
7070
--ac-global-static-color-white-700: rgba(255, 255, 255, 0.7);
7171
--ac-global-static-color-white-300: rgba(255, 255, 255, 0.3);
72+
--ac-global-static-color-black-900: rgba(0, 0, 0, 0.9);
73+
--ac-global-static-color-black-700: rgba(0, 0, 0, 0.7);
74+
--ac-global-static-color-black-300: rgba(0, 0, 0, 0.3);
7275
}
7376
`;
7477

@@ -132,6 +135,25 @@ const dimensionsCSS = css`
132135
--ac-global-dimension-static-grid-columns: 12;
133136
--ac-global-dimension-static-grid-fluid-width: 100%;
134137
--ac-global-dimension-static-grid-fixed-max-width: 1280px;
138+
139+
/* Font sizing */
140+
--ac-global-dimension-font-size-25: 10px;
141+
--ac-global-dimension-font-size-50: 11px;
142+
--ac-global-dimension-font-size-75: 12px;
143+
--ac-global-dimension-font-size-100: 14px;
144+
--ac-global-dimension-font-size-150: 15px;
145+
--ac-global-dimension-font-size-200: 16px;
146+
--ac-global-dimension-font-size-300: 18px;
147+
--ac-global-dimension-font-size-400: 20px;
148+
--ac-global-dimension-font-size-500: 22px;
149+
--ac-global-dimension-font-size-600: 25px;
150+
--ac-global-dimension-font-size-700: 28px;
151+
--ac-global-dimension-font-size-800: 32px;
152+
--ac-global-dimension-font-size-900: 36px;
153+
--ac-global-dimension-font-size-1000: 40px;
154+
--ac-global-dimension-font-size-1100: 45px;
155+
--ac-global-dimension-font-size-1200: 50px;
156+
--ac-global-dimension-font-size-1300: 60px;
135157
}
136158
`;
137159

src/types/progress.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import { ReactNode } from 'react';
2+
import { AriaLabelingProps, DOMProps } from './dom';
3+
import { LabelPosition } from './labelable';
4+
import { StyleProps } from './style';
5+
16
export interface ProgressBaseProps {
27
/**
38
* The current value (controlled).
@@ -15,3 +20,56 @@ export interface ProgressBaseProps {
1520
*/
1621
maxValue?: number;
1722
}
23+
24+
export interface ProgressBarBaseProps extends ProgressBaseProps {
25+
/** The content to display as the label. */
26+
label?: ReactNode;
27+
/**
28+
* The display format of the value label.
29+
* @default {style: 'percent'}
30+
*/
31+
formatOptions?: Intl.NumberFormatOptions;
32+
/** The content to display as the value's label (e.g. 1 of 4). */
33+
valueLabel?: ReactNode;
34+
}
35+
36+
export interface AriaProgressBarBaseProps
37+
extends ProgressBarBaseProps,
38+
DOMProps,
39+
AriaLabelingProps {}
40+
41+
export interface ProgressBarProps extends ProgressBarBaseProps {
42+
/**
43+
* Whether presentation is indeterminate when progress isn't known.
44+
*/
45+
isIndeterminate?: boolean;
46+
}
47+
48+
export interface AriaProgressBarProps
49+
extends ProgressBarProps,
50+
DOMProps,
51+
AriaLabelingProps {}
52+
53+
export interface ACProgressBarBaseProps
54+
extends AriaProgressBarBaseProps,
55+
StyleProps {
56+
/**
57+
* How thick the bar should be.
58+
* @default 'L'
59+
*/
60+
size?: 'S' | 'L';
61+
/**
62+
* The label's overall position relative to the element it is labeling.
63+
* @default 'top'
64+
*/
65+
labelPosition?: LabelPosition;
66+
/** Whether the value's label is displayed. True by default if there's a label, false by default if not. */
67+
showValueLabel?: boolean;
68+
}
69+
70+
export interface ACProgressBarProps
71+
extends ACProgressBarBaseProps,
72+
ProgressBarProps {
73+
/** The static color style to apply. Useful when the button appears over a color background. */
74+
staticColor?: 'white' | 'black';
75+
}

0 commit comments

Comments
 (0)