diff --git a/assets/icons/experimental/spinner.svg b/assets/icons/experimental/spinner.svg new file mode 100644 index 00000000..e20399be --- /dev/null +++ b/assets/icons/experimental/spinner.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/experimental/Button/Button.tsx b/src/components/experimental/Button/Button.tsx index c5fb7c26..a8395901 100644 --- a/src/components/experimental/Button/Button.tsx +++ b/src/components/experimental/Button/Button.tsx @@ -5,7 +5,7 @@ import { Button as BaseButton, ButtonProps as BaseButtonProps } from 'react-aria import { getSemanticValue } from '../../../essentials/experimental/cssVariables'; import { get } from '../../../utils/experimental/themeGet'; import { textStyles } from '../Text/Text'; -import { InlineSpinner } from '../../InlineSpinner/InlineSpinner'; +import { InlineSpinner } from '../InlineSpinner/InlineSpinner'; type Emphasis = 'primary' | 'secondary' | 'textButton'; @@ -129,7 +129,11 @@ const spinnerColor: Record = { function Button({ children, emphasis = 'primary', isLoading = false, ...restProps }: ButtonProps): ReactElement { return ( - {isLoading ? : children} + {isLoading ? ( + + ) : ( + children + )} ); } diff --git a/src/components/experimental/IconButton/IconButton.tsx b/src/components/experimental/IconButton/IconButton.tsx index 41b83e60..b9b9f7d9 100644 --- a/src/components/experimental/IconButton/IconButton.tsx +++ b/src/components/experimental/IconButton/IconButton.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import { ButtonProps, Button } from 'react-aria-components'; import { IconProps } from '../../../icons'; import { getSemanticValue } from '../../../essentials/experimental'; -import { InlineSpinner } from '../../InlineSpinner/InlineSpinner'; +import { InlineSpinner } from '../InlineSpinner/InlineSpinner'; export interface IconButtonProps extends ButtonProps { isActive?: boolean; @@ -123,7 +123,7 @@ export const IconButton = ({ {...restProps} > {isLoading ? ( - + ) : ( )} diff --git a/src/components/experimental/InlineSpinner/InlineSpinner.tsx b/src/components/experimental/InlineSpinner/InlineSpinner.tsx new file mode 100644 index 00000000..e64cb10c --- /dev/null +++ b/src/components/experimental/InlineSpinner/InlineSpinner.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import styled, { keyframes } from 'styled-components'; +import { compose, variant } from 'styled-system'; +import SpinnerIcon from '../../../icons/experimental/SpinnerIcon'; +import { getSemanticValue } from '../../../essentials/experimental'; + +interface InlineSpinnerProps { + /** + * Override the color of the spinner + */ + color?: string; + /** + * Set the size of the component + */ + size?: 'small' | 'medium' | 'large'; +} + +const sizeVariant = variant({ + prop: 'size', + variants: { + small: { + width: '1rem', + height: '1rem' + }, + medium: { + width: '1.25rem', + height: '1.25rem' + }, + large: { + width: '2.5rem', + height: '2.5rem' + } + } +}); + +const rotation = keyframes` + to { + transform: rotate(360deg); + } +`; + +const Wrapper = styled.span` + display: inline-flex; + box-sizing: border-box; + vertical-align: text-bottom; + + ${compose(sizeVariant)} +`; + +const Icon = styled(SpinnerIcon)` + width: 100%; + height: 100%; + + animation: ${rotation} 750ms linear infinite; +`; + +const InlineSpinner: React.FC = ({ + color = getSemanticValue('interactive'), + size = 'medium', + ...rest +}: InlineSpinnerProps) => ( + + + +); + +export { InlineSpinner, InlineSpinnerProps }; diff --git a/src/components/experimental/InlineSpinner/docs/InlineSpinner.stories.tsx b/src/components/experimental/InlineSpinner/docs/InlineSpinner.stories.tsx new file mode 100644 index 00000000..35bdb4ed --- /dev/null +++ b/src/components/experimental/InlineSpinner/docs/InlineSpinner.stories.tsx @@ -0,0 +1,22 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { InlineSpinner } from '../InlineSpinner'; + +const meta: Meta = { + title: 'Experimental/Components/InlineSpinner', + component: InlineSpinner, + parameters: { + layout: 'centered' + }, + argTypes: { + size: { + control: 'radio', + options: ['large', 'medium', 'small'] + } + } +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/src/components/experimental/index.ts b/src/components/experimental/index.ts index d652ba5b..53a27aa4 100644 --- a/src/components/experimental/index.ts +++ b/src/components/experimental/index.ts @@ -7,6 +7,7 @@ export { DatePicker } from './DatePicker/DatePicker'; export { Dialog } from './Dialog/Dialog'; export { Divider } from './Divider/Divider'; export { IconButton } from './IconButton/IconButton'; +export { InlineSpinner } from './InlineSpinner/InlineSpinner'; export { Label } from './Label/Label'; export { ListBox, ListBoxItem } from './ListBox/ListBox'; export { Popover } from './Popover/Popover'; diff --git a/src/icons/experimental/SpinnerIcon.tsx b/src/icons/experimental/SpinnerIcon.tsx new file mode 100644 index 00000000..7934342c --- /dev/null +++ b/src/icons/experimental/SpinnerIcon.tsx @@ -0,0 +1,27 @@ +// DO NOT EDIT. This file was generated by running `npm run generate`.; +import * as React from 'react'; +import { get } from '../../utils/themeGet'; +import { IconProps } from '../IconProps'; +type Props = IconProps; +const SpinnerIcon: React.FC = ({ size = 'medium', color = 'inherit', ...rest }) => { + const props = { ...rest, color }; + const sizePx = Number.isFinite(size as number) + ? size + : get(`iconSizes.${size}`)(props) || get('iconSizes.medium')(props); + return ( + + + + ); +}; +export default SpinnerIcon; diff --git a/src/icons/experimental/index.ts b/src/icons/experimental/index.ts index 74db7afb..7af1405e 100644 --- a/src/icons/experimental/index.ts +++ b/src/icons/experimental/index.ts @@ -1,5 +1,6 @@ export { default as CalendarTodayOutlineIcon } from './CalendarTodayOutlineIcon'; -export { default as AccountOutlineIcon } from "./AccountOutlineIcon"; -export { default as CarGroupOutlineIcon } from "./CarGroupOutlineIcon"; -export { default as CarOutlineIcon } from "./CarOutlineIcon"; -export { default as LocationIcon } from "./LocationIcon"; +export { default as AccountOutlineIcon } from './AccountOutlineIcon'; +export { default as CarGroupOutlineIcon } from './CarGroupOutlineIcon'; +export { default as CarOutlineIcon } from './CarOutlineIcon'; +export { default as LocationIcon } from './LocationIcon'; +export { default as SpinnerIcon } from './SpinnerIcon';