react-navigation-header-buttons icon indicating copy to clipboard operation
react-navigation-header-buttons copied to clipboard

Allow arbitrary props to be passed to the icon component

Open jordanmkoncz opened this issue 6 years ago • 5 comments

Firstly, thanks for taking the time to create this library.

I'm trying to use this library along with https://github.com/shyaniv7/react-native-fontawesome-pro. With react-native-fontawesome-pro, you can use an icon like so:

<Icon name="square" type="light" size={24} color="black" />

You can see that most of the props are the same as what you'd use with react-native-vector-icons, but with one additional and important prop, type, which defines whether you're using the solid, regular, or light family of icons. I need to be able to specify this prop, on a per-icon basis, but I can't see how I'd do this with react-navigation-header-buttons.

Here's my current code:

// FontAwesomeHeaderButtons.js
import React from 'react';
import HeaderButtons, { HeaderButton, Item } from 'react-navigation-header-buttons';
import Icon from 'react-native-fontawesome-pro';

const FontAwesomeHeaderButton = props => (
  <HeaderButton {...props} IconComponent={Icon} iconSize={24} />
);

const FontAwesomeHeaderButtons = props => {
  return (
    <HeaderButtons
      HeaderButtonComponent={FontAwesomeHeaderButton}
      OverflowIcon={<Icon name="ellipsis-v" type="light" size={24} />}
      {...props}
    />
  );
};

export default FontAwesomeHeaderButtons;
// MyScreen.js
static navigationOptions = {
  headerRight: (
    <FontAwesomeHeaderButtons>
      <HeaderButtons.Item title="add" iconName="plus-circle" onPress={() => console.warn('add')} />
      <HeaderButtons.Item title="edit" onPress={() => console.warn('edit')} />
    </FontAwesomeHeaderButtons>
  ),
};

Any suggestions for how I can specify the type for my header button icons?

jordanmkoncz avatar Aug 20 '18 07:08 jordanmkoncz

hi @jordanmkoncz, I wasn't aware the library existed, and the way the lib is done does not does not make this as easy as it could be. That being said I think this approach should work - add the type (or IconComponent for that matter) prop to the HeaderButtons.Item and then do

import Icon from 'react-native-fontawesome-pro';
const SolidIcon = (props) => <Icon type="solid" {...props} />

const FontAwesomeHeaderButton = props => {
  const FAIcon = props.type === 'solid' ? SolidIcon : Icon; // this will be something more clever, but you get the idea
  return <HeaderButton {...props} IconComponent={FAIcon} iconSize={24} />
}

However, this approach will require that you specify the type / IconComponent for each HeaderButtons.Item. Sure, you can have a default value, but still, it would be ideal to have something like HeaderButtons.SolidItem or HeaderButtons.LightItem which, I'm afraid, is not possible with the way I wrote the lib. Hope this helps, let me know if something is unclear.

vonovak avatar Aug 20 '18 15:08 vonovak

Thanks @vonovak, your solution worked.

jordanmkoncz avatar Aug 20 '18 23:08 jordanmkoncz

In case anyone else runs into the same problem as I did, here's the working code that I'm now using.

// FontAwesomeHeaderButtons.js
/**
 * @flow
 */

// See https://github.com/vonovak/react-navigation-header-buttons#how-to-integrate-in-your-project.

import React from 'react';
import { View } from 'react-native';
import HeaderButtons, { HeaderButton } from 'react-navigation-header-buttons';
import Icon from 'react-native-fontawesome-pro';

const SolidIcon = ({ style, ...otherProps }) => (
  <View style={style}>
    <Icon type="solid" {...otherProps} />
  </View>
);

const RegularIcon = ({ style, ...otherProps }) => (
  <View style={style}>
    <Icon type="regular" {...otherProps} />
  </View>
);

const LightIcon = ({ style, ...otherProps }) => (
  <View style={style}>
    <Icon type="light" {...otherProps} />
  </View>
);

type Props = {
  iconType?: string,
  iconSize?: number,
};

const FontAwesomeHeaderButton = (props: Props) => {
  let FontAwesomeIcon;

  if (props.iconType === 'solid') {
    FontAwesomeIcon = SolidIcon;
  } else if (props.iconType === 'regular') {
    FontAwesomeIcon = RegularIcon;
  } else {
    FontAwesomeIcon = LightIcon;
  }

  return <HeaderButton {...props} IconComponent={FontAwesomeIcon} />;
};

FontAwesomeHeaderButton.defaultProps = {
  iconType: 'light',
  iconSize: 18,
};

const FontAwesomeHeaderButtons = props => (
  <HeaderButtons
    HeaderButtonComponent={FontAwesomeHeaderButton}
    OverflowIcon={<Icon name="ellipsis-v" type="light" size={24} />}
    {...props}
  />
);

export default FontAwesomeHeaderButtons;

Usage:

headerRight: (
  <FontAwesomeHeaderButtons>
    <HeaderButtons.Item title="Search" iconName="search" onPress={() => console.log('search')} />
  </FontAwesomeHeaderButtons>
),

jordanmkoncz avatar Aug 21 '18 01:08 jordanmkoncz

@vonovak I'll leave it up to you whether to close this issue. It may be worth considering changing this library so that there's a more straightforward and flexible way to pass any arbitrary props to your icon component.

For example, adding an iconProps prop, which is an object, and whose value is passed through to the icon component as props (via standard object spread). In practice you could then use it like so:

headerRight: (
  <HeaderButtons>
    <HeaderButtons.Item title="Search" iconProps={{ name: 'search', size: 24, type: 'light', someOtherProp: 'foo' }} onPress={() => console.log('search')} />
  </HeaderButtons>
),

This would mean that you can easily pass through any props that you want to use for the icon component, which would be more flexible than having a specific prop like iconSize which is then passed through to the icon component as a pre-determined prop like size.

jordanmkoncz avatar Aug 21 '18 01:08 jordanmkoncz

Alternative solution: use the Button Element prop like here https://github.com/vonovak/react-navigation-header-buttons/blob/master/example/navbar-buttons-demo/screens/UsageCustom.js

vonovak avatar Aug 21 '18 05:08 vonovak