Icon component example
nandorojo opened this issue · 3 comments
nandorojo commented
I think I have a great system setup for using expo icons with Dripsy. Figured I'd share it here.
I wanted type-safe props that used my theme values for colors.
Note, you should replace useTheme
with your own custom useTheme
function, as detailed at #72 (comment)
Click here to see code
import React from 'react'
import Icon from '@expo/vector-icons/build/Ionicons'
import { styled, useDripsyTheme as useTheme } from 'dripsy'
const StyledIcon = styled(Icon)({})
type Color = (string & {}) | keyof ReturnType<typeof useTheme>['colors']
type Name = React.ComponentProps<typeof StyledIcon>['name']
type Icon =
| {
name: Name
size?: number
color?: Color
}
| React.ReactElement
| Name
type BaseProps = {
size?: number
color?: Color
name: Name
}
export type IconProps = {
icon: Icon
}
export type IconsBaseProps = BaseProps
export type AllIconProps = (IconProps | BaseProps) & {
sx?: React.ComponentProps<typeof StyledIcon>['sx']
selectable?: boolean
}
function isIconProps(props: AllIconProps): props is IconProps {
return !!(props as IconProps).icon
}
export default function Ionicons(props: AllIconProps) {
const { colors } = useTheme()
let icon: Icon | undefined
let color: Color = colors.text
if (isIconProps(props)) {
icon = props.icon
} else {
icon = {
name: props.name,
color: props.color,
size: props.size,
}
}
if (React.isValidElement(icon)) {
return icon
}
// this exists for conditional props
if (typeof icon === 'boolean') return null
let name: React.ComponentProps<typeof StyledIcon>['name'] | null = null
let size = 22
if (typeof icon === 'string') {
name = icon as React.ComponentProps<typeof StyledIcon>['name']
} else if (icon?.name) {
name = icon.name
if (icon.size) size = icon.size
if (icon.color) {
color = colors?.[icon.color] ?? icon.color
}
}
if (!name) return null
return (
<StyledIcon
{...props}
color={color}
size={size}
name={name}
sx={props.sx}
/>
)
}
This allows for nice, strictly-typed colors for your icons, and has intellisense for your theme's colors. It also has multiple modes of composition:
Direct props
<Ionicons color="text" name="close" />
Object props
<Ionicons icon={{ color: 'text', name: 'close' }} />
While more obscure, the object prop patterns allows you to easily compose other components that wrap the icon. For example, a Button
component might have a iconPrefix
prop.
type ButtonProps = {
iconPrefix: IconProps['icon']
}
export function Button({ iconPrefix }: ButtonProps) {
return <Ionicons icon={iconPrefix} />
}
Now, you can compose the button with ease:
<Button iconPrefix="close" />
<Button iconPrefix={{ name: 'close', size: 20 }} />
<Button iconPrefix={<CustomIconComponent />} />
nandorojo commented
norman-ags commented
Thank you for this!
nandorojo commented
Happy to help