Skip to content

Commit

Permalink
Merge pull request #82 from ruru-m07/refactor-switch-component
Browse files Browse the repository at this point in the history
refactor(component: switch): make Switch component self-dependent by removing @radix-ui/react-switch dependency
  • Loading branch information
ruru-m07 authored Oct 20, 2024
2 parents 8c6833a + 62c6dea commit 6426efb
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 89 deletions.
15 changes: 14 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
pnpm run --filter=./apps/www build:registry && pnpm prettier
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo "Running pre-commit hook..."
if ! pnpm run --filter=./apps/www build:registry; then
echo "Error: Failed to build registry. Commit aborted."
exit 1
fi
echo "Registry built successfully. Running Prettier..."
if ! pnpm prettier; then
echo "Error: Prettier formatting failed. Commit aborted."
exit 1
fi
echo "Pre-commit hook completed successfully."
8 changes: 4 additions & 4 deletions apps/www/components/SwitchEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
import React, { useState } from "react";
import { Switch } from "ruru-ui/components/switch";

const SwiychEvent = (): React.ReactNode => {
const SwitchEvent = (): React.ReactNode => {
const [value, setValue] = useState<boolean>(false);
return (
<div className="flex flex-col justify-center items-center">
<div className="flex items-center space-x-2">
<Switch onCheckedChange={(e) => setValue(e)} id="toggle-animation" />
<label htmlFor="toggle value"> toggle value </label>
<Switch onCheckedChange={(e) => setValue(e)} id="toggle-value" />
<label htmlFor="toggle-value"> toggle value </label>
</div>
<p>{String(value)}</p>
</div>
);
};

export default SwiychEvent;
export default SwitchEvent;
36 changes: 18 additions & 18 deletions apps/www/content/docs/components/switch.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ preview: switch
import { Tabs, Tab } from "fumadocs-ui/components/tabs";
import { Switch } from "ruru-ui/components/switch";
import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
import SwiychEvent from "../../../components/SwitchEvent.tsx";
import SwitchEvent from "../../../components/SwitchEvent.tsx";

## Installation

Expand Down Expand Up @@ -55,8 +55,7 @@ import SwiychEvent from "../../../components/SwitchEvent.tsx";
</Tab>
<Tab className={"-mt-8"} value="Code">
```tsx
// [!code word:Switch]
import { Switch } from "your-ui-library/components/switch";
import { Switch } from "ruru-ui/components/switch";

export function Demo() {
return <Switch />;
Expand All @@ -76,7 +75,7 @@ export function Demo() {
<Tab className={"-mt-8"} value="Code">
```tsx
// [!code word:disabled]
import { Switch } from "your-ui-library/components/switch";
import { Switch } from "ruru-ui/components/switch";

export function SwitchDemo() {
return <Switch disabled />;
Expand All @@ -86,30 +85,21 @@ export function SwitchDemo() {

</Tabs>

## Props

| Name | Type | Default | Description |
| ------------- | ------- | ------- | -------------------------------------- |
| **className** | **string** | `"" ` | Additional class names for the switch. |
| **disabled** | **boolean** | `false` | Flag to disable the switch. |

## Examples

### Switch with lables

<Tabs items={["Preview", "Code"]}>
<Tab className={"flex justify-center"} value="Preview">
<div className="flex items-center space-x-2">
<Switch id="airplane-mode" />
<label htmlFor="airplane-mode">toggle Mode</label>
<Switch id="airplane-mode-demo" />
<label htmlFor="airplane-mode-demo">toggle Mode</label>
</div>
</Tab>
<Tab className={"-mt-8"} value="Code">
```tsx
// [!code word:airplane-mode]
// [!code word:id]
// [!code word:htmlFor]
import { Switch } from "your-ui-library/components/switch";
import { Switch } from "ruru-ui/components/switch";

export function Demo() {
return (
Expand All @@ -134,7 +124,7 @@ export function Demo() {
<Tab className={"-mt-8"} value="Code">
```tsx
// [!code word:defaultChecked]
import { Switch } from "your-ui-library/components/switch";
import { Switch } from "ruru-ui/components/switch";

export function SwitchDemo() {
return <Switch defaultChecked />;
Expand All @@ -148,7 +138,7 @@ export function SwitchDemo() {

<Tabs items={["Preview", "Code"]}>
<Tab className={"flex justify-center"} value="Preview">
<SwiychEvent />
<SwitchEvent />
</Tab>
<Tab className={"-mt-8"} value="Code">
```tsx
Expand Down Expand Up @@ -179,3 +169,13 @@ export default SwiychDemo;
</Tab>
</Tabs>

## Props

| Name | Type | Default | Description |
| ------------------- | ------------------------ | ----------- | ------------------------------------------------------------ |
| **className** | **string** | `"" ` | Additional class names for the switch. |
| **disabled** | **boolean** | `false` | Flag to disable the switch. |
| **id** | **string** | `""` | Id for the switch. |
| **defaultChecked** | **boolean** | `false` | Flag to set the switch to checked by default. |
| **checked** | **boolean** | `false` | Flag to set the switch to checked. |
| **onCheckedChange** | **(e: boolean) => void** | `undefined` | Callback function that is called when the switch is toggled. |
3 changes: 1 addition & 2 deletions apps/www/public/registry/components/switch.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
{
"name": "switch",
"dependencies": ["@radix-ui/react-switch"],
"files": [
{
"name": "switch.tsx",
"content": "import * as React from \"react\";\nimport * as SwitchPrimitives from \"@radix-ui/react-switch\";\n\nimport { cn } from \"@/utils/cn\";\n\nconst Switch = React.forwardRef<\n React.ElementRef<typeof SwitchPrimitives.Root>,\n React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>\n>(({ className, ...props }, ref) => (\n <SwitchPrimitives.Root\n className={cn(\n \"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm 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 bg-accent data-[state=unchecked]:bg-input\",\n className,\n )}\n {...props}\n ref={ref}\n >\n <SwitchPrimitives.Thumb\n className={cn(\n \"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0\",\n )}\n />\n </SwitchPrimitives.Root>\n));\nSwitch.displayName = SwitchPrimitives.Root.displayName;\n\nexport { Switch };\n"
"content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\n\ninterface SwitchProps extends React.ComponentPropsWithoutRef<\"button\"> {\n checked?: boolean;\n onCheckedChange?: (checked: boolean) => void;\n defaultChecked?: boolean;\n}\n\n\nconst Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(\n (\n { className, checked, onCheckedChange, defaultChecked = false, ...props },\n ref,\n ) => {\n const [isCheckedInternal, setIsCheckedInternal] =\n React.useState(defaultChecked);\n\n const isControlled = checked !== undefined;\n const isCheckedValue = isControlled ? checked : isCheckedInternal;\n\n const handleClick = () => {\n const newChecked = !isCheckedValue;\n if (!isControlled) {\n setIsCheckedInternal(newChecked);\n }\n if (onCheckedChange) {\n onCheckedChange(newChecked);\n }\n };\n\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isCheckedValue}\n className={cn(\n \"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm 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\",\n isCheckedValue ? \"bg-primary\" : \"bg-accent\",\n className,\n )}\n onClick={handleClick}\n ref={ref}\n {...props}\n >\n <span\n className={cn(\n \"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform\",\n isCheckedValue ? \"translate-x-4\" : \"translate-x-0\",\n )}\n />\n </button>\n );\n },\n);\n\nSwitch.displayName = \"Switch\";\n\nexport { Switch };\n"
}
],
"type": "components:ui"
Expand Down
1 change: 0 additions & 1 deletion apps/www/public/registry/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
},
{
"name": "switch",
"dependencies": ["@radix-ui/react-switch"],
"files": ["switch.tsx"],
"type": "components:ui"
},
Expand Down
1 change: 0 additions & 1 deletion apps/www/registry/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export const ui: Registry = [
{
name: "switch",
type: "components:ui",
dependencies: ["@radix-ui/react-switch"],
files: ["switch.tsx"],
},
{
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ruru-ui",
"version": "2.2.4",
"version": "2.2.5",
"description": "Ruru UI Components",
"publishConfig": {
"access": "public"
Expand Down Expand Up @@ -95,7 +95,6 @@
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@swc/core": "^1.6.13",
Expand Down
83 changes: 60 additions & 23 deletions packages/ui/src/components/switch.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,75 @@
import * as React from "react";
import * as SwitchPrimitives from "@radix-ui/react-switch";
"use client";

import * as React from "react";
import { cn } from "@/utils/cn";

interface SwitchProps extends React.ComponentPropsWithoutRef<"button"> {
checked?: boolean;
onCheckedChange?: (checked: boolean) => void;
defaultChecked?: boolean;
}

/**
* Switch component
*
* @param {string} className - Additional class names for the switch.
* @param {React.Ref<React.ElementRef<typeof SwitchPrimitives.Root>>} ref - Forwarded ref.
* @param {boolean} checked - Whether the switch is checked.
* @param {(checked: boolean) => void} onCheckedChange - Callback when the switch is checked or unchecked.
* @param {boolean} defaultChecked - Whether the switch is checked by default.
* @param {React.Ref<HTMLButtonElement>} ref - Forwarded ref.
*
* @example
*
* ```tsx
* <Switch />
* ```
*/
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm 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 bg-accent data-[state=unchecked]:bg-input",
className,
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
)}
/>
</SwitchPrimitives.Root>
));
Switch.displayName = SwitchPrimitives.Root.displayName;
const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
(
{ className, checked, onCheckedChange, defaultChecked = false, ...props },
ref,
) => {
const [isCheckedInternal, setIsCheckedInternal] =
React.useState(defaultChecked);

const isControlled = checked !== undefined;
const isCheckedValue = isControlled ? checked : isCheckedInternal;

const handleClick = () => {
const newChecked = !isCheckedValue;
if (!isControlled) {
setIsCheckedInternal(newChecked);
}
if (onCheckedChange) {
onCheckedChange(newChecked);
}
};

return (
<button
type="button"
role="switch"
aria-checked={isCheckedValue}
className={cn(
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm 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",
isCheckedValue ? "bg-primary" : "bg-accent",
className,
)}
onClick={handleClick}
ref={ref}
{...props}
>
<span
className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform",
isCheckedValue ? "translate-x-4" : "translate-x-0",
)}
/>
</button>
);
},
);

Switch.displayName = "Switch";

export { Switch };
Loading

0 comments on commit 6426efb

Please sign in to comment.