Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
"tailwind-merge": "^2.6.0"
},
"peerDependencies": {
"@radix-ui/react-switch": "^1.0.0",
"framer-motion": "^11.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
Expand Down
53 changes: 45 additions & 8 deletions src/shared/ui/Switch/Switch.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,60 @@
import * as SwitchPrimitives from '@radix-ui/react-switch';
import { useState } from 'react';

import { COLORS, SwitchColor } from '@/shared/lib/colors';

type Props = React.ComponentProps<typeof SwitchPrimitives.Root> & {
type Props = Omit<React.ComponentProps<'button'>, 'onClick'> & {
/**
* color of the switch.
* @default 'primary'
*/
color?: SwitchColor;
checked?: boolean;
defaultChecked?: boolean;
onCheckedChange?: (checked: boolean) => void;
};

export function Switch({ color = 'primary', ...props }: Props) {
export function Switch({
color = 'primary',
checked,
defaultChecked = false,
onCheckedChange,
className,
disabled,
...props
}: Props) {
const [uncontrolledChecked, setUncontrolledChecked] = useState(defaultChecked);
const isChecked = checked ?? uncontrolledChecked;

const handleClick = () => {
if (disabled) return;

const next = !isChecked;
if (checked === undefined) {
setUncontrolledChecked(next);
}
onCheckedChange?.(next);
};

return (
<SwitchPrimitives.Root
<button
{...props}
className="group flex h-3.5 w-7 items-center rounded-full shadow-lg data-[state=checked]:bg-[var(--switch-color)] data-[state=unchecked]:bg-gray-500"
style={color && ({ '--switch-color': COLORS[color] } as React.CSSProperties)}
type="button"
role="switch"
disabled={disabled}
onClick={handleClick}
aria-checked={isChecked}
data-state={isChecked ? 'checked' : 'unchecked'}
className={[
'group flex h-3.5 w-7 items-center rounded-full shadow-lg',
'data-[state=checked]:bg-[var(--switch-color)]',
'data-[state=unchecked]:bg-gray-500',
className,
]
.filter(Boolean)
.join(' ')}
style={{ '--switch-color': COLORS[color] } as React.CSSProperties}
Comment on lines +47 to +55
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

선택적 개선: 명시적인 disabled 및 focus 스타일 고려.

현재는 네이티브 disabled 속성과 브라우저 기본 포커스 스타일에 의존하고 있습니다. 디자인 시스템의 일관성을 위해 명시적인 스타일을 추가하는 것을 고려해보세요:

  • disabled:opacity-50 disabled:cursor-not-allowed - 비활성 상태 시각적 피드백
  • focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 - 접근성 향상을 위한 포커스 표시
🔎 선택적 스타일링 개선 제안
      className={[
        'group flex h-3.5 w-7 items-center rounded-full shadow-lg',
        'data-[state=checked]:bg-[var(--switch-color)]',
        'data-[state=unchecked]:bg-gray-500',
+       'disabled:opacity-50 disabled:cursor-not-allowed',
+       'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-300',
        className,
      ]
🤖 Prompt for AI Agents
In @src/shared/ui/Switch/Switch.tsx around lines 47 - 55, The switch root
rendering builds className and inline style using the className prop, the 'group
...' base classes, and COLORS[color]; update this to include explicit disabled
and focus-visible utility classes (e.g., add disabled:opacity-50
disabled:cursor-not-allowed and focus-visible:outline focus-visible:outline-2
focus-visible:outline-offset-2) so the component no longer relies solely on
browser defaults; modify the className array in Switch.tsx (where className and
style are set) to append these focus/disabled classes and ensure they coexist
with the existing data-[state=...] and COLORS[color] usage.

>
<SwitchPrimitives.Thumb className="h-5 w-5 -translate-x-1.5 rounded-full bg-white shadow-inner drop-shadow-md duration-100 group-data-[state=checked]:translate-x-3.5" />
</SwitchPrimitives.Root>
<span className="h-5 w-5 -translate-x-1.5 rounded-full bg-white shadow-inner drop-shadow-md duration-100 group-data-[state=checked]:translate-x-3.5" />
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

transition 클래스 누락으로 애니메이션이 동작하지 않습니다.

duration-100 클래스가 있지만 transition 또는 transition-transform 클래스가 없어서 스위치 노브가 부드럽게 슬라이드되지 않고 즉시 점프합니다. Tailwind에서는 duration-* 클래스만으로는 트랜지션이 활성화되지 않습니다.

🔎 트랜지션 수정 제안
-      <span className="h-5 w-5 -translate-x-1.5 rounded-full bg-white shadow-inner drop-shadow-md duration-100 group-data-[state=checked]:translate-x-3.5" />
+      <span className="h-5 w-5 -translate-x-1.5 rounded-full bg-white shadow-inner drop-shadow-md transition-transform duration-100 group-data-[state=checked]:translate-x-3.5" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span className="h-5 w-5 -translate-x-1.5 rounded-full bg-white shadow-inner drop-shadow-md duration-100 group-data-[state=checked]:translate-x-3.5" />
<span className="h-5 w-5 -translate-x-1.5 rounded-full bg-white shadow-inner drop-shadow-md transition-transform duration-100 group-data-[state=checked]:translate-x-3.5" />
🤖 Prompt for AI Agents
In @src/shared/ui/Switch/Switch.tsx at line 57, The switch knob span in the
Switch component is missing a transition class so the knob jumps instead of
animating; update the span (the element with className containing "duration-100"
in Switch.tsx) to also include "transition" or "transition-transform" (e.g., add
transition-transform) so the duration takes effect and the
group-data-[state=checked]:translate-x-3.5 animates smoothly.

</button>
);
}
1 change: 0 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export default defineConfig({
'react-dom',
'react/jsx-runtime',
'framer-motion',
'@radix-ui/react-switch',
'tailwindcss',
],
output: {
Expand Down
Loading