Skip to content

Commit 8661cab

Browse files
committed
✨ Add radio component
1 parent f4d4f47 commit 8661cab

File tree

10 files changed

+424
-4
lines changed

10 files changed

+424
-4
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ import { Accordion } from 'webcoreui/react'
149149
- [Badge](https://github.com/Frontendland/webcoreui/tree/main/src/components/Badge)
150150
- [Button](https://github.com/Frontendland/webcoreui/tree/main/src/components/Button)
151151
- [Card](https://github.com/Frontendland/webcoreui/tree/main/src/components/Card)
152+
- [Checkbox](https://github.com/Frontendland/webcoreui/tree/main/src/components/Checkbox)
152153
- [ConditionalWrapper](https://github.com/Frontendland/webcoreui/tree/main/src/components/ConditionalWrapper)
153154
- [Icon](https://github.com/Frontendland/webcoreui/tree/main/src/components/Icon)
155+
- [Radio](https://github.com/Frontendland/webcoreui/tree/main/src/components/Radio)
154156
- [Rating](https://github.com/Frontendland/webcoreui/tree/main/src/components/Rating)
157+
- [Switch](https://github.com/Frontendland/webcoreui/tree/main/src/components/Switch)

src/components/Radio/Radio.astro

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
import type { RadioProps } from './radio'
3+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro'
4+
5+
import './radio.scss'
6+
7+
interface Props extends RadioProps {}
8+
9+
const {
10+
name,
11+
items,
12+
color,
13+
inline,
14+
className
15+
} = Astro.props
16+
17+
const classes = [
18+
'w-radio',
19+
inline && 'inline',
20+
className
21+
]
22+
23+
const style = color
24+
? `--w-radio-color: ${color};`
25+
: null
26+
---
27+
28+
<div class:list={classes} style={style}>
29+
{items.map(item => (
30+
<label class:list={[
31+
item.subText && 'col',
32+
item.disabled && 'disabled'
33+
]}>
34+
<ConditionalWrapper condition={!!(item.subText)}>
35+
<div class="radio-wrapper" slot="wrapper">
36+
children
37+
</div>
38+
<input
39+
type="radio"
40+
name={name}
41+
checked={item.selected}
42+
disabled={item.disabled}
43+
/>
44+
<span class="radio" />
45+
<span class="label">
46+
<Fragment set:html={item.label} />
47+
</span>
48+
</ConditionalWrapper>
49+
{item.subText && (
50+
<span class="sub-text">
51+
<Fragment set:html={item.subText} />
52+
</span>
53+
)}
54+
</label>
55+
))}
56+
</div>

src/components/Radio/Radio.svelte

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<script lang="ts">
2+
import type { RadioProps } from './radio'
3+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.svelte'
4+
5+
import './radio.scss'
6+
7+
export let name: RadioProps['name'] = ''
8+
export let items: RadioProps['items'] = []
9+
export let color: RadioProps['color'] = ''
10+
export let inline: RadioProps['inline'] = false
11+
export let className: RadioProps['className'] = ''
12+
export let onChange: () => any = () => {}
13+
14+
const classes = [
15+
'w-radio',
16+
inline && 'inline',
17+
className
18+
].filter(Boolean).join(' ')
19+
20+
const style = color
21+
? `--w-radio-color: ${color};`
22+
: null
23+
</script>
24+
25+
<div class={classes} style={style}>
26+
{#each items as item}
27+
<label
28+
class:col={item.subText}
29+
class:disabled={item.disabled}
30+
>
31+
<ConditionalWrapper
32+
condition={!!(item.subText)}
33+
element="div"
34+
class="radio-wrapper"
35+
>
36+
<input
37+
type="radio"
38+
name={name}
39+
checked={item.selected}
40+
disabled={item.disabled}
41+
on:change={onChange}
42+
/>
43+
<span class="radio" />
44+
<span class="label">
45+
{@html item.label}
46+
</span>
47+
</ConditionalWrapper>
48+
{#if item.subText}
49+
<span class="sub-text">
50+
{@html item.subText}
51+
</span>
52+
{/if}
53+
</label>
54+
{/each}
55+
</div>

src/components/Radio/Radio.tsx

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React from 'react'
2+
import type { RadioProps } from './radio'
3+
4+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.tsx'
5+
6+
import './radio.scss'
7+
8+
type ReactRadioProps = {
9+
onChange?: () => any
10+
} & RadioProps
11+
12+
const Radio = ({
13+
name,
14+
items,
15+
color,
16+
inline,
17+
className,
18+
onChange
19+
}: ReactRadioProps) => {
20+
const classes = [
21+
'w-radio',
22+
inline && 'inline',
23+
className
24+
].filter(Boolean).join(' ')
25+
26+
const style = color
27+
? { '--w-radio-color': color } as React.CSSProperties
28+
: undefined
29+
30+
return (
31+
<div className={classes} style={style}>
32+
{items.map(item => (
33+
<label className={[
34+
item.subText && 'col',
35+
item.disabled && 'disabled'
36+
].filter(Boolean).join(' ')}>
37+
<ConditionalWrapper
38+
condition={!!(item.subText)}
39+
wrapper={children => (
40+
<div className="radio-wrapper">
41+
{children}
42+
</div>
43+
)}
44+
>
45+
<input
46+
type="radio"
47+
name={name}
48+
checked={item.selected}
49+
disabled={item.disabled}
50+
onChange={onChange}
51+
/>
52+
<span className="radio" />
53+
<span
54+
className="label"
55+
dangerouslySetInnerHTML={{ __html: item.label }}
56+
/>
57+
</ConditionalWrapper>
58+
{item.subText && (
59+
<span
60+
className="sub-text"
61+
dangerouslySetInnerHTML={{ __html: item.subText }}
62+
/>
63+
)}
64+
</label>
65+
))}
66+
</div>
67+
)
68+
}
69+
70+
export default Radio
71+

src/components/Radio/radio.scss

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
@import '../../scss/config.scss';
2+
3+
.w-radio {
4+
display: flex;
5+
flex-direction: column;
6+
gap: 10px;
7+
8+
&.inline {
9+
flex-direction: row;
10+
}
11+
12+
label {
13+
display: flex;
14+
align-items: center;
15+
gap: 5px;
16+
cursor: pointer;
17+
font-size: 16px;
18+
19+
&.disabled {
20+
cursor: no-drop;
21+
22+
input + span::after {
23+
background: #BBB;
24+
}
25+
}
26+
27+
&.col {
28+
flex-direction: column;
29+
align-items: flex-start;
30+
}
31+
}
32+
33+
input {
34+
display: none;
35+
36+
+ span::after {
37+
@include Transition(transform);
38+
content: '';
39+
position: absolute;
40+
top: 50%;
41+
left: 50%;
42+
transform: translate(-50%, -50%) scale(0);
43+
width: 8px;
44+
height: 8px;
45+
border-radius: 100%;
46+
background: var(--w-radio-color);
47+
}
48+
49+
&:checked + span::after {
50+
transform: translate(-50%, -50%) scale(1);
51+
}
52+
53+
&:disabled + span {
54+
background-color: #333;
55+
border-color: #333;
56+
}
57+
}
58+
59+
a {
60+
text-decoration: underline;
61+
}
62+
63+
.radio-wrapper {
64+
display: flex;
65+
align-items: center;
66+
gap: 10px;
67+
}
68+
69+
.radio {
70+
display: inline-block;
71+
width: 16px;
72+
height: 16px;
73+
border-radius: 100%;
74+
border: 1px solid var(--w-radio-color);
75+
position: relative;
76+
}
77+
78+
.sub-text {
79+
margin-left: 25px;
80+
font-size: 14px;
81+
color: #BBB;
82+
83+
a {
84+
@include Transition(color);
85+
color: #BBB;
86+
87+
&:hover {
88+
color: #FFF;
89+
}
90+
}
91+
}
92+
}

src/components/Radio/radio.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export type RadioProps = {
2+
items: {
3+
label: string
4+
subText?: string
5+
selected?: boolean
6+
disabled?: boolean
7+
}[]
8+
name: string
9+
color?: string
10+
inline?: boolean
11+
className?: string
12+
}

src/pages/checkbox.astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ const sections = [
6161
<section.component label="Accept the <a href='/' target='_blank'>terms</a>." />
6262
</ComponentWrapper>
6363

64-
<ComponentWrapper title="With label and subtitle">
64+
<ComponentWrapper title="With label and linked subtitle">
6565
<section.component
6666
label="Accept terms and conditions"
67-
subText="You must accept our terms to continue."
67+
subText="You must accept our <a href='/' target='_blank'>terms</a> to continue."
6868
/>
6969
</ComponentWrapper>
7070

src/pages/index.astro

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Avatar from '@components/Avatar/Avatar.astro'
88
import Badge from '@components/Badge/Badge.astro'
99
import Button from '@components/Button/Button.astro'
1010
import Icon from '@components/Icon/Icon.astro'
11+
import Radio from '@components/Radio/Radio.astro'
1112
import Rating from '@components/Rating/Rating.astro'
1213
import Switch from '@components/Switch/Switch.astro'
1314
import Checkbox from '@components/Checkbox/Checkbox.astro'
@@ -74,7 +75,7 @@ import Checkbox from '@components/Checkbox/Checkbox.astro'
7475
<p>Paragraph inside a card</p>
7576
</CardWrapper>
7677
<CardWrapper title="Checkbox" href="/checkbox">
77-
<Checkbox checked={true} />
78+
<Checkbox checked={true} label="Accept terms and conditions" />
7879
</CardWrapper>
7980
<CardWrapper title="Icon" href="/icon">
8081
<Icon
@@ -103,10 +104,15 @@ import Checkbox from '@components/Checkbox/Checkbox.astro'
103104
color="#f2c262"
104105
/>
105106
</CardWrapper>
107+
<CardWrapper title="Radio" href="/radio">
108+
<Radio
109+
items={[{ label: 'Radio', selected: true }]}
110+
name="radio"
111+
/>
112+
</CardWrapper>
106113
<CardWrapper title="Rating" href="/rating">
107114
<Rating score={4} />
108115
</CardWrapper>
109-
110116
<CardWrapper title="Switch" href="/switch">
111117
<Switch toggled={true} />
112118
</CardWrapper>

0 commit comments

Comments
 (0)