fix: update spinner to use svg instead

This commit is contained in:
Tobias Johansson
2025-04-24 11:32:54 +02:00
committed by Simon Emanuelsson
parent f56a1ece0f
commit d0c6d1d875
5 changed files with 69 additions and 155 deletions

View File

@@ -5,6 +5,7 @@ import { variants } from './variants'
import type { ButtonProps } from './types'
import { Spinner } from '../Spinner'
import styles from './button.module.css'
import { SpinnerProps } from '../Spinner/Spinner'
export function Button({
variant,
@@ -15,7 +16,6 @@ export function Button({
typography,
className,
children,
isPending,
...props
}: ButtonProps) {
const classNames = variants({
@@ -27,15 +27,28 @@ export function Button({
className,
})
let spinnerType: SpinnerProps['type'] = 'Dark'
switch (color) {
case 'Primary':
spinnerType = 'Dark'
break
case 'Inverted':
spinnerType = 'White'
break
default:
spinnerType = 'Dark'
}
return (
<ButtonRAC {...props} className={classNames} isPending={isPending}>
<ButtonRAC {...props} className={classNames}>
{({ isPending }) => {
return (
<>
{children}
{isPending && (
<div className={styles.spinnerWrapper}>
<Spinner size="Small" color="CurrentColor" />
<Spinner size={20} type={spinnerType} />
</div>
)}
</>

View File

@@ -7,98 +7,35 @@ import { Spinner } from './Spinner'
const meta: Meta<typeof Spinner> = {
title: 'Components/Spinner',
component: Spinner,
argTypes: {},
argTypes: {
type: {
control: 'text',
},
size: {
control: 'number',
},
},
}
export default meta
type Story = StoryObj<typeof Spinner>
function Wrapper({
children,
backgroundColor,
}: {
children: React.ReactNode
backgroundColor?: string
}) {
return (
<div
style={{
height: '200px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor,
}}
>
{children}
</div>
)
export const Dark: Story = {
args: {
type: 'Dark',
size: 40,
},
}
export const Default: Story = {
export const White: Story = {
args: {
color: 'Accent',
size: 'Medium',
type: 'White',
size: 40,
},
decorators: [
(Story) => (
<Wrapper>
<Story />
</Wrapper>
),
],
}
export const Inverted: Story = {
args: {
color: 'Inverted',
size: 'Medium',
parameters: {
backgrounds: {
default: 'Scandic Primary Dark',
},
},
decorators: [
(Story) => (
<Wrapper backgroundColor="var(--Icon-Interactive-Default)">
<Story />
</Wrapper>
),
],
}
export const Small: Story = {
args: {
size: 'Small',
},
decorators: [
(Story) => (
<Wrapper>
<Story />
</Wrapper>
),
],
}
export const Medium: Story = {
args: {
size: 'Medium',
},
decorators: [
(Story) => (
<Wrapper>
<Story />
</Wrapper>
),
],
}
export const Large: Story = {
args: {
size: 'Large',
},
decorators: [
(Story) => (
<Wrapper>
<Story />
</Wrapper>
),
],
}

View File

@@ -1,21 +1,34 @@
import { VariantProps } from 'class-variance-authority'
import styles from './spinner.module.css'
import { VariantProps } from 'class-variance-authority'
import { variants } from './variants'
type SpinnerProps = VariantProps<typeof variants>
export interface SpinnerProps extends VariantProps<typeof variants> {
size?: number
}
export function Spinner({ color, size }: SpinnerProps) {
export function Spinner({ type, size = 20 }: SpinnerProps) {
const classNames = variants({
color,
size,
type,
})
return (
<div className={classNames}>
{[...Array(8)].map((_, i) => (
<div key={i} className={styles.dot} />
))}
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 20 21"
fill="none"
className={classNames}
>
<circle className={styles.dot} cx="10" cy="2.64147" r="1.73913" />
<circle className={styles.dot} cx="16.087" cy="5.25018" r="1.73913" />
<circle className={styles.dot} cx="18.2609" cy="10.9023" r="1.73913" />
<circle className={styles.dot} cx="16.087" cy="16.5545" r="1.73913" />
<circle className={styles.dot} cx="10" cy="19.1632" r="1.73913" />
<circle className={styles.dot} cx="3.91304" cy="16.5545" r="1.73913" />
<circle className={styles.dot} cx="1.73913" cy="10.9023" r="1.73913" />
<circle className={styles.dot} cx="3.91304" cy="5.25018" r="1.73913" />
</svg>
)
}

View File

@@ -1,84 +1,42 @@
.spinner {
display: inline-block;
position: relative;
--size: 20px;
--dot-size: 3px;
width: var(--size);
height: var(--size);
}
.size-small {
--size: 20px;
}
.size-medium {
--size: 30px;
--dot-size: 5px;
}
.size-large {
--size: 40px;
--dot-size: 6px;
}
.spinner .dot {
transform-origin: calc(var(--size) / 2) calc(var(--size) / 2);
.dot {
animation: spinnerAnimation 0.8s linear infinite;
transform-origin: center;
}
.spinner .dot::after {
content: ' ';
display: block;
position: absolute;
top: calc(var(--dot-size) / 2);
left: var(--dot-size);
width: var(--dot-size);
height: var(--dot-size);
border-radius: 50%;
background-color: currentColor;
.dark .dot {
fill: var(--Icon-Interactive-Default);
}
.accent .dot::after {
background-color: var(--Icon-Interactive-Default);
}
.inverted .dot::after {
background-color: var(--Icon-Inverted);
.white .dot {
fill: var(--Icon-Inverted);
}
.dot:nth-child(1) {
transform: rotate(0deg);
animation-delay: -0.7s;
}
.dot:nth-child(2) {
transform: rotate(45deg);
animation-delay: -0.6s;
}
.dot:nth-child(3) {
transform: rotate(90deg);
animation-delay: -0.5s;
}
.dot:nth-child(4) {
transform: rotate(135deg);
animation-delay: -0.4s;
}
.dot:nth-child(5) {
transform: rotate(180deg);
animation-delay: -0.3s;
}
.dot:nth-child(6) {
transform: rotate(225deg);
animation-delay: -0.2s;
}
.dot:nth-child(7) {
transform: rotate(270deg);
animation-delay: -0.1s;
}
.dot:nth-child(8) {
transform: rotate(315deg);
animation-delay: 0s;
}

View File

@@ -4,20 +4,13 @@ import styles from './spinner.module.css'
export const config = {
variants: {
color: {
Accent: styles.accent,
Inverted: styles.inverted,
CurrentColor: 'currentColor',
},
size: {
Small: styles['size-small'],
Medium: styles['size-medium'],
Large: styles['size-large'],
type: {
Dark: styles.dark,
White: styles.white,
},
},
defaultVariants: {
color: 'Accent',
size: 'Medium',
type: 'Dark',
},
} as const