Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chip 에 onDelete prop 추가했다 #34

Merged
merged 5 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
64 changes: 52 additions & 12 deletions packages/co-design-core/src/components/Chips/Chip.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,22 @@ export const sizes = {
xlarge: 40,
};

const iconSizes = {
const checkIconSizes = {
xsmall: 10,
small: 12,
medium: 14,
large: 16,
xlarge: 18,
};

const closeIconSizes = {
xsmall: 14,
small: 16,
medium: 18,
large: 20,
xlarge: 22,
};

const padding = {
xsmall: 16,
small: 20,
Expand All @@ -33,13 +41,22 @@ const checkedPadding = {
xlarge: 15,
};

const deletablePadding = {
xsmall: 7.5,
small: 10,
medium: 11.5,
large: 13,
xlarge: 15,
};

interface ChipStyles {
radius: CoRadius | number;
size: CoSize;
color: CoPalette;
checked: boolean;
}

export default createStyles((theme, { radius, size, color }: ChipStyles, getRef) => {
export default createStyles((theme, { radius, size, color, checked }: ChipStyles, getRef) => {
const label = getRef('label');
const iconWrapper = getRef('iconWrapper');

Expand All @@ -51,8 +68,10 @@ export default createStyles((theme, { radius, size, color }: ChipStyles, getRef)
...defaultFontStyles(theme),
boxSizing: 'border-box',
color: theme.colorScheme === 'dark' ? theme.colors.white : theme.colors.black,
display: 'inline-block',
display: 'flex',
width: 'fit-content',
alignItems: 'center',
justifyContent: 'space-around',
userSelect: 'none',
border: `1px solid ${theme.colorScheme === 'dark' ? theme.palettes.gray[2] : theme.palettes.gray[3]}`,
borderRadius: getFieldValue(radius, theme.radius),
Expand All @@ -66,21 +85,30 @@ export default createStyles((theme, { radius, size, color }: ChipStyles, getRef)
transition: 'background-color 100ms ease',
WebkitTapHighlightColor: 'transparent',
backgroundColor: theme.colorScheme === 'dark' ? theme.palettes.gray[8] : theme.colors.white,

position: 'relative',
'&:hover': {
backgroundColor: theme.colorScheme === 'dark' ? theme.palettes.gray[8] : theme.palettes.gray[0],
},
},

iconWrapper: {
checkWrapper: {
ref: iconWrapper,
color: theme.palettes[color][theme.colorScheme === 'dark' ? 3 : 5],
width: getFieldValue(size, iconSizes),
maxWidth: getFieldValue(size, iconSizes),
height: getFieldValue(size, iconSizes),
width: getFieldValue(size, checkIconSizes),
maxWidth: getFieldValue(size, checkIconSizes),
height: getFieldValue(size, checkIconSizes),
marginRight: theme.spacing.small,
display: 'inline-block',
verticalAlign: 'middle',
overflow: 'hidden',
},

closeWrapper: {
display: 'flex',
alignItems: 'center',
ref: iconWrapper,
width: getFieldValue(size, closeIconSizes),
maxWidth: getFieldValue(size, closeIconSizes),
height: getFieldValue(size, closeIconSizes),
marginLeft: theme.spacing.small,
overflow: 'hidden',
},

Expand All @@ -103,11 +131,23 @@ export default createStyles((theme, { radius, size, color }: ChipStyles, getRef)
paddingLeft: getFieldValue(size, checkedPadding),
paddingRight: getFieldValue(size, checkedPadding),
border: `1px solid ${theme.palettes[color][theme.colorScheme === 'dark' ? 3 : 5]}`,
color: theme.palettes[color][theme.colorScheme === 'dark' ? 3 : 5],
},

deletable: {
paddingLeft: getFieldValue(size, deletablePadding),
paddingRight: getFieldValue(size, deletablePadding),
},

checkIcon: {
width: getFieldValue(size, iconSizes),
height: getFieldValue(size, iconSizes) / 1.1,
width: getFieldValue(size, checkIconSizes),
height: getFieldValue(size, checkIconSizes) / 1.1,
display: 'block',
},

deleteIcon: {
width: getFieldValue(size, closeIconSizes),
height: getFieldValue(size, closeIconSizes) / 1.1,
display: 'block',
},

Expand Down
28 changes: 23 additions & 5 deletions packages/co-design-core/src/components/Chips/Chip.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { forwardRef } from 'react';
import React, { MouseEventHandler, SyntheticEvent, forwardRef } from 'react';
import { useUncontrolled, useId } from '@co-design/hooks';
import { CoComponentProps, CoPalette, CoSize, CoRadius, useCoTheme, ClassNames } from '@co-design/styles';
import { View } from '../View';
import { CheckboxIcon } from './CheckboxIcon';
import useStyles from './Chip.style';
import CloseIcon from './CloseIcon';
import { Text } from '../Text';

export type ChipStylesNames = ClassNames<typeof useStyles>;

Expand Down Expand Up @@ -55,6 +57,10 @@ export interface ChipProps extends CoComponentProps<ChipStylesNames>, Omit<React
*/
onChange?(checked: boolean): void;

/**
* Chip 에 Delete Icon 을 추가하고, 클릭 시 onDelete 함수가 실행됩니다.
*/
onDelete?(): void;
__staticSelector?: string;
}

Expand All @@ -74,6 +80,7 @@ export const Chip = forwardRef<HTMLInputElement, ChipProps>(
checked,
defaultChecked,
onChange,
onDelete,
co,
overrideStyles,
...props
Expand All @@ -83,7 +90,6 @@ export const Chip = forwardRef<HTMLInputElement, ChipProps>(
const uuid = useId(id);
const theme = useCoTheme();
const _color = color || theme.primaryColor;
const { classes, cx } = useStyles({ radius, size, color: _color }, { overrideStyles, name: __staticSelector });

const [value, setValue] = useUncontrolled({
value: checked,
Expand All @@ -93,25 +99,37 @@ export const Chip = forwardRef<HTMLInputElement, ChipProps>(
rule: (val) => typeof val === 'boolean',
});

const { classes, cx } = useStyles({ radius, size, color: _color, checked: value }, { overrideStyles, name: __staticSelector });

return (
<View className={cx(classes.root, className)} style={style} co={co}>
<input
type={type}
className={classes.input}
checked={value}
onChange={(event) => setValue(event.currentTarget.checked)}
onChange={(event) => {
setValue(event.currentTarget.checked);
}}
id={uuid}
disabled={disabled}
ref={ref}
{...props}
/>
<label htmlFor={uuid} className={cx(classes.label, { [classes.checked]: value, [classes.disabled]: disabled })}>
<label
htmlFor={uuid}
className={cx(classes.label, { [classes.checked]: value, [classes.disabled]: disabled, [classes.deletable]: !!onDelete })}
>
{value && (
<span className={classes.iconWrapper}>
<span className={classes.checkWrapper}>
<CheckboxIcon indeterminate={false} className={classes.checkIcon} />
</span>
)}
{children}
{onDelete && (
<span className={classes.closeWrapper} onClick={onDelete}>
<CloseIcon className={classes.deleteIcon} />
</span>
)}
</label>
</View>
);
Expand Down
13 changes: 13 additions & 0 deletions packages/co-design-core/src/components/Chips/CloseIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as React from 'react';
import type { SVGProps } from 'react';

const CloseIcon = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 24 24" {...props}>
<path
fill="currentColor"
d="M17.65 7.06 16.59 6l-4.76 4.77L7.06 6 6 7.06l4.77 4.77L6 16.59l1.06 1.06 4.77-4.76 4.76 4.76 1.06-1.06-4.76-4.76 4.76-4.77Z"
/>
</svg>
);

export default CloseIcon;
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useState } from 'react';
import { Chip } from '../Chip';

export default {
title: '@co-design/core/Chip',
component: Chip,
argTypes: {
size: {
options: ['xsmall', 'small', 'medium', 'large', 'xlarge'],
control: { type: 'inline-radio' },
},
color: {
options: ['purple', 'gray', 'red', 'blue', 'orange', 'green'],
control: { type: 'inline-radio' },
},
radius: {
options: ['small', 'medium', 'large', 'round'],
control: { type: 'inline-radio' },
},
checked: {
control: { type: 'boolean' },
},
},
args: {
size: 'small',
color: 'purple',
radius: 'medium',
},
};

export const Default = {
render: (props) => {
return <Chip {...props}>React</Chip>;
},
};

export const Deletable = {
render: (props) => {
const [chips, setChips] = useState(['React', 'Angular', 'Svelte', 'Vue']);
return (
<div>
{chips.map((chip) => (
<Chip
key={chip}
value={chip}
radius={props.radius}
size={props.size}
onDelete={() => {
setChips(chips.filter((c) => c !== chip));
}}
>
{chip}
</Chip>
))}
</div>
);
},
};