Skip to content
This repository has been archived by the owner on Feb 10, 2025. It is now read-only.

Commit

Permalink
fix: refactored Switch and Checkbox from shadcn (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
nijoe1 authored Jan 17, 2025
1 parent 31bcaed commit c960f15
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { PlusIcon } from "@heroicons/react/outline";
import { X } from "lucide-react";

import { Button } from "@/primitives/Button";
import { Checkbox } from "@/ui-shadcn/checkbox";
import { Checkbox } from "@/primitives/Checkbox";
import { Switch } from "@/primitives/Switch";
import { Input } from "@/ui-shadcn/input";
import { Label } from "@/ui-shadcn/label";
import { Select, SelectTrigger, SelectContent, SelectItem } from "@/ui-shadcn/select";
import { Switch } from "@/ui-shadcn/switch";

import { inputTypes } from "./utils";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useFormContext, Controller } from "react-hook-form";

import moment from "moment-timezone";

import { Checkbox } from "@/ui-shadcn/checkbox";
import { Checkbox } from "@/primitives/Checkbox";
import { Label } from "@/ui-shadcn/label";
import { Select, SelectTrigger, SelectContent, SelectItem } from "@/ui-shadcn/select";

Expand Down
94 changes: 94 additions & 0 deletions src/primitives/Checkbox/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type { Meta, StoryObj } from "@storybook/react";

import { Checkbox } from "./Checkbox";

const meta: Meta<typeof Checkbox> = {
title: "Primitives/Checkbox",
component: Checkbox,
tags: ["autodocs"],
};

export default meta;
type Story = StoryObj<typeof Checkbox>;

// Small Checkboxes Story
export const SmallCheckboxes: Story = {
render: () => (
<div className="flex items-center gap-4">
<Checkbox size="sm" color="moss" defaultChecked />
<Checkbox size="sm" color="black" defaultChecked />
<Checkbox size="sm" color="white" defaultChecked />
<Checkbox size="sm" color="purple" defaultChecked />
</div>
),
};

// Medium Checkboxes Story
export const MediumCheckboxes: Story = {
render: () => (
<div className="flex items-center gap-4">
<Checkbox size="md" color="moss" defaultChecked />
<Checkbox size="md" color="black" defaultChecked />
<Checkbox size="md" color="white" defaultChecked />
<Checkbox size="md" color="purple" defaultChecked />
</div>
),
};

// Large Checkboxes Story
export const LargeCheckboxes: Story = {
render: () => (
<div className="flex items-center gap-4">
<Checkbox size="lg" color="moss" defaultChecked />
<Checkbox size="lg" color="black" defaultChecked />
<Checkbox size="lg" color="white" defaultChecked />
<Checkbox size="lg" color="purple" defaultChecked />
</div>
),
};

// Interactive Checkbox Story with Controls
export const Interactive: Story = {
args: {
size: "md",
color: "moss",
defaultChecked: true,
},
argTypes: {
size: {
control: "select",
options: ["sm", "md", "lg"],
},
color: {
control: "select",
options: ["moss", "black", "white", "purple"],
},
defaultChecked: {
control: "boolean",
},
},
};

// States Story
export const States: Story = {
render: () => (
<div className="flex flex-col gap-4">
<div className="flex items-center gap-4">
<Checkbox color="moss" />
<span className="text-sm">Unchecked</span>
</div>
<div className="flex items-center gap-4">
<Checkbox color="moss" defaultChecked />
<span className="text-sm">Checked</span>
</div>
<div className="flex items-center gap-4">
<Checkbox color="moss" disabled />
<span className="text-sm">Disabled Unchecked</span>
</div>
<div className="flex items-center gap-4">
<Checkbox color="moss" disabled defaultChecked />
<span className="text-sm">Disabled Checked</span>
</div>
</div>
),
};
68 changes: 68 additions & 0 deletions src/primitives/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"use client";

import * as React from "react";

import { CheckIcon } from "@heroicons/react/solid";
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { tv, VariantProps } from "tailwind-variants";

import { cn } from "@/lib/utils";

const checkboxVariants = tv({
slots: {
base: "peer size-4 rounded-sm border border-moss-300 text-center ring-offset-white hover:border-moss-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-moss-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-moss-500 data-[state=checked]:text-grey-50",
},
variants: {
size: {
sm: {
base: "size-4",
},
md: {
base: "size-5",
},
lg: {
base: "size-6",
},
},
color: {
moss: {
base: "border-moss-700 hover:border-moss-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-moss-900 focus-visible:ring-offset-2 data-[state=checked]:bg-moss-700 data-[state=unchecked]:bg-transparent data-[state=checked]:text-white data-[state=unchecked]:text-white",
},
black: {
base: "border-grey-300 hover:border-grey-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-grey-500 focus-visible:ring-offset-2 data-[state=checked]:bg-black data-[state=unchecked]:bg-transparent data-[state=checked]:text-grey-50 data-[state=unchecked]:text-grey-50",
},
white: {
base: "border-black hover:border-black focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2 data-[state=checked]:bg-white data-[state=unchecked]:bg-transparent data-[state=checked]:text-black data-[state=unchecked]:text-black dark:border-grey-50 dark:hover:border-grey-200 dark:focus-visible:ring-grey-200 dark:data-[state=checked]:bg-grey-50 dark:data-[state=checked]:text-black dark:data-[state=unchecked]:text-grey-50",
},
purple: {
base: "border-purple-700 hover:border-purple-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-900 focus-visible:ring-offset-2 data-[state=checked]:bg-purple-700 data-[state=unchecked]:bg-transparent data-[state=checked]:text-grey-50 data-[state=unchecked]:text-white",
},
},
},
defaultVariants: {
size: "sm",
color: "moss",
},
});

export type CheckboxVariants = VariantProps<typeof checkboxVariants>;

const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> & CheckboxVariants
>(({ className, ...props }, ref) => {
const { size, color, ...rest } = props;
const { base } = checkboxVariants({ size, color });

return (
<CheckboxPrimitive.Root ref={ref} className={cn(base(), className)} {...rest}>
<CheckboxPrimitive.Indicator>
<CheckIcon />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
);
});

Checkbox.displayName = CheckboxPrimitive.Root.displayName;

export { Checkbox };
1 change: 1 addition & 0 deletions src/primitives/Checkbox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Checkbox } from "./Checkbox";
107 changes: 107 additions & 0 deletions src/primitives/Switch/Switch.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import type { Meta, StoryObj } from "@storybook/react";

import { Switch } from "./Switch";

const meta: Meta<typeof Switch> = {
title: "Primitives/Switch",
component: Switch,
tags: ["autodocs"],
};

export default meta;
type Story = StoryObj<typeof Switch>;

// Color Variants Story
export const ColorVariants: Story = {
render: () => (
<div className="flex flex-col gap-4">
<div className="flex items-center gap-4">
<Switch color="moss" defaultChecked />
<span className="text-sm">Moss</span>
</div>
<div className="flex items-center gap-4">
<Switch color="black" defaultChecked />
<span className="text-sm">Black</span>
</div>
<div className="flex items-center gap-4">
<Switch color="purple" defaultChecked />
<span className="text-sm">Purple</span>
</div>
<div className="flex items-center gap-4">
<Switch color="white" defaultChecked />
<span className="text-sm">White</span>
</div>
</div>
),
};

// States Story
export const States: Story = {
render: () => (
<div className="flex flex-col gap-4">
<div className="flex items-center gap-4">
<Switch color="moss" />
<span className="text-sm">Unchecked</span>
</div>
<div className="flex items-center gap-4">
<Switch color="moss" defaultChecked />
<span className="text-sm">Checked</span>
</div>
<div className="flex items-center gap-4">
<Switch color="moss" disabled />
<span className="text-sm">Disabled Unchecked</span>
</div>
<div className="flex items-center gap-4">
<Switch color="moss" disabled defaultChecked />
<span className="text-sm">Disabled Checked</span>
</div>
</div>
),
};

// Interactive Story with Controls
export const Interactive: Story = {
args: {
color: "moss",
defaultChecked: true,
disabled: false,
},
argTypes: {
color: {
control: "select",
options: ["moss", "black", "purple", "white"],
},
defaultChecked: {
control: "boolean",
},
disabled: {
control: "boolean",
},
},
};

// Group Story
export const Group: Story = {
render: () => (
<div className="space-y-8">
<div className="flex items-center justify-between">
<label htmlFor="airplane-mode" className="text-sm font-medium">
Airplane Mode
</label>
<Switch id="airplane-mode" color="moss" />
</div>
<div className="flex items-center justify-between">
<label htmlFor="wifi" className="text-sm font-medium">
Wi-Fi
</label>
<Switch id="wifi" color="purple" defaultChecked />
</div>
<div className="flex items-center justify-between">
<label htmlFor="notifications" className="text-sm font-medium">
Notifications
</label>
<Switch id="notifications" color="black" defaultChecked />
</div>
</div>
),
};
59 changes: 59 additions & 0 deletions src/primitives/Switch/Switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"use client";

import * as React from "react";

import * as SwitchPrimitives from "@radix-ui/react-switch";
import { tv, VariantProps } from "tailwind-variants";

import { cn } from "@/lib/utils";

const switchVariants = tv({
slots: {
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-black focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-moss-700 data-[state=unchecked]:bg-grey-100",
thumb:
"pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
},
variants: {
color: {
moss: {
base: "data-[state=checked]:bg-moss-700 data-[state=unchecked]:bg-grey-100",
thumb: "bg-white",
},
black: {
base: "data-[state=checked]:bg-black data-[state=unchecked]:bg-grey-100",
thumb: "bg-white",
},
purple: {
base: "data-[state=checked]:bg-purple-700 data-[state=unchecked]:bg-grey-100",
thumb: "bg-white",
},
white: {
base: "data-[state=checked]:bg-white data-[state=unchecked]:bg-grey-100",
thumb: "bg-black",
},
},
},
defaultVariants: {
color: "moss",
},
});

type SwitchVariants = VariantProps<typeof switchVariants>;

const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> & SwitchVariants
>(({ className, ...props }, ref) => {
const { color, ...rest } = props;
const { base, thumb } = switchVariants({ color });

return (
<SwitchPrimitives.Root className={cn(base(), className)} {...rest} ref={ref}>
<SwitchPrimitives.Thumb className={thumb()} />
</SwitchPrimitives.Root>
);
});

Switch.displayName = SwitchPrimitives.Root.displayName;

export { Switch };
1 change: 1 addition & 0 deletions src/primitives/Switch/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Switch } from "./Switch";
27 changes: 0 additions & 27 deletions src/ui-shadcn/checkbox.tsx

This file was deleted.

Loading

0 comments on commit c960f15

Please sign in to comment.