Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Icon component example #112

Closed
nandorojo opened this issue Jul 26, 2021 · 3 comments
Closed

Icon component example #112

nandorojo opened this issue Jul 26, 2021 · 3 comments

Comments

@nandorojo
Copy link
Owner

nandorojo commented Jul 26, 2021

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 nandorojo changed the title Icon components Icon component example Jul 26, 2021
@nandorojo
Copy link
Owner Author

Theme intellisense 🤑

Screen Shot 2021-07-26 at 5 44 34 PM

@norman-ags
Copy link

Thank you for this!

@nandorojo
Copy link
Owner Author

Happy to help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants