Skip to content
This repository was archived by the owner on Sep 20, 2024. It is now read-only.

Commit 9613902

Browse files
feat(c-radio): create component
1 parent 1178174 commit 9613902

18 files changed

+948
-706
lines changed

packages/c-radio/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# `@chakra-ui/c-radio`
2+
3+
Radios are used when only one choice may be selected in a series of options
4+
5+
## Installation
6+
7+
```sh
8+
# with pnpm
9+
pnpm add @chakra-ui/c-radio
10+
# or with Yarn
11+
yarn i @chakra-ui/c-radio
12+
# or with npm
13+
npm i @chakra-ui/c-radio
14+
```
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
import { ref } from "vue"
3+
import { CVStack } from "../../layout/index"
4+
import { CRadio, CRadioGroup } from "../index"
5+
import { RadioGroupContext } from "../src/radio-context"
6+
7+
const selectedVal = ref("")
8+
9+
const radioGroupRef = ref<RadioGroupContext>()
10+
</script>
11+
<template>
12+
<c-radio-group ref="radioGroupRef" v-model="selectedVal" v-slot="{ value }">
13+
<div>Current Value: {{ value }}</div>
14+
<c-v-stack>
15+
<c-radio value="opt-1">Option 1</c-radio>
16+
<c-radio value="opt-2">Option 2</c-radio>
17+
<c-radio value="opt-3">Option 3</c-radio>
18+
</c-v-stack>
19+
</c-radio-group>
20+
<button @click="() => radioGroupRef?.clearValue()">Clear</button>
21+
</template>

packages/c-radio/examples/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * as ControlledCRadio from "./controlled.vue"
2+
export * as StateVariantsCRadio from "./state-variants.vue"
3+
export * as WithSimpleGridCRadio from "./with-simple-grid.vue"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script setup lang="ts">
2+
import { CVStack } from "../../layout/index"
3+
import { CRadio, CRadioGroup } from "../index"
4+
</script>
5+
<template>
6+
<c-radio-group value="1">
7+
<c-v-stack>
8+
<c-radio value="hello">Hello</c-radio>
9+
<c-radio value="disabled" disabled>I'm disabled</c-radio>
10+
<c-radio value="readonly" read-only>I can only be read</c-radio>
11+
</c-v-stack>
12+
</c-radio-group>
13+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import { CSimpleGrid } from "../../layout"
3+
import { CRadio, CRadioGroup } from "../"
4+
const range = Array.from(Array(10)).map((_, i) => i + 1)
5+
</script>
6+
<template>
7+
<c-radio-group value="1">
8+
<c-simple-grid as="div" :columns="2" :spacing="[2, 4, 6]">
9+
<c-radio v-for="num in range" :key="num" :value="`Option ${num}`">{{
10+
`Option ${num}`
11+
}}</c-radio>
12+
</c-simple-grid>
13+
</c-radio-group>
14+
</template>

packages/c-radio/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./src"

packages/c-radio/package.json

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "@chakra-ui/c-radio",
3+
"description": "Chakra UI Vue | Radios are used when only one choice may be selected in a series of options component",
4+
"version": "0.0.0-beta.0",
5+
"author": "Jonathan Bakebwa <[email protected]>",
6+
"homepage": "https://github.com/chakra-ui/chakra-ui-vue-next#readme",
7+
"license": "MIT",
8+
"main": "src/index.ts",
9+
"clean-package": "../../clean-package.config.json",
10+
"files": [
11+
"dist"
12+
],
13+
"repository": {
14+
"type": "git",
15+
"url": "git+https://github.com/chakra-ui/chakra-ui-vue-next.git"
16+
},
17+
"bugs": {
18+
"url": "https://github.com/chakra-ui/chakra-ui-vue-next/issues"
19+
},
20+
"sideEffects": false,
21+
"scripts": {
22+
"clean": "rimraf dist .turbo",
23+
"build": "tsup && pnpm build:types",
24+
"build:fast": "tsup",
25+
"build:types": "tsup src --dts-only",
26+
"types:check": "tsc --noEmit",
27+
"dev": "tsup --watch"
28+
},
29+
"dependencies": {
30+
"@chakra-ui/vue-system": "workspace:*",
31+
"@chakra-ui/vue-composables": "workspace:*",
32+
"@chakra-ui/vue-utils": "workspace:*",
33+
"@zag-js/radio-group": "0.7.0",
34+
"@zag-js/vue": "0.7.0",
35+
"@chakra-ui/styled-system": "2.9.0"
36+
},
37+
"devDependencies": {
38+
"vue": "^3.2.37"
39+
},
40+
"peerDependencies": {
41+
"vue": "^3.1.4"
42+
},
43+
"publishConfig": {
44+
"access": "public"
45+
}
46+
}
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { h, PropType, defineComponent } from "vue"
2+
import { UseRadioGroupProps, useRadioGroup } from "./use-radio-group"
3+
import {
4+
HTMLChakraProps,
5+
ThemingProps,
6+
chakra,
7+
useMultiStyleConfig,
8+
} from "@chakra-ui/vue-system"
9+
import { RadioGroupProvider, RadioGroupStylesProvider } from "./radio-context"
10+
import { useThemingProps, vueThemingProps } from "@chakra-ui/vue-utils"
11+
12+
export interface CRadioGroupProps
13+
extends UseRadioGroupProps,
14+
HTMLChakraProps<"div">,
15+
Omit<ThemingProps<"Radio">, "orientation"> {}
16+
17+
export const CRadioGroup = defineComponent({
18+
name: "CRadioGroup",
19+
props: {
20+
dir: {
21+
type: String as PropType<CRadioGroupProps["dir"]>,
22+
},
23+
disabled: {
24+
type: Boolean as PropType<CRadioGroupProps["disabled"]>,
25+
},
26+
form: {
27+
type: String as PropType<CRadioGroupProps["form"]>,
28+
},
29+
getRootNode: {
30+
type: Function as PropType<CRadioGroupProps["getRootNode"]>,
31+
},
32+
id: {
33+
type: String as PropType<CRadioGroupProps["id"]>,
34+
},
35+
ids: {
36+
type: Object as PropType<CRadioGroupProps["ids"]>,
37+
},
38+
name: {
39+
type: String as PropType<CRadioGroupProps["name"]>,
40+
},
41+
modelValue: {
42+
type: String as PropType<CRadioGroupProps["modelValue"]>,
43+
},
44+
orientation: {
45+
type: String as PropType<CRadioGroupProps["orientation"]>,
46+
},
47+
readonly: {
48+
type: Boolean as PropType<CRadioGroupProps["readOnly"]>,
49+
},
50+
...vueThemingProps,
51+
},
52+
emits: ["change", "update:modelValue"],
53+
setup(props, { slots, attrs, expose }) {
54+
const api = useRadioGroup(props)
55+
56+
const themeProps = useThemingProps(props)
57+
58+
const styles = useMultiStyleConfig("Radio", themeProps)
59+
60+
RadioGroupProvider(api)
61+
62+
RadioGroupStylesProvider(styles)
63+
64+
expose(api.value)
65+
66+
return () => (
67+
<chakra.div __label="radio-group" {...api.value.rootProps} {...attrs}>
68+
{slots.default?.(api.value)}
69+
</chakra.div>
70+
)
71+
},
72+
})

packages/c-radio/src/c-radio.tsx

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* Hey! Welcome to @chakra-ui/vue-next CRadio
3+
*
4+
* Radios are used when only one choice may be selected in a series of options
5+
*
6+
* @see Docs https://next.vue.chakra-ui.com/c-radio
7+
* @see Source https://github.com/chakra-ui/chakra-ui-vue-next/blob/main/packages/c-radio/src/c-radio/c-radio.ts
8+
* @see WAI-ARIA https://www.w3.org/TR/wai-aria-practices-1.2
9+
*/
10+
11+
import {
12+
computed,
13+
defineComponent,
14+
h,
15+
InputHTMLAttributes,
16+
isVNode,
17+
mergeProps,
18+
PropType,
19+
reactive,
20+
} from "vue"
21+
import {
22+
chakra,
23+
HTMLChakraProps,
24+
type ThemingProps,
25+
type StyleResolverProps,
26+
useMultiStyleConfig,
27+
} from "@chakra-ui/vue-system"
28+
import * as VS from "@chakra-ui/vue-system"
29+
import { getValidChildren, SNAO, vueThemingProps } from "@chakra-ui/vue-utils"
30+
import { RadioContext, useRadioGroupContext } from "./radio-context"
31+
32+
export interface CRadioProps
33+
extends Omit<HTMLChakraProps<"label">, keyof RadioContext>,
34+
ThemingProps<"Radio">,
35+
RadioContext {
36+
/**
37+
* The spacing between the checkbox and its label text
38+
* @default 0.5rem
39+
* @type SystemProps["marginLeft"]
40+
*/
41+
spacing?: StyleResolverProps["marginLeft"]
42+
/**
43+
* Additional props to be forwarded to the `input` element
44+
*/
45+
inputProps?: InputHTMLAttributes
46+
}
47+
48+
export const CRadio = defineComponent({
49+
props: {
50+
value: {
51+
type: String as PropType<CRadioProps["value"]>,
52+
required: true,
53+
},
54+
disabled: {
55+
type: Boolean as PropType<CRadioProps["disabled"]>,
56+
},
57+
invalid: {
58+
type: Boolean as PropType<CRadioProps["invalid"]>,
59+
},
60+
readOnly: {
61+
type: Boolean as PropType<CRadioProps["readOnly"]>,
62+
},
63+
spacing: {
64+
type: SNAO as PropType<CRadioProps["spacing"]>,
65+
default: "0.5rem",
66+
},
67+
...vueThemingProps,
68+
},
69+
setup(props, { slots, attrs }) {
70+
const groupApi = useRadioGroupContext()
71+
72+
const styleAttrs = computed(() => mergeProps(props, groupApi.value, attrs))
73+
74+
const styles = useMultiStyleConfig("Radio", styleAttrs)
75+
76+
const radioProps = reactive({
77+
value: props.value,
78+
disabled: props.disabled,
79+
invalid: props.invalid,
80+
readOnly: props.readOnly,
81+
})
82+
83+
const inputProps = computed(() => {
84+
const apiInputProps = groupApi.value.getRadioInputProps(radioProps)
85+
const apiInputState = groupApi.value.getRadioState(radioProps)
86+
87+
return {
88+
...apiInputProps,
89+
// modelValue: apiInputState.isChecked,
90+
}
91+
})
92+
93+
const rootStyles = computed(() => ({
94+
display: "inline-flex",
95+
alignItems: "center",
96+
verticalAlign: "top",
97+
cursor: "pointer",
98+
position: "relative",
99+
...styles.value.container,
100+
}))
101+
102+
const labelStyles = computed(() => ({
103+
userSelect: "none",
104+
marginStart: props.spacing,
105+
...styles.value.label,
106+
}))
107+
108+
const controlStyles = computed(() => ({
109+
display: "inline-flex",
110+
alignItems: "center",
111+
justifyContent: "center",
112+
flexShrink: 0,
113+
...styles.value.control,
114+
}))
115+
116+
return () => (
117+
<chakra.label
118+
{...groupApi.value.getRadioProps(radioProps)}
119+
__css={rootStyles.value}
120+
{...attrs}
121+
>
122+
<chakra.input
123+
__chakraIsRaw
124+
__label="radio__input"
125+
{...inputProps.value}
126+
/>
127+
<chakra.span
128+
__label="radio__control"
129+
{...groupApi.value.getRadioControlProps(radioProps)}
130+
__css={controlStyles.value}
131+
/>
132+
<chakra.span
133+
__label="radio__label"
134+
{...groupApi.value.getRadioLabelProps(radioProps)}
135+
__css={labelStyles.value}
136+
>
137+
{() => getValidChildren(slots)}
138+
</chakra.span>
139+
</chakra.label>
140+
)
141+
},
142+
})

packages/c-radio/src/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { CRadioGroup, type CRadioGroupProps } from "./c-radio-group"
2+
export { CRadio, type CRadioProps } from "./c-radio"
3+
export type { RadioGroupContext } from "./radio-context"

packages/c-radio/src/radio-context.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as CSS from "csstype"
2+
import * as radio from "@zag-js/radio-group"
3+
import { createContext } from "@chakra-ui/vue-utils"
4+
import type { UseRadioGroupReturn } from "./use-radio-group"
5+
import { UnwrapRef } from "vue"
6+
import { createStylesContext } from "@chakra-ui/vue-system"
7+
import * as VS from "@chakra-ui/vue-system"
8+
9+
export type RadioContext = Parameters<
10+
ReturnType<typeof radio.connect>["getRadioProps"]
11+
>[0]
12+
13+
export const [RadioProvider, useRadioContext] = createContext<RadioContext>({
14+
name: "CRadioContext",
15+
strict: true,
16+
})
17+
18+
export type RadioGroupContext = UnwrapRef<UseRadioGroupReturn>
19+
20+
export const [RadioGroupProvider, useRadioGroupContext] =
21+
createContext<UseRadioGroupReturn>({
22+
name: "CRadioGroupContext",
23+
strict: true,
24+
})
25+
26+
export const [RadioGroupStylesProvider, useRadioGroupStyles] =
27+
createStylesContext("CRadioGroup")

0 commit comments

Comments
 (0)