[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:
marcelines 2023-06-06 12:46:54 +01:00 committed by GitHub
parent eb5cbcdda3
commit 36538591ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 49 deletions

View File

@ -133,7 +133,7 @@ const ChartComponent = (props: Props): JSX.Element => {
return { return {
dy: '.33em', dy: '.33em',
fill: '#A1ABBD', fill: '#A1ABBD',
fontFamily: 'Inter, sans-serif', fontFamily: 'Menlo',
fontSize: 11, fontSize: 11,
textAnchor, textAnchor,
} }
@ -161,6 +161,7 @@ const ChartComponent = (props: Props): JSX.Element => {
{...tickProps} {...tickProps}
fill="#A1ABBD" fill="#A1ABBD"
fontSize={11} fontSize={11}
fontFamily="Menlo"
textAnchor="middle" textAnchor="middle"
dx="-1em" dx="-1em"
> >

View File

@ -56,41 +56,6 @@ const DATA = [
open_issues: 10, open_issues: 10,
closed_issues: 130, 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 = { type Props = {
@ -126,20 +91,20 @@ export const EpicOverview = (props: Props) => {
{description} {description}
</Text> </Text>
<div className="flex py-3"> <div className="flex py-3">
<Tag size={24} label="E:CommunitiesProtocol" /> <Tag size={24} label="E:CommunitiesProtocol" color="$blue-50" />
</div> </div>
<Chart data={DATA} height={300} isLoading={isLoading} /> <Chart data={DATA} height={300} isLoading={isLoading} />
<div className="flex justify-between pt-3"> <div className="flex justify-between pt-3">
<div className="flex gap-1"> <div className="flex gap-1">
<Tag size={24} label="Communities" /> <Tag size={24} label="Communities" color="#FF7D46" icon="🧙‍♂️" />
<Tag size={24} label="Wallet" /> <Tag size={24} label="Wallet" color="#7140FD" icon="🎎" />
</div> </div>
<div className="flex gap-1"> <div className="flex gap-1">
<Tag size={24} label="M:0.11.0" /> <Tag size={24} label="M:0.11.0" color="$danger-50" />
<Tag size={24} label="M:0.12.0" /> <Tag size={24} label="M:0.12.0" color="$success-50" />
</div> </div>
</div> </div>
</div> </div>

View File

@ -56,10 +56,10 @@ export const TableIssues = () => {
<div className="flex gap-3"> <div className="flex gap-3">
<div className="flex gap-1"> <div className="flex gap-1">
<Tag size={24} label="E:Syncing" /> <Tag size={24} label="E:Syncing" color="$orange-50" />
<Tag size={24} label="E:Wallet" /> <Tag size={24} label="E:Wallet" color="$green-50" />
<Tag size={24} label="Feature" /> <Tag size={24} label="Feature" color="$pink-50" />
<Tag size={24} label="Web" /> <Tag size={24} label="Web" color="$purple-50" />
</div> </div>
<Tag size={24} label="9435" /> <Tag size={24} label="9435" />

View File

@ -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 }

View File

@ -1,3 +1,4 @@
import { NftIcon } from '@status-im/icons'
import { Stack } from '@tamagui/core' import { Stack } from '@tamagui/core'
import { Tag } from './tag' 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} selected />
<Tag icon="🐷" label="Tag" size={32} disabled /> <Tag icon="🐷" label="Tag" size={32} disabled />
<Tag icon="🐷" size={32} /> <Tag icon="🐷" size={32} />
<Tag icon={NftIcon} size={32} label="bajoras" />
<Tag icon="🐷" label="Tag" size={24} /> <Tag icon="🐷" label="Tag" size={24} />
<Tag icon="🐷" size={24} /> <Tag icon="🐷" size={24} />
@ -37,6 +39,19 @@ export const Default: Story = {
<Tag label="Tag" size={32} disabled /> <Tag label="Tag" size={32} disabled />
<Tag label="Tag" size={24} /> <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>
</Stack> </Stack>
) )

View File

@ -3,9 +3,11 @@ import { createElement } from 'react'
import { Stack, styled } from '@tamagui/core' import { Stack, styled } from '@tamagui/core'
import { Text } from '../text' import { Text } from '../text'
import { getCustomStyles } from './get-custom-styles'
import type { TextProps } from '../text' import type { TextProps } from '../text'
import type { IconProps } from '@status-im/icons' import type { IconProps } from '@status-im/icons'
import type { ColorTokens } from '@tamagui/core'
import type { ComponentType } from 'react' import type { ComponentType } from 'react'
type Props = { type Props = {
@ -14,6 +16,8 @@ type Props = {
label?: string label?: string
selected?: boolean selected?: boolean
disabled?: boolean disabled?: boolean
onPress?: () => void
color?: ColorTokens | `#${string}`
} }
const textSizes: Record<NonNullable<Props['size']>, TextProps['size']> = { 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 Tag = (props: Props) => {
const { size, icon, label, selected, disabled } = props const { size, icon, label, selected, disabled, onPress, color } = props
const renderIcon = () => { const renderIcon = () => {
if (!icon) { if (!icon) {
@ -47,10 +51,12 @@ const Tag = (props: Props) => {
selected={selected} selected={selected}
disabled={disabled} disabled={disabled}
iconOnly={Boolean(icon && !label)} iconOnly={Boolean(icon && !label)}
onPress={() => onPress?.()}
{...getCustomStyles(props)}
> >
{renderIcon()} {renderIcon()}
{label && ( {label && (
<Text size={textSizes[size]} weight="medium"> <Text size={textSizes[size]} weight="medium" {...(color && { color })}>
{label} {label}
</Text> </Text>
)} )}
@ -62,6 +68,10 @@ export { Tag }
export type { Props as TagProps } export type { Props as TagProps }
const Base = styled(Stack, { const Base = styled(Stack, {
tag: 'tag',
name: 'Tag',
accessibilityRole: 'button',
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
@ -71,6 +81,18 @@ const Base = styled(Stack, {
borderRadius: '$full', borderRadius: '$full',
backgroundColor: '$white-100', backgroundColor: '$white-100',
animation: 'fast',
cursor: 'pointer',
hoverStyle: {
borderColor: '$neutral-30',
backgroundColor: '$neutral-5',
},
pressStyle: {
borderColor: '$neutral-30',
backgroundColor: '$neutral-5',
},
variants: { variants: {
size: { size: {
32: { 32: {
@ -86,11 +108,19 @@ const Base = styled(Stack, {
gap: 5, gap: 5,
}, },
}, },
selected: { selected: {
true: { true: {
backgroundColor: '$primary-50-opa-10', backgroundColor: '$primary-50-opa-10',
borderColor: '$primary-50', borderColor: '$primary-50',
hoverStyle: {
backgroundColor: '$primary-50-opa-20',
borderColor: '$primary-60',
},
pressStyle: {
backgroundColor: '$primary-50-opa-20',
borderColor: '$primary-60',
},
}, },
}, },

View File

@ -12,7 +12,7 @@ type Weight = NonNullable<Variants['weight']>
type Props = { type Props = {
children: React.ReactNode children: React.ReactNode
color?: ColorTokens color?: ColorTokens | string
truncate?: boolean truncate?: boolean
wrap?: false wrap?: false
} & ( } & (

View File

@ -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 }