forked from cerc-io/snowballtools-base
⚡️ feat: implement tags component
This commit is contained in:
parent
496404195c
commit
fb932eeb04
93
packages/frontend/src/components/shared/Tags/Tags.theme.ts
Normal file
93
packages/frontend/src/components/shared/Tags/Tags.theme.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { tv } from 'tailwind-variants';
|
||||
import type { VariantProps } from 'tailwind-variants';
|
||||
|
||||
export const tagsTheme = tv(
|
||||
{
|
||||
slots: {
|
||||
wrapper: ['flex', 'gap-1.5', 'rounded-lg', 'border'],
|
||||
icon: ['h-4', 'w-4'],
|
||||
label: ['font-inter', 'text-xs'],
|
||||
},
|
||||
variants: {
|
||||
type: {
|
||||
attention: {
|
||||
icon: ['text-elements-warning'],
|
||||
},
|
||||
negative: {
|
||||
icon: ['text-elements-danger'],
|
||||
},
|
||||
positive: {
|
||||
icon: ['text-elements-success'],
|
||||
},
|
||||
emphasized: {
|
||||
icon: ['text-elements-on-secondary'],
|
||||
},
|
||||
neutral: {
|
||||
icon: ['text-elements-mid-em'],
|
||||
},
|
||||
},
|
||||
style: {
|
||||
default: {},
|
||||
minimal: {
|
||||
wrapper: ['border-border-interactive', 'bg-controls-tertiary'],
|
||||
label: ['text-elements-high-em'],
|
||||
},
|
||||
},
|
||||
size: {
|
||||
sm: {
|
||||
wrapper: ['px-2', 'py-2'],
|
||||
},
|
||||
xs: {
|
||||
wrapper: ['px-2', 'py-1.5'],
|
||||
},
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
type: 'attention',
|
||||
style: 'default',
|
||||
class: {
|
||||
wrapper: ['border-orange-200', 'bg-orange-50'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'negative',
|
||||
style: 'default',
|
||||
class: {
|
||||
wrapper: ['border-rose-200', 'bg-rose-50'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'positive',
|
||||
style: 'default',
|
||||
class: {
|
||||
wrapper: ['border-emerald-200', 'bg-emerald-50'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'emphasized',
|
||||
style: 'default',
|
||||
class: {
|
||||
wrapper: ['border-snowball-200', 'bg-snowball-50'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'neutral',
|
||||
style: 'default',
|
||||
class: {
|
||||
wrapper: ['border-gray-200', 'bg-gray-50'],
|
||||
},
|
||||
},
|
||||
],
|
||||
defaultVariants: {
|
||||
type: 'attention',
|
||||
style: 'default',
|
||||
size: 'sm',
|
||||
},
|
||||
},
|
||||
{
|
||||
responsiveVariants: true,
|
||||
},
|
||||
);
|
||||
|
||||
export type TagsTheme = VariantProps<typeof tagsTheme>;
|
75
packages/frontend/src/components/shared/Tags/Tags.tsx
Normal file
75
packages/frontend/src/components/shared/Tags/Tags.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import React, {
|
||||
type ReactNode,
|
||||
type ComponentPropsWithoutRef,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import { TagsTheme, tagsTheme } from './Tags.theme';
|
||||
import { cloneIcon } from 'utils/cloneIcon';
|
||||
|
||||
type TagsProps = ComponentPropsWithoutRef<'div'> &
|
||||
TagsTheme & {
|
||||
/**
|
||||
* The optional left icon element for a component.
|
||||
* @type {ReactNode}
|
||||
*/
|
||||
leftIcon?: ReactNode;
|
||||
/**
|
||||
* The optional right icon element to display.
|
||||
* @type {ReactNode}
|
||||
*/
|
||||
rightIcon?: ReactNode;
|
||||
/**
|
||||
* The optional type of the tags component.
|
||||
* @type {TagsTheme['type']}
|
||||
**/
|
||||
type?: TagsTheme['type'];
|
||||
/**
|
||||
* The optional style of the tags component.
|
||||
* @type {TagsTheme['style']}
|
||||
*/
|
||||
style?: TagsTheme['style'];
|
||||
/**
|
||||
* The optional size of the tags component.
|
||||
* @type {TagsTheme['size']}
|
||||
*/
|
||||
size?: TagsTheme['size'];
|
||||
};
|
||||
|
||||
export const Tags = ({
|
||||
children,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
type = 'attention',
|
||||
style = 'default',
|
||||
size = 'sm',
|
||||
}: TagsProps) => {
|
||||
const {
|
||||
wrapper: wrapperCls,
|
||||
icon: iconCls,
|
||||
label: labelCls,
|
||||
} = tagsTheme({
|
||||
type,
|
||||
style,
|
||||
size,
|
||||
});
|
||||
|
||||
const renderLeftIcon = useMemo(() => {
|
||||
if (!leftIcon) return null;
|
||||
return <div className={iconCls()}>{cloneIcon(leftIcon, { size: 16 })}</div>;
|
||||
}, [iconCls, leftIcon]);
|
||||
|
||||
const renderRightIcon = useMemo(() => {
|
||||
if (!rightIcon) return null;
|
||||
return (
|
||||
<div className={iconCls()}>{cloneIcon(rightIcon, { size: 16 })}</div>
|
||||
);
|
||||
}, [iconCls, rightIcon]);
|
||||
|
||||
return (
|
||||
<div className={wrapperCls()}>
|
||||
{renderLeftIcon}
|
||||
<p className={labelCls()}>{children}</p>
|
||||
{renderRightIcon}
|
||||
</div>
|
||||
);
|
||||
};
|
2
packages/frontend/src/components/shared/Tags/index.ts
Normal file
2
packages/frontend/src/components/shared/Tags/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './Tags';
|
||||
export * from './Tags.theme';
|
Loading…
Reference in New Issue
Block a user