Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: progress bar #220

Merged
merged 3 commits into from
Jun 19, 2024
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.3.7",
"version": "1.4.0",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down
31 changes: 31 additions & 0 deletions src/progress/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { DOMRef } from '@react-types/shared';
import React from 'react';
import { useProgressBar } from '@react-aria/progress';
import { ACProgressBarProps } from '../types';
import { classNames } from '../utils';
import { ProgressBarBase } from './ProgressBarBase';

function ProgressBar(props: ACProgressBarProps, ref: DOMRef<HTMLDivElement>) {
let { staticColor, ...otherProps } = props;
const { progressBarProps, labelProps } = useProgressBar(props);

return (
<ProgressBarBase
{...otherProps}
ref={ref}
barProps={progressBarProps}
labelProps={labelProps}
barClassName={classNames({
'ac-barloader--static-white': staticColor === 'white',
'ac-barloader--static-black': staticColor === 'black',
})}
/>
);
}

/**
* ProgressBars show the progression of a system operation: downloading, uploading, processing, etc., in a visual way.
* They can represent either determinate or indeterminate progress.
*/
let _ProgressBar = React.forwardRef(ProgressBar);
export { _ProgressBar as ProgressBar };
91 changes: 91 additions & 0 deletions src/progress/ProgressBarBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { clamp } from '@react-aria/utils';
import React, { CSSProperties, HTMLAttributes } from 'react';
import { DOMRef, ProgressBarProps, ACProgressBarBaseProps } from '../types';
import { classNames, useDOMRef, useStyleProps } from '../utils';
import { progressBarCSS } from './styles';
interface ProgressBarBaseProps
extends ACProgressBarBaseProps,
ProgressBarProps {
barClassName?: string;
barProps?: HTMLAttributes<HTMLDivElement>;
labelProps?: HTMLAttributes<HTMLLabelElement>;
}

// Base ProgressBar component shared with Meter.
function ProgressBarBase(
props: ProgressBarBaseProps,
ref: DOMRef<HTMLDivElement>
) {
let {
value = 0,
minValue = 0,
maxValue = 100,
size = 'L',
label,
barClassName,
showValueLabel = !!label,
labelPosition = 'top',
isIndeterminate = false,
barProps,
labelProps,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledby,
...otherProps
} = props;
let domRef = useDOMRef(ref);
let { styleProps } = useStyleProps(otherProps);

value = clamp(value, minValue, maxValue);

let barStyle: CSSProperties = {};
if (!isIndeterminate) {
let percentage = (value - minValue) / (maxValue - minValue);
barStyle.width = `${Math.round(percentage * 100)}%`;
}

// Ideally this should be in useProgressBar, but children
// are not supported in ProgressCircle which shares that hook...
if (!label && !ariaLabel && !ariaLabelledby) {
// eslint-disable-next-line no-console
console.warn(
'If you do not provide a visible label via children, you must specify an aria-label or aria-labelledby attribute for accessibility'
);
}
// use inline style for fit-content because cssnano is too smart for us and will strip out the -moz prefix in css files
return (
<div
{...barProps}
ref={domRef}
className={classNames(
'ac-barloader',
{
'ac-barloader--small': size === 'S',
'ac-barloader--large': size === 'L',
'ac-barloader--indeterminate': isIndeterminate,
'ac-barloader--sideLabel': labelPosition === 'side',
},
barClassName,
styleProps.className
)}
css={progressBarCSS}
style={{ minWidth: '-moz-fit-content', ...styleProps.style }}
>
{label && (
<span {...labelProps} className="ac-barloader-label">
{label}
</span>
)}
{showValueLabel && barProps && (
<div className={'ac-barloader-percentage'}>
{barProps['aria-valuetext']}
</div>
)}
<div className={'ac-barloader-track'}>
<div className={'ac-barloader-fill'} style={barStyle} />
</div>
</div>
);
}

let _ProgressBarBase = React.forwardRef(ProgressBarBase);
export { _ProgressBarBase as ProgressBarBase };
1 change: 1 addition & 0 deletions src/progress/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './ProgressCircle';
export * from './ProgressBar';
80 changes: 80 additions & 0 deletions src/progress/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,83 @@ export const progressCircleCSS = css`
}
}
`;

export const progressBarCSS = css`
--ac-barloader-large-border-radius: 3px;
--ac-barloader-track-color-default: var(--ac-global-color-grey-300);
&.ac-barloader {
--ac-barloader-large-track-fill-color: var(--ac-global-color-primary);
--ac-barloader-static-black-track-color: #00000040;
--ac-barloader-static-black-fill-color: var(
--ac-global-static-color-black-900
);

min-inline-size: var(--ac-global-dimension-static-size-600, 48px);
inline-size: var(--ac-global-dimension-size-2400);
vertical-align: top;
isolation: isolate;
flex-flow: wrap;
justify-content: space-between;
align-items: center;
display: inline-flex;
position: relative;
}

&.ac-barloader--static-white {
--mod-barloader-label-and-value-color: var(
--ac-global-static-color-white-900
);
--mod-barloader-fill-color: var(--ac-global-color-white-900);
}
&.ac-barloader--static-black {
--mod-barloader-label-and-value-color: var(
--ac-global-static-color-black-900
);
--mod-barloader-fill-color: var(--ac-global-static-color-black-900);
--mod-barloader-track-color: var(--ac-barloader-static-black-track-color);
}

.ac-barloader-label,
.ac-barloader-percentage {
color: var(
--mod-barloader-label-and-value-color,
var(--ac-global-text-color-900)
);
font-size: var(--spectrum-global-dimension-font-size-75);
font-weight: var(--spectrum-global-font-weight-regular);
line-height: var(--spectrum-global-font-line-height-small);
text-align: start;
text-align: start;
margin-bottom: var(--ac-global-dimension-size-115);
}

.ac-barloader-label {
flex: 1;
}

.ac-barloader-percentage {
align-self: flex-start;
margin-inline-start: var(--ac-global-dimension-size-150);
}

.ac-barloader-track {
background-color: var(
--mod-barloader-track-color,
var(--ac-barloader-track-color-default)
);
min-inline-size: var(--ac-global-dimension-static-size-600);
height: var(--ac-global-dimension-size-75);
border-radius: var(--ac-barloader-large-border-radius);
z-index: 1;
inline-size: 100%;
overflow: hidden;
}

.ac-barloader-fill {
background: var(--mod-barloader-fill-color, var(--ac-global-color-primary));
height: var(--ac-global-dimension-size-75);

border: none;
transition: width 1s;
}
`;
22 changes: 22 additions & 0 deletions src/provider/GlobalStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ const staticCSS = css`
--ac-global-static-color-white-900: rgba(255, 255, 255, 0.9);
--ac-global-static-color-white-700: rgba(255, 255, 255, 0.7);
--ac-global-static-color-white-300: rgba(255, 255, 255, 0.3);
--ac-global-static-color-black-900: rgba(0, 0, 0, 0.9);
--ac-global-static-color-black-700: rgba(0, 0, 0, 0.7);
--ac-global-static-color-black-300: rgba(0, 0, 0, 0.3);
}
`;

Expand Down Expand Up @@ -132,6 +135,25 @@ const dimensionsCSS = css`
--ac-global-dimension-static-grid-columns: 12;
--ac-global-dimension-static-grid-fluid-width: 100%;
--ac-global-dimension-static-grid-fixed-max-width: 1280px;

/* Font sizing */
--ac-global-dimension-font-size-25: 10px;
--ac-global-dimension-font-size-50: 11px;
--ac-global-dimension-font-size-75: 12px;
--ac-global-dimension-font-size-100: 14px;
--ac-global-dimension-font-size-150: 15px;
--ac-global-dimension-font-size-200: 16px;
--ac-global-dimension-font-size-300: 18px;
--ac-global-dimension-font-size-400: 20px;
--ac-global-dimension-font-size-500: 22px;
--ac-global-dimension-font-size-600: 25px;
--ac-global-dimension-font-size-700: 28px;
--ac-global-dimension-font-size-800: 32px;
--ac-global-dimension-font-size-900: 36px;
--ac-global-dimension-font-size-1000: 40px;
--ac-global-dimension-font-size-1100: 45px;
--ac-global-dimension-font-size-1200: 50px;
--ac-global-dimension-font-size-1300: 60px;
}
`;

Expand Down
58 changes: 58 additions & 0 deletions src/types/progress.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { ReactNode } from 'react';
import { AriaLabelingProps, DOMProps } from './dom';
import { LabelPosition } from './labelable';
import { StyleProps } from './style';

export interface ProgressBaseProps {
/**
* The current value (controlled).
Expand All @@ -15,3 +20,56 @@ export interface ProgressBaseProps {
*/
maxValue?: number;
}

export interface ProgressBarBaseProps extends ProgressBaseProps {
/** The content to display as the label. */
label?: ReactNode;
/**
* The display format of the value label.
* @default {style: 'percent'}
*/
formatOptions?: Intl.NumberFormatOptions;
/** The content to display as the value's label (e.g. 1 of 4). */
valueLabel?: ReactNode;
}

export interface AriaProgressBarBaseProps
extends ProgressBarBaseProps,
DOMProps,
AriaLabelingProps {}

export interface ProgressBarProps extends ProgressBarBaseProps {
/**
* Whether presentation is indeterminate when progress isn't known.
*/
isIndeterminate?: boolean;
}

export interface AriaProgressBarProps
extends ProgressBarProps,
DOMProps,
AriaLabelingProps {}

export interface ACProgressBarBaseProps
extends AriaProgressBarBaseProps,
StyleProps {
/**
* How thick the bar should be.
* @default 'L'
*/
size?: 'S' | 'L';
/**
* The label's overall position relative to the element it is labeling.
* @default 'top'
*/
labelPosition?: LabelPosition;
/** Whether the value's label is displayed. True by default if there's a label, false by default if not. */
showValueLabel?: boolean;
}

export interface ACProgressBarProps
extends ACProgressBarBaseProps,
ProgressBarProps {
/** The static color style to apply. Useful when the button appears over a color background. */
staticColor?: 'white' | 'black';
}
Loading
Loading