From 72ec2f5ddc6fba5fd6bf1205aea0c99652161a8e Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Thu, 22 Aug 2024 12:20:22 -0700 Subject: [PATCH] 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 --- packages/@react-spectrum/s2/src/Meter.tsx | 5 ++- .../@react-spectrum/s2/src/ProgressBar.tsx | 28 ++++++++++--- packages/@react-spectrum/s2/src/bar-utils.ts | 41 +++++++++++++++---- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/packages/@react-spectrum/s2/src/Meter.tsx b/packages/@react-spectrum/s2/src/Meter.tsx index 7b37291e2c7..507b8ac8d9a 100644 --- a/packages/@react-spectrum/s2/src/Meter.tsx +++ b/packages/@react-spectrum/s2/src/Meter.tsx @@ -46,7 +46,7 @@ export interface MeterProps extends Omit>>(null); -const wrapper = style({ +const wrapper = style({ ...bar(), width: { default: 208, @@ -123,7 +123,8 @@ function Meter(props: MeterProps, ref: DOMRef) { className={UNSAFE_className + wrapper({ size, variant, - staticColor + staticColor, + labelPosition: 'top' }, styles)}> {({percentage, valueText}) => ( <> diff --git a/packages/@react-spectrum/s2/src/ProgressBar.tsx b/packages/@react-spectrum/s2/src/ProgressBar.tsx index 9c9b4949c27..87319699f61 100644 --- a/packages/@react-spectrum/s2/src/ProgressBar.tsx +++ b/packages/@react-spectrum/s2/src/ProgressBar.tsx @@ -17,7 +17,7 @@ import { } from 'react-aria-components'; import {bar, track} from './bar-utils' with {type: 'macro'}; import {createContext, forwardRef, ReactNode} from 'react'; -import {DOMRef, DOMRefValue} from '@react-types/shared'; +import {DOMRef, DOMRefValue, LabelPosition} from '@react-types/shared'; import {FieldLabel} from './Field'; import {fieldLabel, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; import {keyframes} from '../style/style-macro' with {type: 'macro'}; @@ -37,8 +37,16 @@ interface ProgressBarStyleProps { * Whether presentation is indeterminate when progress isn't known. */ isIndeterminate?: boolean, - /** The static color style to apply. Useful when the button appears over a color background. */ - staticColor?: 'white' | 'black' + /** + * The static color style to apply. Useful when the button appears over a color background. + */ + staticColor?: 'white' | 'black', + /** + * The label's overall position relative to the element it is labeling. + * @default 'top' + */ + labelPosition?: LabelPosition + } export interface ProgressBarProps extends Omit, ProgressBarStyleProps, StyleProps { @@ -89,6 +97,7 @@ const trackStyles = style({ const fill = style({ height: 'full', borderStyle: 'none', + borderRadius: 'full', backgroundColor: { default: 'accent', staticColor: { @@ -117,17 +126,24 @@ const indeterminateAnimation = style({ function ProgressBar(props: ProgressBarProps, ref: DOMRef) { [props, ref] = useSpectrumContextProps(props, ref, ProgressBarContext); - let {label, size = 'M', staticColor, isIndeterminate, UNSAFE_style, UNSAFE_className = ''} = props; + let { + label, size = 'M', + staticColor, + isIndeterminate, + labelPosition = 'top', + UNSAFE_style, + UNSAFE_className = '' + } = props; let domRef = useDOMRef(ref); return ( + className={UNSAFE_className + wrapper({...props, size, labelPosition}, props.styles)}> {({percentage, valueText}) => ( <> - {label && {label}} + {label && {label}} {label && {valueText}}
({ position: 'relative', display: 'grid', - gridTemplateColumns: '1fr auto', - gridTemplateAreas: [ - 'label value', - 'bar bar' - ], + gridTemplateColumns: { + labelPosition: { + top: ['1fr', 'auto'], + side: ['auto', '1fr'] + } + }, + gridTemplateAreas: { + labelPosition: { + top: [ + 'label value', + 'bar bar', + ], + side: [ + 'label bar value' + ] + } + }, + alignItems: 'baseline', isolation: 'isolate', minWidth: 48, // progress-bar-minimum-width maxWidth: '[768px]', // progress-bar-maximum-width - minHeight: 'control', + '--field-height': { + type: 'height', + value: 'control' + }, + '--track-to-label': { + type: 'height', + value: 4 + }, + // Spectrum defines the field label/help text with a (minimum) height, with text centered inside. + // Calculate what the gap should be based on the height and line height. + // Use a variable here rather than rowGap since it is applied to the children as padding. + // This allows the gap to collapse when the label/help text is not present. + // Eventually this may be possible to do in pure CSS: https://github.com/w3c/csswg-drafts/issues/5813 '--field-gap': { type: 'rowGap', - value: centerPadding() + value: centerPadding('calc(var(--field-height) + var(--track-to-label))') }, + columnGap: 12 // spacing-200 } as const); export const track = () => ({ gridArea: 'bar', overflow: 'hidden', - marginTop: 4, borderRadius: 'full', backgroundColor: { default: 'gray-300',