From a312649c5e9380495bd4969f386562d4fc782159 Mon Sep 17 00:00:00 2001 From: Enrico Date: Thu, 8 Aug 2024 11:34:53 -0300 Subject: [PATCH] feat: add radio button --- package.json | 9 +- pnpm-lock.yaml | 104 ++++++++++++++++++++++++ src/components/ui/form/form-builder.tsx | 29 +++++++ src/components/ui/form/label.tsx | 2 +- src/components/ui/form/radio.tsx | 46 +++++++++++ src/stories/form/form-item.stories.tsx | 30 +++++++ 6 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 src/components/ui/form/radio.tsx diff --git a/package.json b/package.json index 1db03b9..e05203d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@continha/ui", - "version": "1.3.1", + "version": "1.4.0", "private": false, "repository": { "url": "https://github.com/bearkfear/ui" @@ -14,7 +14,9 @@ "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", - "files": ["dist"], + "files": [ + "dist" + ], "peerDependencies": { "@continha/formbuilder": "^1.0.2", "@radix-ui/colors": "^3.0.0", @@ -31,7 +33,6 @@ "tailwind-merge": "^2.4.0" }, "devDependencies": { - "@swc/core": "^1.3.35", "@biomejs/biome": "^1.8.3", "@storybook/addon-essentials": "^8.2.4", "@storybook/addon-interactions": "^8.2.4", @@ -42,6 +43,7 @@ "@storybook/nextjs": "^8.2.4", "@storybook/react": "^8.2.4", "@storybook/test": "^8.2.4", + "@swc/core": "^1.3.35", "@types/lodash.get": "^4.4.9", "@types/lodash.isequal": "^4.5.8", "@types/node": "^20", @@ -60,6 +62,7 @@ "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c45b423..537ae58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: '@radix-ui/react-popover': specifier: ^1.1.1 version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-radio-group': + specifier: ^1.2.0 + version: 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-separator': specifier: ^1.1.0 version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1483,6 +1486,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-collection@1.1.0': + resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-compose-refs@1.0.1': resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: @@ -1545,6 +1561,15 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-direction@1.1.0': + resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dismissable-layer@1.0.5': resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} peerDependencies: @@ -1750,6 +1775,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-radio-group@1.2.0': + resolution: {integrity: sha512-yv+oiLaicYMBpqgfpSPw6q+RyXlLdIpQWDHZbUKURxe+nEh53hFXPPlfhfQQtYkS5MMK/5IWIa76SksleQZSzw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.0': + resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-separator@1.1.0': resolution: {integrity: sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==} peerDependencies: @@ -6655,6 +6706,18 @@ snapshots: '@types/react': 18.3.3 '@types/react-dom': 18.3.0 + '@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -6726,6 +6789,12 @@ snapshots: '@types/react': 18.3.3 '@types/react-dom': 18.3.0 + '@radix-ui/react-direction@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.0 @@ -6914,6 +6983,41 @@ snapshots: '@types/react': 18.3.3 '@types/react-dom': 18.3.0 + '@radix-ui/react-radio-group@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + '@radix-ui/react-separator@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) diff --git a/src/components/ui/form/form-builder.tsx b/src/components/ui/form/form-builder.tsx index 76b7bfb..fb387dc 100644 --- a/src/components/ui/form/form-builder.tsx +++ b/src/components/ui/form/form-builder.tsx @@ -11,6 +11,7 @@ import { Label } from "./label"; import { MultiSelector, SingleSelector } from "./selector"; import { Textarea } from "./textarea"; import { Checkbox } from "./checkbox"; +import * as Radio from "./radio"; function FormControl(props: FormRenderProps) { const disabled = props.field.disabled || props.fieldConfig.disabled; @@ -99,6 +100,34 @@ function FormControl(props: FormRenderProps) { ); } + if (props.fieldConfig.type === "radio") { + const options = props.fieldConfig.options || []; + + return ( + + props.field.onChange( + options.find((option) => `${option.value}` === newValue)?.value, + ) + } + > + {props.fieldConfig.options?.map((option) => { + const id = `radio-${props.fieldConfig.name}-option${option.value}`; + return ( +
+ + +
+ ); + })} +
+ ); + } + return null; } diff --git a/src/components/ui/form/label.tsx b/src/components/ui/form/label.tsx index 52d7a47..1ffcad7 100644 --- a/src/components/ui/form/label.tsx +++ b/src/components/ui/form/label.tsx @@ -8,7 +8,7 @@ import { LuAsterisk } from "react-icons/lu"; import { cn } from "~/lib/utils"; const labelVariants = cva( - "text-xs text-black dark:text-white font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", + "text-xs text-black dark:text-white leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 font-bold", ); const Label = React.forwardRef< diff --git a/src/components/ui/form/radio.tsx b/src/components/ui/form/radio.tsx new file mode 100644 index 0000000..832b7f6 --- /dev/null +++ b/src/components/ui/form/radio.tsx @@ -0,0 +1,46 @@ +"use client"; + +import * as React from "react"; +import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"; +import { Circle } from "lucide-react"; + +import { cn } from "~/lib/utils"; + +const Group = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + ); +}); +Group.displayName = RadioGroupPrimitive.Root.displayName; + +const Item = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + + + + + ); +}); +Item.displayName = RadioGroupPrimitive.Item.displayName; + +const Root = RadioGroupPrimitive.Root; + +export { Group, Item, Root }; diff --git a/src/stories/form/form-item.stories.tsx b/src/stories/form/form-item.stories.tsx index a186f2f..bef973d 100644 --- a/src/stories/form/form-item.stories.tsx +++ b/src/stories/form/form-item.stories.tsx @@ -211,3 +211,33 @@ export const Checkbox = () => { ); }; + +export const Radio = () => { + const form = useForm({ + defaultValues: { + gender: "", + }, + }); + + return ( + + + + ); +};