[website] Custom tags (#409)
* feat: change tags component and add util to get color with opacity * feat: change some tags for the example purpose * fix: some issues and code organization * feat: removes complexity from the code and uses css color-mix function
This commit is contained in:
parent
eb5cbcdda3
commit
36538591ce
|
@ -133,7 +133,7 @@ const ChartComponent = (props: Props): JSX.Element => {
|
|||
return {
|
||||
dy: '.33em',
|
||||
fill: '#A1ABBD',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontFamily: 'Menlo',
|
||||
fontSize: 11,
|
||||
textAnchor,
|
||||
}
|
||||
|
@ -161,6 +161,7 @@ const ChartComponent = (props: Props): JSX.Element => {
|
|||
{...tickProps}
|
||||
fill="#A1ABBD"
|
||||
fontSize={11}
|
||||
fontFamily="Menlo"
|
||||
textAnchor="middle"
|
||||
dx="-1em"
|
||||
>
|
||||
|
|
|
@ -56,41 +56,6 @@ const DATA = [
|
|||
open_issues: 10,
|
||||
closed_issues: 130,
|
||||
},
|
||||
{
|
||||
date: '2022-02-04',
|
||||
open_issues: 10,
|
||||
closed_issues: 140,
|
||||
},
|
||||
{
|
||||
date: '2022-02-05',
|
||||
open_issues: 10,
|
||||
closed_issues: 150,
|
||||
},
|
||||
{
|
||||
date: '2022-02-06',
|
||||
open_issues: 10,
|
||||
closed_issues: 160,
|
||||
},
|
||||
{
|
||||
date: '2022-02-07',
|
||||
open_issues: 10,
|
||||
closed_issues: 180,
|
||||
},
|
||||
{
|
||||
date: '2022-02-08',
|
||||
open_issues: 10,
|
||||
closed_issues: 190,
|
||||
},
|
||||
{
|
||||
date: '2022-02-09',
|
||||
open_issues: 10,
|
||||
closed_issues: 200,
|
||||
},
|
||||
{
|
||||
date: '2022-02-10',
|
||||
open_issues: 0,
|
||||
closed_issues: 220,
|
||||
},
|
||||
]
|
||||
|
||||
type Props = {
|
||||
|
@ -126,20 +91,20 @@ export const EpicOverview = (props: Props) => {
|
|||
{description}
|
||||
</Text>
|
||||
<div className="flex py-3">
|
||||
<Tag size={24} label="E:CommunitiesProtocol" />
|
||||
<Tag size={24} label="E:CommunitiesProtocol" color="$blue-50" />
|
||||
</div>
|
||||
|
||||
<Chart data={DATA} height={300} isLoading={isLoading} />
|
||||
|
||||
<div className="flex justify-between pt-3">
|
||||
<div className="flex gap-1">
|
||||
<Tag size={24} label="Communities" />
|
||||
<Tag size={24} label="Wallet" />
|
||||
<Tag size={24} label="Communities" color="#FF7D46" icon="🧙♂️" />
|
||||
<Tag size={24} label="Wallet" color="#7140FD" icon="🎎" />
|
||||
</div>
|
||||
|
||||
<div className="flex gap-1">
|
||||
<Tag size={24} label="M:0.11.0" />
|
||||
<Tag size={24} label="M:0.12.0" />
|
||||
<Tag size={24} label="M:0.11.0" color="$danger-50" />
|
||||
<Tag size={24} label="M:0.12.0" color="$success-50" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -56,10 +56,10 @@ export const TableIssues = () => {
|
|||
|
||||
<div className="flex gap-3">
|
||||
<div className="flex gap-1">
|
||||
<Tag size={24} label="E:Syncing" />
|
||||
<Tag size={24} label="E:Wallet" />
|
||||
<Tag size={24} label="Feature" />
|
||||
<Tag size={24} label="Web" />
|
||||
<Tag size={24} label="E:Syncing" color="$orange-50" />
|
||||
<Tag size={24} label="E:Wallet" color="$green-50" />
|
||||
<Tag size={24} label="Feature" color="$pink-50" />
|
||||
<Tag size={24} label="Web" color="$purple-50" />
|
||||
</div>
|
||||
|
||||
<Tag size={24} label="9435" />
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
import { getColorWithOpacity } from '../../utils/get-color-with-opacity'
|
||||
import { tokens } from '../tokens'
|
||||
|
||||
import type { TagProps } from './tag'
|
||||
import type { ColorTokens, StackStyleProps } from '@tamagui/core'
|
||||
|
||||
// TypeGuard for ColorTokens
|
||||
function isColorTokens(
|
||||
value: `#${string}` | ColorTokens
|
||||
): value is ColorTokens {
|
||||
return typeof value === 'string' && value.startsWith('$')
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the styles for the custom tag
|
||||
* @param props - the props of the tag
|
||||
* @returns the styles for the custom tag or null if no color is provided
|
||||
**/
|
||||
const getCustomStyles = (props: TagProps): StackStyleProps | null => {
|
||||
const { color: colorFromProps, icon } = props
|
||||
|
||||
if (!colorFromProps) {
|
||||
return null
|
||||
}
|
||||
|
||||
let color: ColorTokens | string = colorFromProps
|
||||
|
||||
if (isColorTokens(colorFromProps)) {
|
||||
const colorToken = colorFromProps.replace(
|
||||
'$',
|
||||
''
|
||||
) as keyof typeof tokens.color
|
||||
color = tokens.color[colorToken]?.val || colorFromProps
|
||||
}
|
||||
|
||||
if (icon) {
|
||||
return {
|
||||
borderColor: getColorWithOpacity(color!, 0.2),
|
||||
backgroundColor: getColorWithOpacity(color!, 0.1),
|
||||
pressStyle: {
|
||||
backgroundColor: getColorWithOpacity(color, 0.2),
|
||||
borderColor: getColorWithOpacity(color, 0.3),
|
||||
},
|
||||
hoverStyle: {
|
||||
backgroundColor: getColorWithOpacity(color, 0.2),
|
||||
borderColor: getColorWithOpacity(color, 0.3),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
borderColor: getColorWithOpacity(color, 0.2),
|
||||
pressStyle: {
|
||||
borderColor: getColorWithOpacity(color, 0.3),
|
||||
backgroundColor: getColorWithOpacity(color, 0.1),
|
||||
},
|
||||
hoverStyle: {
|
||||
borderColor: getColorWithOpacity(color, 0.3),
|
||||
backgroundColor: getColorWithOpacity(color, 0.1),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export { getCustomStyles }
|
|
@ -1,3 +1,4 @@
|
|||
import { NftIcon } from '@status-im/icons'
|
||||
import { Stack } from '@tamagui/core'
|
||||
|
||||
import { Tag } from './tag'
|
||||
|
@ -26,6 +27,7 @@ export const Default: Story = {
|
|||
<Tag icon="🐷" label="Tag" size={32} selected />
|
||||
<Tag icon="🐷" label="Tag" size={32} disabled />
|
||||
<Tag icon="🐷" size={32} />
|
||||
<Tag icon={NftIcon} size={32} label="bajoras" />
|
||||
|
||||
<Tag icon="🐷" label="Tag" size={24} />
|
||||
<Tag icon="🐷" size={24} />
|
||||
|
@ -37,6 +39,19 @@ export const Default: Story = {
|
|||
<Tag label="Tag" size={32} disabled />
|
||||
|
||||
<Tag label="Tag" size={24} />
|
||||
<Tag label="New tag" size={24} disabled selected />
|
||||
<Tag label="New tag" size={24} color="#FF7D46" />
|
||||
|
||||
<Tag label="New tag #7140FD" size={24} color="#BA34F5" />
|
||||
<Tag label="New tag #7140FD" size={24} color="#7140FD" icon="🧙♂️" />
|
||||
|
||||
<Tag
|
||||
label="custom color purple"
|
||||
size={24}
|
||||
icon="🐷"
|
||||
color="$purple-50"
|
||||
/>
|
||||
<Tag label="New tag with icon" size={24} color="#FF7D46" icon="🥷🏾" />
|
||||
</Stack>
|
||||
</Stack>
|
||||
)
|
||||
|
|
|
@ -3,9 +3,11 @@ import { createElement } from 'react'
|
|||
import { Stack, styled } from '@tamagui/core'
|
||||
|
||||
import { Text } from '../text'
|
||||
import { getCustomStyles } from './get-custom-styles'
|
||||
|
||||
import type { TextProps } from '../text'
|
||||
import type { IconProps } from '@status-im/icons'
|
||||
import type { ColorTokens } from '@tamagui/core'
|
||||
import type { ComponentType } from 'react'
|
||||
|
||||
type Props = {
|
||||
|
@ -14,6 +16,8 @@ type Props = {
|
|||
label?: string
|
||||
selected?: boolean
|
||||
disabled?: boolean
|
||||
onPress?: () => void
|
||||
color?: ColorTokens | `#${string}`
|
||||
}
|
||||
|
||||
const textSizes: Record<NonNullable<Props['size']>, TextProps['size']> = {
|
||||
|
@ -27,7 +31,7 @@ const iconSizes: Record<NonNullable<Props['size']>, IconProps['size']> = {
|
|||
}
|
||||
|
||||
const Tag = (props: Props) => {
|
||||
const { size, icon, label, selected, disabled } = props
|
||||
const { size, icon, label, selected, disabled, onPress, color } = props
|
||||
|
||||
const renderIcon = () => {
|
||||
if (!icon) {
|
||||
|
@ -47,10 +51,12 @@ const Tag = (props: Props) => {
|
|||
selected={selected}
|
||||
disabled={disabled}
|
||||
iconOnly={Boolean(icon && !label)}
|
||||
onPress={() => onPress?.()}
|
||||
{...getCustomStyles(props)}
|
||||
>
|
||||
{renderIcon()}
|
||||
{label && (
|
||||
<Text size={textSizes[size]} weight="medium">
|
||||
<Text size={textSizes[size]} weight="medium" {...(color && { color })}>
|
||||
{label}
|
||||
</Text>
|
||||
)}
|
||||
|
@ -62,6 +68,10 @@ export { Tag }
|
|||
export type { Props as TagProps }
|
||||
|
||||
const Base = styled(Stack, {
|
||||
tag: 'tag',
|
||||
name: 'Tag',
|
||||
accessibilityRole: 'button',
|
||||
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
|
@ -71,6 +81,18 @@ const Base = styled(Stack, {
|
|||
borderRadius: '$full',
|
||||
backgroundColor: '$white-100',
|
||||
|
||||
animation: 'fast',
|
||||
cursor: 'pointer',
|
||||
|
||||
hoverStyle: {
|
||||
borderColor: '$neutral-30',
|
||||
backgroundColor: '$neutral-5',
|
||||
},
|
||||
pressStyle: {
|
||||
borderColor: '$neutral-30',
|
||||
backgroundColor: '$neutral-5',
|
||||
},
|
||||
|
||||
variants: {
|
||||
size: {
|
||||
32: {
|
||||
|
@ -86,11 +108,19 @@ const Base = styled(Stack, {
|
|||
gap: 5,
|
||||
},
|
||||
},
|
||||
|
||||
selected: {
|
||||
true: {
|
||||
backgroundColor: '$primary-50-opa-10',
|
||||
borderColor: '$primary-50',
|
||||
|
||||
hoverStyle: {
|
||||
backgroundColor: '$primary-50-opa-20',
|
||||
borderColor: '$primary-60',
|
||||
},
|
||||
pressStyle: {
|
||||
backgroundColor: '$primary-50-opa-20',
|
||||
borderColor: '$primary-60',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ type Weight = NonNullable<Variants['weight']>
|
|||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
color?: ColorTokens
|
||||
color?: ColorTokens | string
|
||||
truncate?: boolean
|
||||
wrap?: false
|
||||
} & (
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Gets the color with opacity based on the original color and opacity value
|
||||
* @param color - the original color
|
||||
* @param opacity - the opacity value
|
||||
* @returns the color with opacity
|
||||
**/
|
||||
|
||||
function getColorWithOpacity(color: string, opacity: number): string {
|
||||
// Ensure the opacity value is within the valid range of 0 to 1
|
||||
const clampedOpacity = Math.max(0, Math.min(1, opacity)) * 100
|
||||
|
||||
// Construct the color string with opacity using CSS color-mix function
|
||||
const newColor = `color-mix(in srgb, ${color} ${clampedOpacity}%, transparent);`
|
||||
|
||||
return newColor
|
||||
}
|
||||
|
||||
export { getColorWithOpacity }
|
Loading…
Reference in New Issue