diff --git a/src/components/common/Toggle/index.tsx b/src/components/common/Toggle/index.tsx new file mode 100644 index 0000000..ff21b30 --- /dev/null +++ b/src/components/common/Toggle/index.tsx @@ -0,0 +1,97 @@ +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; +import * as React from 'react'; + +import { composeEventHandlers } from '@/libs/event'; +import { SizeType } from '@/types/size'; + +export interface ToggleProps extends React.ComponentPropsWithoutRef<'button'> { + size?: SizeType; + selected?: boolean; +} + +const ButtonSizeStyles = ({ size }: { size: SizeType }) => { + switch (size) { + case 'sm': + return css` + padding: 0.25rem; + `; + case 'lg': + return css` + padding: 0.75rem; + `; + case 'md': + default: + return css` + padding: 0.5rem; + `; + } +}; + +const StyledToggle = styled.button` + display: inline-flex; + padding: 0.5rem; + outline: none; + border-radius: 0.375rem; + font-weight: 500; + justify-content: center; + align-items: center; + white-space: nowrap; + cursor: pointer; + background-color: transparent; + border: 1px solid transparent; + + &:focus { + box-shadow: 0 0 5px ${({ theme }) => theme.colors.gray['200']}; + } + + ${({ disabled, theme }) => + disabled && + css` + opacity: 0.7; + background-color: ${theme.colors.gray['100']}; + cursor: not-allowed; + `} + + ${({ selected, theme }) => + selected + ? css` + border: 1px solid ${theme.colors.gray['300']}; + background-color: ${theme.colors.gray['200']}; + ` + : css` + &:hover { + opacity: 0.5; + background-color: ${theme.colors.gray['200']}; + } + `} + + + ${({ size }) => size && ButtonSizeStyles({ size })} +`; + +export const Toggle = React.forwardRef( + ({ className, selected = false, size, disabled = false, ...props }, ref) => { + const [sel, setSel] = React.useState(selected); + + React.useEffect(() => { + setSel(selected); + }, [selected]); + + return ( + { + setSel(prev => !prev); + })} + {...props} + /> + ); + }, +); +Toggle.displayName = 'Input'; diff --git a/src/components/common/index.ts b/src/components/common/index.ts index f988346..9a55011 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -10,5 +10,6 @@ export * from './Label'; export * from './Switch'; export * from './Textarea'; export * from './Toast'; +export * from './Toggle'; export * from './Tooltip'; export * from './Checkbox'; diff --git a/src/stories/common/Toggle.stories.tsx b/src/stories/common/Toggle.stories.tsx new file mode 100644 index 0000000..2a30584 --- /dev/null +++ b/src/stories/common/Toggle.stories.tsx @@ -0,0 +1,85 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; + +// import React from 'react'; +import { Toggle, Tooltip } from '../..'; + +const meta = { + title: 'common/Toggle', + component: Toggle, + tags: ['autodocs'], + args: { + children: 'Toggle', + }, + argTypes: {}, + parameters: { + layout: 'centered', + componentSubtitle: 'Base Toggle', + docs: { + description: { + component: `Base Toggle of Everything.`, + }, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + children: ( + + + + + ), + }, +}; + +export const Small: Story = { + args: { + size: 'sm', + ...Default.args, + }, +}; + +export const Large: Story = { + args: { + size: 'lg', + ...Default.args, + }, +}; + +export const WithTooltip: Story = { + args: { + ...Default.args, + }, + decorators: [ + Story => { + return ( + + + + ); + }, + ], +}; + +export const Disabled: Story = { + args: { + disabled: true, + ...Default.args, + }, +};