Skip to content

Commit 72ec2f5

Browse files
authored
feat: add rounded edges and side label to S2 progress bar (#6918)
* feat: add rounded edges and side label to progress bar * only add margin if label exists
1 parent b4093df commit 72ec2f5

File tree

3 files changed

+58
-16
lines changed

3 files changed

+58
-16
lines changed

packages/@react-spectrum/s2/src/Meter.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export interface MeterProps extends Omit<AriaMeterProps, 'children' | 'className
4646

4747
export const MeterContext = createContext<ContextValue<MeterProps, DOMRefValue<HTMLDivElement>>>(null);
4848

49-
const wrapper = style<MeterStyleProps>({
49+
const wrapper = style({
5050
...bar(),
5151
width: {
5252
default: 208,
@@ -123,7 +123,8 @@ function Meter(props: MeterProps, ref: DOMRef<HTMLDivElement>) {
123123
className={UNSAFE_className + wrapper({
124124
size,
125125
variant,
126-
staticColor
126+
staticColor,
127+
labelPosition: 'top'
127128
}, styles)}>
128129
{({percentage, valueText}) => (
129130
<>

packages/@react-spectrum/s2/src/ProgressBar.tsx

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from 'react-aria-components';
1818
import {bar, track} from './bar-utils' with {type: 'macro'};
1919
import {createContext, forwardRef, ReactNode} from 'react';
20-
import {DOMRef, DOMRefValue} from '@react-types/shared';
20+
import {DOMRef, DOMRefValue, LabelPosition} from '@react-types/shared';
2121
import {FieldLabel} from './Field';
2222
import {fieldLabel, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
2323
import {keyframes} from '../style/style-macro' with {type: 'macro'};
@@ -37,8 +37,16 @@ interface ProgressBarStyleProps {
3737
* Whether presentation is indeterminate when progress isn't known.
3838
*/
3939
isIndeterminate?: boolean,
40-
/** The static color style to apply. Useful when the button appears over a color background. */
41-
staticColor?: 'white' | 'black'
40+
/**
41+
* The static color style to apply. Useful when the button appears over a color background.
42+
*/
43+
staticColor?: 'white' | 'black',
44+
/**
45+
* The label's overall position relative to the element it is labeling.
46+
* @default 'top'
47+
*/
48+
labelPosition?: LabelPosition
49+
4250
}
4351

4452
export interface ProgressBarProps extends Omit<AriaProgressBarProps, 'children' | 'className' | 'style'>, ProgressBarStyleProps, StyleProps {
@@ -89,6 +97,7 @@ const trackStyles = style({
8997
const fill = style<ProgressBarStyleProps>({
9098
height: 'full',
9199
borderStyle: 'none',
100+
borderRadius: 'full',
92101
backgroundColor: {
93102
default: 'accent',
94103
staticColor: {
@@ -117,17 +126,24 @@ const indeterminateAnimation = style({
117126

118127
function ProgressBar(props: ProgressBarProps, ref: DOMRef<HTMLDivElement>) {
119128
[props, ref] = useSpectrumContextProps(props, ref, ProgressBarContext);
120-
let {label, size = 'M', staticColor, isIndeterminate, UNSAFE_style, UNSAFE_className = ''} = props;
129+
let {
130+
label, size = 'M',
131+
staticColor,
132+
isIndeterminate,
133+
labelPosition = 'top',
134+
UNSAFE_style,
135+
UNSAFE_className = ''
136+
} = props;
121137
let domRef = useDOMRef(ref);
122138
return (
123139
<AriaProgressBar
124140
{...props}
125141
ref={domRef}
126142
style={UNSAFE_style}
127-
className={UNSAFE_className + wrapper({...props, size}, props.styles)}>
143+
className={UNSAFE_className + wrapper({...props, size, labelPosition}, props.styles)}>
128144
{({percentage, valueText}) => (
129145
<>
130-
{label && <FieldLabel size={size} labelAlign="start" labelPosition="top" staticColor={staticColor}>{label}</FieldLabel>}
146+
{label && <FieldLabel size={size} labelAlign="start" labelPosition={labelPosition} staticColor={staticColor}>{label}</FieldLabel>}
131147
{label && <span className={valueStyles({size, labelAlign: 'end', staticColor})}>{valueText}</span>}
132148
<div className={trackStyles({...props})}>
133149
<div

packages/@react-spectrum/s2/src/bar-utils.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,51 @@ import {centerPadding} from './style-utils' with {type: 'macro'};
1515
export const bar = () => ({
1616
position: 'relative',
1717
display: 'grid',
18-
gridTemplateColumns: '1fr auto',
19-
gridTemplateAreas: [
20-
'label value',
21-
'bar bar'
22-
],
18+
gridTemplateColumns: {
19+
labelPosition: {
20+
top: ['1fr', 'auto'],
21+
side: ['auto', '1fr']
22+
}
23+
},
24+
gridTemplateAreas: {
25+
labelPosition: {
26+
top: [
27+
'label value',
28+
'bar bar',
29+
],
30+
side: [
31+
'label bar value'
32+
]
33+
}
34+
},
35+
alignItems: 'baseline',
2336
isolation: 'isolate',
2437
minWidth: 48, // progress-bar-minimum-width
2538
maxWidth: '[768px]', // progress-bar-maximum-width
26-
minHeight: 'control',
39+
'--field-height': {
40+
type: 'height',
41+
value: 'control'
42+
},
43+
'--track-to-label': {
44+
type: 'height',
45+
value: 4
46+
},
47+
// Spectrum defines the field label/help text with a (minimum) height, with text centered inside.
48+
// Calculate what the gap should be based on the height and line height.
49+
// Use a variable here rather than rowGap since it is applied to the children as padding.
50+
// This allows the gap to collapse when the label/help text is not present.
51+
// Eventually this may be possible to do in pure CSS: https://github.com/w3c/csswg-drafts/issues/5813
2752
'--field-gap': {
2853
type: 'rowGap',
29-
value: centerPadding()
54+
value: centerPadding('calc(var(--field-height) + var(--track-to-label))')
3055
},
56+
3157
columnGap: 12 // spacing-200
3258
} as const);
3359

3460
export const track = () => ({
3561
gridArea: 'bar',
3662
overflow: 'hidden',
37-
marginTop: 4,
3863
borderRadius: 'full',
3964
backgroundColor: {
4065
default: 'gray-300',

0 commit comments

Comments
 (0)