Skip to content

Commit

Permalink
feat: add RadioGroup component (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
halvaradop authored Jan 10, 2025
1 parent e3bcdf0 commit f1fd843
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 1 deletion.
48 changes: 48 additions & 0 deletions packages/ui-radio-group/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@halvaradop/ui-radio-group",
"version": "0.1.0",
"private": false,
"description": "A customizable Radio Group component for @halvaradop/ui library with Tailwind CSS styling.",
"type": "module",
"scripts": {
"dev": "tsup --watch",
"build": "tsup",
"format": "prettier --write .",
"format:check": "prettier --check .",
"clean": "pnpm clean:build && pnpm clean:modules",
"clean:build": "rm -rf dist",
"clean:modules": "rm -rf node_modules"
},
"repository": {
"type": "git",
"url": "git+https://github.com/halvaradop/ui.git"
},
"keywords": [
"react",
"component",
"tailwindcss",
"react",
"ui",
"ui library"
],
"author": "Hernan Alvarado <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/halvaradop/ui/issues"
},
"homepage": "https://github.com/halvaradop/ui#readme",
"files": [
"dist"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"dependencies": {
"@halvaradop/ui-core": "workspace:*",
"class-variance-authority": "0.7.0"
}
}
43 changes: 43 additions & 0 deletions packages/ui-radio-group/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use client"
import { ComponentProps, forwardRef, useEffect, useRef } from "react"
import { merge, type ArgsFunction } from "@halvaradop/ui-core"
import { cva, type VariantProps } from "class-variance-authority"

export type RadioGroupProps<T extends ArgsFunction> = VariantProps<T> &
Omit<ComponentProps<"fieldset">, "children"> & {
children: React.ReactNode
}

export const radioGroupVariants = cva("flex", {
variants: {
variant: {
row: "flex-row gap-x-5",
column: "flex-col gap-y-1",
},
},
defaultVariants: {
variant: "column",
},
})

export const RadioGroup = forwardRef<HTMLFieldSetElement, RadioGroupProps<typeof radioGroupVariants>>(({ className, variant, name, defaultValue, children, ...props }, ref) => {
// @ts-ignore
const reference = useRef<HTMLFieldSetElement>(ref?.current)

useEffect(() => {
if (!reference.current) return
const radioButtons = reference.current.querySelectorAll("input[type=radio]")
radioButtons.forEach((radio) => {
if (radio.getAttribute("value") === defaultValue) {
radio.setAttribute("checked", "checked")
}
radio.setAttribute("name", name ?? "default-radio-group")
})
}, [])

return (
<fieldset className={merge(radioGroupVariants({ className, variant }))} defaultValue={defaultValue} ref={reference} {...props}>
{children}
</fieldset>
)
})
68 changes: 68 additions & 0 deletions packages/ui-radio-group/src/radio-group.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { Meta, StoryObj } from "@storybook/react"
import { RadioGroup } from "./index.js"
import { Label } from "../../ui-label/src/index.js"
import { Radio } from "../../ui-radio/src/index.js"

const meta: Meta = {
title: "ui-radio-group",
tags: ["autodocs"],
component: RadioGroup,
parameters: {
layout: "centered",
},
} satisfies Meta<typeof RadioGroup>

type Story = StoryObj<typeof meta>

export const Column: Story = {
render: () => {
return (
<RadioGroup name="food">
<Label className="flex items-center gap-x-2">
<Radio value="pizza" name="food" />
Pizza
</Label>
<Label className="flex items-center gap-x-2">
<Radio value="hamburger" name="food" />
Hamburger
</Label>
</RadioGroup>
)
},
}

export const Row: Story = {
render: () => {
return (
<RadioGroup variant="row" name="food">
<Label className="flex items-center gap-x-2">
<Radio value="pizza" name="food" />
Pizza
</Label>
<Label className="flex items-center gap-x-2">
<Radio value="hamburger" name="food" />
Hamburger
</Label>
</RadioGroup>
)
},
}

export const DefaultChecked: Story = {
render: () => {
return (
<RadioGroup name="food" defaultValue="pizza">
<Label className="flex items-center gap-x-2">
<Radio value="pizza" name="food" />
Pizza
</Label>
<Label className="flex items-center gap-x-2">
<Radio value="hamburger" name="food" />
Hamburger
</Label>
</RadioGroup>
)
},
}

export default meta
9 changes: 9 additions & 0 deletions packages/ui-radio-group/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@halvaradop/ui-core/tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"jsx": "react-jsx"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
4 changes: 4 additions & 0 deletions packages/ui-radio-group/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { defineConfig } from "tsup"
import { tsupConfig } from "@halvaradop/ui-core/tsup.config.base"

export default defineConfig(tsupConfig)
4 changes: 3 additions & 1 deletion packages/ui-radio/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ const internalVariants = cva("block absolute rounded-full", {

export const Radio = ({ className, size, color, name, ...props }: RadioProps<typeof radioVariants & typeof internalVariants>) => {
return (
<label className="flex items-center justify-center relative" htmlFor={name}>
<label className="w-min inline-flex items-center justify-center relative" htmlFor={name}>
<input className={merge(radioVariants({ className, size, color }))} type="radio" name={name} {...props} />
<span className={internalVariants({ size, color })} />
</label>
)
}

Radio.displayName = "Radio"
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f1fd843

Please sign in to comment.