nandorojo / dripsy

🍷 Responsive, unstyled UI primitives for React Native + Web.
https://dripsy.xyz
MIT License
2.01k stars 80 forks source link

Icon component example #112

Closed nandorojo closed 3 years ago

nandorojo commented 3 years ago

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 https://github.com/nandorojo/dripsy/issues/72#issuecomment-828587515

Click here to see code ```tsx 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['colors'] type Name = React.ComponentProps['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['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['name'] | null = null let size = 22 if (typeof icon === 'string') { name = icon as React.ComponentProps['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 ( ) } ```

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 3 years ago

Theme intellisense 🤑

Screen Shot 2021-07-26 at 5 44 34 PM
norman-ags commented 2 years ago

Thank you for this!

nandorojo commented 2 years ago

Happy to help