Skip to content

Commit

Permalink
feat-fe: Switch Toggle 컴포넌트 구현 (#230)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeongwoo Park <[email protected]>
  • Loading branch information
github-actions[bot] and lurgi authored Aug 1, 2024
1 parent a311c59 commit d9a13a1
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import formatDate from '@utils/formatDate';
import DateInput from './index';

const meta: Meta<typeof DateInput> = {
title: 'Common/DateInput',
title: 'Common/Input/DateInput',
component: DateInput,
parameters: {
layout: 'centered',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import QuestionBox from '.';

const meta: Meta<typeof QuestionBox> = {
title: 'Common/QuestionBox',
title: 'Components/QuestionBox',
component: QuestionBox,
parameters: {
layout: 'centered',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useArgs } from '@storybook/preview-api';
import type { Meta, StoryObj } from '@storybook/react';
import ToggleSwitch from './index';

const meta: Meta<typeof ToggleSwitch> = {
title: 'Common/ToggleSwitch',
component: ToggleSwitch,
parameters: {
layout: 'centered',
docs: {
description: {
component: 'ToggleSwitch 컴포넌트는 스위치를 토글할 수 있는 기능을 제공합니다.',
},
},
},
tags: ['autodocs'],
argTypes: {
isChecked: {
description: '스위치의 체크 상태를 나타냅니다.',
control: { type: 'boolean' },
table: {
type: { summary: 'boolean' },
},
},
isDisabled: {
description: '스위치의 활성화 상태를 나타냅니다.',
control: { type: 'boolean' },
table: {
type: { summary: 'boolean' },
},
},
onChange: {
description: '스위치 상태 변경 시 호출되는 콜백 함수입니다.',
action: 'changed',
table: {
type: { summary: '() => void' },
},
},
},
decorators: [
(Child, context) => {
const [args, updateArgs] = useArgs();

const handleChange = () => {
updateArgs({ isChecked: !context.args.isChecked });
};

return (
<Child
args={{
...args,
onChange: handleChange,
}}
/>
);
},
],
};

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

export const Default: Story = {
args: {
isChecked: false,
},
};

export const Disabled: Story = {
args: {
isChecked: false,
isDisabled: true,
},
};

Default.parameters = {
docs: {
description: {
story: 'ToggleSwitch 컴포넌트의 기본 상태입니다.',
},
},
};
20 changes: 20 additions & 0 deletions frontend/src/components/common/ToggleSwitch/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import S, { StyleProps } from './style';

interface ToggleSwitchProps extends StyleProps {
onChange: () => void;
}

export default function ToggleSwitch({ isChecked, isDisabled, onChange }: ToggleSwitchProps) {
return (
<S.Switch
isChecked={isChecked}
isDisabled={isDisabled}
onClick={onChange}
>
<S.Knob
isChecked={isChecked}
isDisabled={isDisabled}
/>
</S.Switch>
);
}
61 changes: 61 additions & 0 deletions frontend/src/components/common/ToggleSwitch/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';

export interface StyleProps {
isChecked: boolean;
isDisabled: boolean;
}

const Switch = styled.div<StyleProps>`
--parent-width: 5rem;
--parent-padding: 0.3rem;
width: var(--parent-width);
aspect-ratio: 5/3;
background-color: ${({ isChecked, theme }) =>
isChecked ? theme.baseColors.purplescale[600] : theme.baseColors.grayscale[100]};
outline: 0.2rem solid
${({ isChecked, theme }) => (isChecked ? theme.baseColors.purplescale[600] : theme.baseColors.grayscale[700])};
border-radius: 99rem;
display: flex;
align-items: center;
padding: var(--parent-padding);
cursor: pointer;
transition: background-color 0.3s;
outline-offset: -0.1rem;
${({ isDisabled, theme }) =>
isDisabled &&
css({
backgroundColor: theme.baseColors.grayscale[400],
outline: `0.2rem solid ${theme.baseColors.grayscale[500]}`,
})}
`;

const Knob = styled.div<StyleProps>`
height: 100%;
aspect-ratio: 1/1;
background-color: ${({ isChecked, theme }) =>
isChecked ? theme.baseColors.grayscale[200] : theme.baseColors.grayscale[700]};
border-radius: 50%;
transform: ${({ isChecked }) =>
isChecked ? 'translate(calc(var(--parent-width) - var(--parent-padding)*2 - 100%))' : 'translate(0)'};
transition: transform 0.2s;
${({ isDisabled, theme }) =>
isDisabled &&
css({
backgroundColor: theme.baseColors.grayscale[700],
})}
`;

const S = {
Switch,
Knob,
};

export default S;

0 comments on commit d9a13a1

Please sign in to comment.