Skip to content

Commit 31f334e

Browse files
committed
✨ Add Spinner component
1 parent c338626 commit 31f334e

File tree

9 files changed

+266
-1
lines changed

9 files changed

+266
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Full documentation coming soon on [webcoreui.dev/docs](https://webcoreui.dev/doc
4343

4444
## Getting Started
4545

46-
Webcore can be used as a standalone project, or it can be integrated into your existing Astro, Svelte, or React ecosystems.
46+
Webcore can be used as a standalone project, or it can be integrated into your existing Astro, Svelte, or React ecosystems. The easiest way to get started is to clone the repository and run `npm run dev` to start building your pages with the components available.
4747

4848
### Prerequisites
4949

@@ -154,5 +154,6 @@ import { Accordion } from 'webcoreui/react'
154154
- [Icon](https://github.com/Frontendland/webcoreui/tree/main/src/components/Icon)
155155
- [Radio](https://github.com/Frontendland/webcoreui/tree/main/src/components/Radio)
156156
- [Rating](https://github.com/Frontendland/webcoreui/tree/main/src/components/Rating)
157+
- [Spinner](https://github.com/Frontendland/webcoreui/tree/main/src/components/Spinner)
157158
- [Switch](https://github.com/Frontendland/webcoreui/tree/main/src/components/Switch)
158159
- [Tabs](https://github.com/Frontendland/webcoreui/tree/main/src/components/Tabs)

src/components/Spinner/Spinner.astro

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
import type { SpinnerProps } from './spinner'
3+
import './spinner.scss'
4+
5+
interface Props extends SpinnerProps {}
6+
7+
const {
8+
color,
9+
width,
10+
speed,
11+
size,
12+
dashArray,
13+
} = Astro.props
14+
15+
const classes = [
16+
'w-spinner',
17+
dashArray && 'dashed'
18+
]
19+
20+
const styles = [
21+
color && `--w-spinner-color: ${color};`,
22+
width && `--w-spinner-width: ${width}px;`,
23+
speed && `--w-spinner-speed: ${speed}s;`,
24+
size && `--w-spinner-size: ${size}px;`,
25+
dashArray && `--w-spinner-dash: ${dashArray}`,
26+
].filter(Boolean).join(' ')
27+
---
28+
29+
<svg
30+
class:list={classes}
31+
viewBox="25 25 50 50"
32+
role="status"
33+
style={styles}
34+
>
35+
<circle
36+
class="spinner-path"
37+
cx="50"
38+
cy="50"
39+
r="20"
40+
fill="none"
41+
/>
42+
</svg>

src/components/Spinner/Spinner.svelte

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<script lang="ts">
2+
import type { SpinnerProps } from './spinner'
3+
import './spinner.scss'
4+
5+
export let color: SpinnerProps['color'] = ''
6+
export let width: SpinnerProps['width'] = 0
7+
export let speed: SpinnerProps['speed'] = 0
8+
export let size: SpinnerProps['size'] = 0
9+
export let dashArray: SpinnerProps['dashArray'] = 0
10+
11+
const classes = [
12+
'w-spinner',
13+
dashArray && 'dashed'
14+
].filter(Boolean).join(' ')
15+
16+
const styles = [
17+
color && `--w-spinner-color: ${color};`,
18+
width && `--w-spinner-width: ${width}px;`,
19+
speed && `--w-spinner-speed: ${speed}s;`,
20+
size && `--w-spinner-size: ${size}px;`,
21+
dashArray && `--w-spinner-dash: ${dashArray}`,
22+
].filter(Boolean).join(' ')
23+
</script>
24+
25+
<svg
26+
class={classes}
27+
viewBox="25 25 50 50"
28+
role="status"
29+
style={styles}
30+
>
31+
<circle
32+
class="spinner-path"
33+
cx="50"
34+
cy="50"
35+
r="20"
36+
fill="none"
37+
/>
38+
</svg>

src/components/Spinner/Spinner.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react'
2+
import type { SpinnerProps } from './spinner'
3+
4+
import './spinner.scss'
5+
6+
const Spinner = ({
7+
color,
8+
width,
9+
speed,
10+
size,
11+
dashArray
12+
}: SpinnerProps) => {
13+
const classes = [
14+
'w-spinner',
15+
dashArray && 'dashed'
16+
].filter(Boolean).join(' ')
17+
18+
const styles = {
19+
...(color && { '--w-spinner-color': color }),
20+
...(width && { '--w-spinner-width': `${width}px;` }),
21+
...(speed && { '--w-spinner-speed': `${speed}s;` }),
22+
...(size && { '--w-spinner-size': `${size}px;` }),
23+
...(dashArray && { '--w-spinner-dash': dashArray }),
24+
} as React.CSSProperties
25+
26+
return (
27+
<svg
28+
className={classes}
29+
viewBox="25 25 50 50"
30+
role="status"
31+
style={styles}
32+
>
33+
<circle
34+
className="spinner-path"
35+
cx="50"
36+
cy="50"
37+
r="20"
38+
fill="none"
39+
/>
40+
</svg>
41+
)
42+
}
43+
44+
export default Spinner

src/components/Spinner/spinner.scss

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@import '../../scss/config.scss';
2+
3+
.w-spinner {
4+
margin: 0px auto;
5+
width: var(--w-spinner-size);
6+
height: var(--w-spinner-size);
7+
animation: rotate var(--w-spinner-speed) linear infinite;
8+
9+
&.dashed .spinner-path {
10+
stroke-dasharray: var(--w-spinner-dash);
11+
animation: none;
12+
}
13+
14+
.spinner-path {
15+
stroke: var(--w-spinner-color);
16+
stroke-width: var(--w-spinner-width);
17+
animation: dash 1.5s ease-in-out infinite;
18+
stroke-linecap: round;
19+
}
20+
}
21+
22+
@keyframes rotate {
23+
100% {
24+
transform: rotate(360deg);
25+
}
26+
}
27+
28+
@keyframes dash {
29+
0% {
30+
stroke-dasharray: 1, 200;
31+
stroke-dashoffset: 0;
32+
}
33+
50% {
34+
stroke-dasharray: 89, 200;
35+
stroke-dashoffset: -35;
36+
}
37+
100% {
38+
stroke-dasharray: 89, 200;
39+
stroke-dashoffset: -124;
40+
}
41+
}

src/components/Spinner/spinner.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type SpinnerProps = {
2+
color?: string
3+
width?: number
4+
speed?: number
5+
size?: number
6+
dashArray?: number
7+
}

src/pages/index.astro

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Checkbox from '@components/Checkbox/Checkbox.astro'
1111
import Icon from '@components/Icon/Icon.astro'
1212
import Radio from '@components/Radio/Radio.astro'
1313
import Rating from '@components/Rating/Rating.astro'
14+
import Spinner from '@components/Spinner/Spinner.astro'
1415
import Switch from '@components/Switch/Switch.astro'
1516
import Tabs from '@components/Tabs/Tabs.astro'
1617
@@ -116,6 +117,9 @@ const tabItems = [{
116117
<CardWrapper title="Rating" href="/rating">
117118
<Rating score={4} />
118119
</CardWrapper>
120+
<CardWrapper title="Spinner" href="/spinner">
121+
<Spinner />
122+
</CardWrapper>
119123
<CardWrapper title="Switch" href="/switch">
120124
<Switch toggled={true} />
121125
</CardWrapper>

src/pages/spinner.astro

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
import Layout from '@static/Layout.astro'
3+
import ComponentWrapper from '@static/ComponentWrapper.astro'
4+
5+
import AstroSpinner from '@components/Spinner/Spinner.astro'
6+
import SvelteSpinner from '@components/Spinner/Spinner.svelte'
7+
import ReactSpinner from '@components/Spinner/Spinner.tsx'
8+
9+
const sections = [
10+
{
11+
title: 'Astro spinners',
12+
component: AstroSpinner
13+
},
14+
{
15+
title: 'Svelte spinners',
16+
component: SvelteSpinner
17+
},
18+
{
19+
title: 'React spinners',
20+
component: ReactSpinner
21+
}
22+
]
23+
---
24+
25+
<Layout>
26+
<h1>Spinner</h1>
27+
<div class="grid md-2 lg-3">
28+
<ComponentWrapper type="Astro">
29+
<AstroSpinner />
30+
</ComponentWrapper>
31+
32+
<ComponentWrapper type="Svelte">
33+
<SvelteSpinner color="#ee5253" />
34+
</ComponentWrapper>
35+
36+
<ComponentWrapper type="React">
37+
<ReactSpinner color="#48dbfb" />
38+
</ComponentWrapper>
39+
</div>
40+
41+
{sections.map(section => (
42+
<h1>{section.title}</h1>
43+
<div class="grid md-2 lg-3">
44+
<ComponentWrapper title="Default">
45+
<section.component />
46+
</ComponentWrapper>
47+
48+
<ComponentWrapper title="Colored spinner">
49+
<section.component color="#48dbfb" />
50+
</ComponentWrapper>
51+
52+
<ComponentWrapper title="Spinner width">
53+
<section.component width={5} />
54+
</ComponentWrapper>
55+
56+
<ComponentWrapper title="Custom speed in seconds">
57+
<section.component speed={5} />
58+
</ComponentWrapper>
59+
60+
<ComponentWrapper title="Custom size">
61+
<section.component size={15} />
62+
<section.component size={20} />
63+
<section.component size={25} />
64+
</ComponentWrapper>
65+
66+
<ComponentWrapper title="Dashed spinner">
67+
<section.component dashArray={8} />
68+
</ComponentWrapper>
69+
70+
<ComponentWrapper title="Small dashes">
71+
<section.component dashArray={4} />
72+
</ComponentWrapper>
73+
74+
<ComponentWrapper title="Four dashes">
75+
<section.component dashArray={15} />
76+
</ComponentWrapper>
77+
78+
<ComponentWrapper title="Three dashes">
79+
<section.component dashArray={20} />
80+
</ComponentWrapper>
81+
</div>
82+
))}
83+
</Layout>

src/scss/setup.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ $config: (
1515
--w-switch-on-color: #FFF;
1616
--w-checkbox-color: #FFF;
1717
--w-radio-color: #FFF;
18+
--w-spinner-color: #FFF;
19+
--w-spinner-width: 2px;
20+
--w-spinner-speed: 2s;
21+
--w-spinner-size: 30px;
22+
--w-spinner-dash: 8;
1823
}
1924

2025
@function config($key) {

0 commit comments

Comments
 (0)