diff --git a/apps/docs/src/stories/components/checkbox.stories.tsx b/apps/docs/src/stories/components/checkbox.stories.tsx new file mode 100644 index 0000000..0993656 --- /dev/null +++ b/apps/docs/src/stories/components/checkbox.stories.tsx @@ -0,0 +1,34 @@ +import { Checkbox, type CheckboxProps } from "@fellipeutaka/ui/checkbox"; +import { Label } from "@fellipeutaka/ui/label"; +import type { Meta, StoryObj } from "@storybook/react"; + +const meta: Meta = { + title: "Components/Checkbox", + component: Checkbox, + args: { + disabled: false, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithLabel: Story = { + render(props) { + return ( +
+ + +
+ ); + }, +}; + +export const Disabled: Story = { + args: { + disabled: true, + }, +}; diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 9d89401..b12f525 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,11 @@ # @fellipeutaka/ui +## 2.1.0 + +### Minor Changes + +- add textfield and checkbox + ## 2.0.0 ### Major Changes diff --git a/packages/ui/package.json b/packages/ui/package.json index 5eb440c..12733cf 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@fellipeutaka/ui", - "version": "2.0.0", + "version": "2.1.0", "description": "An accessible and customizable component library powered by shadcn/ui", "sideEffects": false, "license": "MIT", @@ -39,6 +39,11 @@ "require": "./dist/button.js", "types": "./dist/button.d.ts" }, + "./checkbox": { + "import": "./dist/checkbox.mjs", + "require": "./dist/checkbox.js", + "types": "./dist/checkbox.d.ts" + }, "./dialog": { "import": "./dist/dialog.mjs", "require": "./dist/dialog.js", @@ -92,6 +97,7 @@ "clean": "rimraf .turbo node_modules dist" }, "dependencies": { + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-select": "^2.0.0", diff --git a/packages/ui/src/badge.tsx b/packages/ui/src/badge.tsx index 6ca38e1..37df657 100644 --- a/packages/ui/src/badge.tsx +++ b/packages/ui/src/badge.tsx @@ -3,14 +3,22 @@ import { forwardRef } from "react"; import { tv, type VariantProps } from "@fellipeutaka/styles"; export const BadgeStyles = tv({ - base: "inline-flex items-center rounded-full border border-transparent px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + base: [ + "inline-flex items-center rounded-full border border-transparent px-2.5 py-0.5 text-xs font-semibold outline-none transition-colors", + "focus:ring-ring focus:ring-2 focus:ring-offset-2", + ], variants: { variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/80", - secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", - destructive: - "bg-destructive text-destructive-foreground hover:bg-destructive/80", - outline: "border-border text-foreground", + default: ["bg-primary text-primary-foreground", "hover:bg-primary/80"], + secondary: [ + "bg-secondary text-secondary-foreground", + "hover:bg-secondary/80", + ], + destructive: [ + "bg-destructive text-destructive-foreground", + "hover:bg-destructive/80", + ], + outline: ["border-border text-foreground"], }, }, defaultVariants: { diff --git a/packages/ui/src/button.tsx b/packages/ui/src/button.tsx index 968f336..0848e9f 100644 --- a/packages/ui/src/button.tsx +++ b/packages/ui/src/button.tsx @@ -3,17 +3,28 @@ import { forwardRef } from "react"; import { tv, type VariantProps } from "@fellipeutaka/styles"; export const ButtonStyles = tv({ - base: "ring-offset-background focus-visible:ring-ring inline-flex select-none items-center justify-center rounded-md text-sm font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", + base: [ + "ring-offset-background inline-flex select-none items-center justify-center rounded-md text-sm font-medium outline-none transition", + "focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2", + "disabled:cursor-not-allowed disabled:opacity-50", + ], variants: { variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: - "bg-destructive text-destructive-foreground hover:bg-destructive/90", - outline: - "border-input bg-background hover:bg-accent hover:text-accent-foreground border", - secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", + default: ["bg-primary text-primary-foreground", "hover:bg-primary/90"], + destructive: [ + "bg-destructive text-destructive-foreground", + "hover:bg-destructive/90", + ], + outline: [ + "border-input bg-background hover:bg-accent border", + "hover:text-accent-foreground", + ], + secondary: [ + "bg-secondary text-secondary-foreground", + "hover:bg-secondary/80", + ], + ghost: ["hover:bg-accent hover:text-accent-foreground"], + link: ["text-primary underline-offset-4", "hover:underline"], }, size: { default: "h-10 px-4 py-2", diff --git a/packages/ui/src/checkbox.tsx b/packages/ui/src/checkbox.tsx new file mode 100644 index 0000000..f052549 --- /dev/null +++ b/packages/ui/src/checkbox.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { forwardRef } from "react"; + +import { tv } from "@fellipeutaka/styles"; +import { Root, Indicator } from "@radix-ui/react-checkbox"; +import { Check } from "lucide-react"; + +export type { CheckboxProps } from "@radix-ui/react-checkbox"; + +export const CheckboxStyles = { + Root: tv({ + base: [ + "border-input text-primary-foreground flex aspect-square w-5 appearance-none items-center justify-center rounded-sm border shadow outline-none transition", + "disabled:cursor-not-allowed disabled:opacity-50", + "data-[state=checked]:border-primary data-[state=checked]:bg-primary", + "focus-visible:ring-input focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2", + ], + }), +}; + +export const Checkbox = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)); +Checkbox.displayName = "Checkbox"; diff --git a/packages/ui/src/select.tsx b/packages/ui/src/select.tsx index 5e77028..7d061b8 100644 --- a/packages/ui/src/select.tsx +++ b/packages/ui/src/select.tsx @@ -10,35 +10,62 @@ import { Text } from "./text"; export const SelectStyles = { Trigger: tv({ - base: "border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:ring-ring group flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", + base: [ + "border-input bg-background ring-offset-background group flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm outline-none transition", + "placeholder:text-muted-foreground", + "focus:ring-ring focus:ring-2 focus:ring-offset-2", + "disabled:cursor-not-allowed disabled:opacity-50", + ], }), Content: tv({ - base: "border-border bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 min-w-[8rem] overflow-hidden rounded-md border shadow-md", + base: [ + [ + "border-border bg-popover text-popover-foreground relative z-50 min-w-[8rem] overflow-hidden rounded-md border shadow-md", + "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95", + "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95", + "data-[side=bottom]:slide-in-from-top-2", + "data-[side=left]:slide-in-from-right-2", + "data-[side=right]:slide-in-from-left-2", + "data-[side=top]:slide-in-from-bottom-2", + ], + ], variants: { popper: { - true: "max-h-[var(--radix-select-content-available-height)] data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", + true: [ + "max-h-[var(--radix-select-content-available-height)]", + "data-[side=bottom]:translate-y-1", + "data-[side=left]:-translate-x-1", + "data-[side=right]:translate-x-1", + "data-[side=top]:-translate-y-1", + ], }, }, }), Viewport: tv({ - base: "p-1", + base: ["p-1"], variants: { popper: { - true: "h-[var(--radix-popper-available-height)] w-full min-w-[var(--radix-popper-anchor-width)]", + true: [ + "h-[var(--radix-popper-available-height)] w-full min-w-[var(--radix-popper-anchor-width)]", + ], }, }, }), Label: tv({ - base: "py-1.5 pl-8 pr-2 text-sm font-semibold", + base: ["py-1.5 pl-8 pr-2 text-sm font-semibold"], }), Item: tv({ - base: "focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50", + base: [ + "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none", + "focus:bg-accent focus:text-accent-foreground", + "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", + ], }), Separator: tv({ - base: "bg-muted -mx-1 my-1 h-px", + base: ["bg-muted -mx-1 my-1 h-px"], }), ScrollButton: tv({ - base: "flex h-6 items-center justify-center", + base: ["flex h-6 items-center justify-center"], }), }; diff --git a/packages/ui/src/separator.tsx b/packages/ui/src/separator.tsx index 6cd887b..49883e6 100644 --- a/packages/ui/src/separator.tsx +++ b/packages/ui/src/separator.tsx @@ -1,6 +1,16 @@ import { forwardRef } from "react"; -import { cn } from "@fellipeutaka/styles"; +import { tv } from "@fellipeutaka/styles"; + +export const SeparatorStyles = tv({ + base: ["bg-border shrink-0"], + variants: { + orientation: { + horizontal: "h-px w-full", + vertical: "h-full w-px", + }, + }, +}); export type SeparatorProps = { orientation?: "horizontal" | "vertical"; @@ -19,15 +29,9 @@ export const Separator = forwardRef, SeparatorProps>( return (
); }, diff --git a/packages/ui/src/switch.tsx b/packages/ui/src/switch.tsx index 99eb5bf..c471e1c 100644 --- a/packages/ui/src/switch.tsx +++ b/packages/ui/src/switch.tsx @@ -7,10 +7,18 @@ import { Root, Thumb } from "@radix-ui/react-switch"; export const SwitchStyles = { Root: tv({ - base: "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", + base: [ + "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent outline-none transition-colors", + "focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2", + "disabled:cursor-not-allowed disabled:opacity-50", + "data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", + ], }), Thumb: tv({ - base: "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0", + base: [ + "bg-background pointer-events-none block h-5 w-5 rounded-full shadow-lg ring-0 transition-transform", + "data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0", + ], }), }; diff --git a/packages/ui/src/text.tsx b/packages/ui/src/text.tsx index abac272..6786c35 100644 --- a/packages/ui/src/text.tsx +++ b/packages/ui/src/text.tsx @@ -6,10 +6,11 @@ import { Slot } from "@radix-ui/react-slot"; export const TextStyles = tv({ variants: { variant: { - p: "leading-7 [&:not(:first-child)]:mt-6", - blockquote: "border-l-2 border-border pl-6 italic", - label: + p: ["leading-7 [&:not(:first-child)]:mt-6"], + blockquote: ["border-border border-l-2 pl-6 italic"], + label: [ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", + ], }, }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 306e9da..6983326 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -171,6 +171,9 @@ importers: packages/ui: dependencies: + '@radix-ui/react-checkbox': + specifier: ^1.0.4 + version: 1.0.4(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dialog': specifier: ^1.0.5 version: 1.0.5(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) @@ -1529,7 +1532,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 - dev: true /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} @@ -2647,6 +2649,33 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-checkbox@1.0.4(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.2 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.28)(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.28)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.28)(react@18.2.0) + '@types/react': 18.2.28 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.32)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: @@ -3534,7 +3563,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.2 '@types/react': 18.2.28 react: 18.2.0 dev: false @@ -3548,7 +3577,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.2 '@types/react': 18.2.32 react: 18.2.0 dev: true @@ -8833,7 +8862,7 @@ packages: /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /jsonfile@6.1.0: @@ -8841,7 +8870,7 @@ packages: dependencies: universalify: 2.0.0 optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /jsonparse@1.3.1: